javaaudiomp3jlayer

Play java mp3s simultaneously at different volumes


I am using JLayer to play my sounds in my game. I am trying to set independent volume levels for my music (constantly playing) and sound effects (sporadic). Right now my code changes the master volume level so, as expected, it changes the volume of both. Here is some sample code to demonstrate what I want to do(I cut a lot of stuff out to try and be SSCCE-like and realize there are some "bugs").

Any help would be hugely appreciated.

public static void playSoundOrMusic(String filename, String type) {
    String soundFilename = "";

    if (type.equals("SFX")){
        soundFilename = "res/sounds/sfx/" + filename;
    } else if (type.equals("MUSIC")){
        soundFilename = "res/sounds/music/" + filename;
    }

    FileInputStream fis = null;
    try {
        fis = new FileInputStream(soundFilename);
    } catch (FileNotFoundException e) {
        LOGGER.error("Sound file missing", e);
    }  
    BufferedInputStream bis = new BufferedInputStream(fis);  

    try {
        if (type.equals("SFX")){
            sfxPlayer = new Player(bis);
        } else if (type.equals("MUSIC")){
            musicPlayer = new Player(bis);
        }
    } catch (JavaLayerException e) {
        LOGGER.error("Sound file issue", e);
    } catch (ArrayIndexOutOfBoundsException e) {
        LOGGER.error("Sound file issue", e);
    }

    if (type.equals("SFX")){
        Info source = Port.Info.SPEAKER;
        if (AudioSystem.isLineSupported(source)){   
            try {
                Port outline = (Port) AudioSystem.getLine(source);
                outline.open();                
                FloatControl volumeControl = (FloatControl) outline.getControl(FloatControl.Type.VOLUME);                
                volumeControl.setValue(OptionsJPanel.sfxVolume);
            } catch (LineUnavailableException ex) {
                LOGGER.error("Sound line issue", ex);
            }

            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        sfxPlayer.play();
                    } catch (Exception ex) {
                        LOGGER.error("Sound(sfx) playing issue", ex);
                    }
                }
            }).start();
        }
    }

    if (type.equals("MUSIC")){
        Info source = Port.Info.SPEAKER;
        if (AudioSystem.isLineSupported(source)){   
            try {
                Port outline = (Port) AudioSystem.getLine(source);
                outline.open();                
                FloatControl volumeControl = (FloatControl) outline.getControl(FloatControl.Type.VOLUME);                
                volumeControl.setValue(OptionsJPanel.musicVolume);
            } catch (LineUnavailableException ex) {
                LOGGER.error("Sound line issue", ex);
            }

            new Thread(new Runnable() {
                String threadFilename = filename;
                @Override
                public void run() {
                    try {
                        musicPlayer.play();
                        while(!musicPlayer.isComplete()){
                            Thread.currentThread();
                            Thread.sleep(1000);
                        }
                        playSoundOrMusic(threadFilename, type);
                    } catch (Exception ex) {
                        LOGGER.error("Sound(music) playing issue", ex);
                    }
                }
            }).start();
        }
    }
}

Solution

  • You have the buffered input stream: you read it, you manipulate the data (reduce value by 0.5(50%)), then you put into a new stream and play in the second player.

    BufferedInputStream bis = new BufferedInputStream(fis);
    ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
    byte[] data=new byte[1024];
    while((k=audioInputStream.read(data, 0, data.length))>-1) {
      for(int i=0; i<k; i++) data[i]=(byte)(0.5*data[i]);
      outputStream.write(data, 0, k);
    }   
    byte[] audioData=outputStream.toByteArray();
    InputStream bis2=new ByteArrayInputStream(outData);    
    player2.play(bis2);
    

    --

    Here is a class that will process and play a file; method processPlay() can be adjusted to reduce by a certain fraction (0.5) as noted above - you could create your own method with that fraction as parameter.

    import javax.sound.sampled.*;
    import java.io.*;
    import java.net.*;
    import javazoom.spi.mpeg.sampled.file.MpegAudioFileReader;
    
    public class QHPlayer {
    
    public void play(String f) {
      int k=0;
      AudioFormat targetFormat=null;
      try {
        AudioInputStream audioInputStream=openFile(f);
        targetFormat=audioInputStream.getFormat();
        byte[] data=new byte[1024];
        DataLine.Info dinfo=new DataLine.Info(SourceDataLine.class, targetFormat);
        SourceDataLine line=null;
          line=(SourceDataLine)AudioSystem.getLine(dinfo);
          if(line!=null) {
            line.open(targetFormat);
            line.start();
            while((k=audioInputStream.read(data, 0, data.length))>-1) {
              line.write(data, 0, k);
            }
            line.stop();
            line.close();
        }
      }
      catch(Exception ex) { ex.printStackTrace(); }
    }
    
    public void processPlay(String f) {
      int k=0;
      AudioFormat targetFormat=null;
      ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
      try {
        AudioInputStream audioInputStream=openFile(f);
        targetFormat=audioInputStream.getFormat();
        byte[] data=new byte[1024];
    
        while((k=audioInputStream.read(data, 0, data.length))>-1)
          outputStream.write(data, 0, k);
        byte[] b=outputStream.toByteArray();
        for(k=0; k<b.length; k++) b[k]=(byte)(0.5*b[k]);
    
    
        DataLine.Info dinfo=new DataLine.Info(SourceDataLine.class, targetFormat);
        SourceDataLine line=null;
          line=(SourceDataLine)AudioSystem.getLine(dinfo);
          if(line!=null) {
            line.open(targetFormat);
            line.start();
            System.out.println(line.available());
            k=0;
            while(k<b.length) {
            System.out.println(line.available());
              int i=line.write(b, k, 8*1024);
              k+=i;
            }
            }
            line.stop();
            line.close();
      }
      catch(Exception ex) { ex.printStackTrace(); }
    }
    
    public void play(byte[] b) {
      int k=0;
      AudioFormat targetFormat=getAudioFormat();
      try {
        DataLine.Info dinfo=new DataLine.Info(SourceDataLine.class, targetFormat);
        SourceDataLine line=null;
          line=(SourceDataLine)AudioSystem.getLine(dinfo);
          if(line!=null) {
            line.open(targetFormat);
            line.start();
            while(k<b.length) {
              int i=line.write(b, k, 8*1024);
              k+=i;
            }
            line.stop();
            line.close();
        }
      }
      catch(Exception ex) { ex.printStackTrace(); }
    }
    
    
    
      public AudioInputStream openFile(String file) {
        AudioInputStream audioInputStream=null;
        try {
          File fileIn = new File(file);
          if(file.endsWith(".mp3"))
            audioInputStream=createMp3(fileIn);
          else
            audioInputStream=AudioSystem.getAudioInputStream(fileIn);
        }
        catch(Exception ex) { ex.printStackTrace(); }
        return audioInputStream;
      }
    
    
      AudioInputStream createMp3(File fileIn) throws IOException, Exception {
        AudioInputStream audioInputStream=null;
        AudioFormat targetFormat=null;
        try {
          AudioInputStream in=null;
          MpegAudioFileReader mp=new MpegAudioFileReader();
          in=mp.getAudioInputStream(fileIn);
          AudioFormat baseFormat=in.getFormat();
          targetFormat=new AudioFormat(
                  AudioFormat.Encoding.PCM_SIGNED,
                  baseFormat.getSampleRate(),
                  16,
                  baseFormat.getChannels(),
                  baseFormat.getChannels() * 2,
                  baseFormat.getSampleRate(),
                  false);
          audioInputStream=AudioSystem.getAudioInputStream(targetFormat, in);
        }
        catch(UnsupportedAudioFileException ue) { System.out.println("\nUnsupported Audio"); }
        return audioInputStream;
      }
    
    
      AudioFormat getAudioFormat() {
        float sampleRate = 44100F;
        int sampleSizeInBits = 16;
        int channels = 2;
        boolean signed = true;
        boolean bigEndian = true;
        return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
      }
    
    
      public static void main(String args[]) {
        QHPlayer q=new QHPlayer();
        q.processPlay("c:/.....mp3");
      }
    
    }