1

I have a stereo AudioInputStream and I want to split it into two mono AudioInputStreams, one for the left and one for the right channel of the stereo stream. How do I do that?

Note: I've already tested the answers from the following question but nothing worked for me (that's why I'm asking again).

How to split a Wav file into channels in java?

Here is what I'm trying to get working (it's mostly from the link above). In the following code, while loop runs infinitely. readsize is always 4 (or whatever sampleInBytes * 2 is), it never becomes -1 (which means it's the end of stream) so it never breaks.

   AudioInputStream originalAudioInputStream = null;
    try {
        originalAudioInputStream = AudioSystem.getAudioInputStream(audiofile);
    } catch (UnsupportedAudioFileException e) {
        //file not supported
    } catch (IOException e) {
        //error
    }
    //read file and convert it to PCM SIGNED big endian
    AudioFormat destaf = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
            originalAudioInputStream.getFormat().getSampleRate(),
            originalAudioInputStream.getFormat().getSampleSizeInBits(),
            originalAudioInputStream.getFormat().getChannels(),
            originalAudioInputStream.getFormat().getSampleSizeInBits() / 8 * originalAudioInputStream.getFormat().getChannels(),
            originalAudioInputStream.getFormat().getSampleRate(), true);
    AudioInputStream signedBigEndianInputStream = AudioSystem.getAudioInputStream(destaf, originalAudioInputStream);

    //split stereo AudioInputStream
    ByteArrayOutputStream leftbaos = new ByteArrayOutputStream();
    ByteArrayOutputStream rightbaos = new ByteArrayOutputStream();

    byte[] bytes = new byte[(signedBigEndianInputStream.getFormat().getSampleSizeInBits()/8)*2];

    while (true) {

        int readsize = 0;
        try {
            readsize = signedBigEndianInputStream.read(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }  

        if(readsize==-1){
            break;
        }

        rightbaos.write(bytes,0,bytes.length/2);
        leftbaos.write(bytes,bytes.length/2,bytes.length/2);
    }

    byte[] leftData = leftbaos.toByteArray();
    byte[] rightData = rightbaos.toByteArray();

    AudioFormat outFormat = new AudioFormat(signedBigEndianInputStream.getFormat().getEncoding(),signedBigEndianInputStream.getFormat().getSampleRate(),signedBigEndianInputStream.getFormat().getSampleSizeInBits(),1,signedBigEndianInputStream.getFormat().getFrameSize()/2, signedBigEndianInputStream.getFormat().getFrameRate(),signedBigEndianInputStream.getFormat().isBigEndian());

    ByteArrayInputStream leftbais = new ByteArrayInputStream(leftData);
    AudioInputStream leftoutputAIS = new AudioInputStream(leftbais, outFormat, leftData.length / outFormat.getFrameSize());

    ByteArrayInputStream rightbais = new ByteArrayInputStream(rightData);
    AudioInputStream rightoutputAIS = new AudioInputStream(rightbais, outFormat, rightData.length / outFormat.getFrameSize());

    //close inputstreams
    try {
        rightoutputAIS.close();
        leftoutputAIS.close();
        rightbais.close();
        leftbais.close();
        rightbaos.close();
        leftbaos.close();
        signedBigEndianInputStream.close();
        originalAudioInputStream.close();

    } catch (IOException e) {
        //error
    }

Thanks!

1nikolas
  • 93
  • 1
  • 8
  • Depending on the sample bit depth, you just need to read left and right samples in an interleaved fashion. Note that one sample consists of multiple bytes, if your sample bit depth is greater than 8. To get better help, please post your code and describe how it is not working. Also make sure that you specify your `AudioFormat`. – Hendrik May 02 '20 at 09:01
  • @Hendrik okay added code. As for the AudioFormat, I convert the stream to signed pcm big endian (as seen in the code). The other AudioFormat parameters are variable – 1nikolas May 02 '20 at 23:50
  • I would wrap that `AudioInputStream` into `DataInputStream`, and use the proper `readXy()` method for reading, depending on sample-size. For 2x16-bit samples, that would be 2x`readShort()` (it reads signed bigendian 16-bit shorts, so your current settings should be okay). To store the result into `ByteArrayOutputStream`, I would use `DataOutputStream` in the same bitdepth-dependent way (or just oversize it with a fixed, wide datatype like 32-bit integers, depending on what happens with the data afterwards). – tevemadar May 03 '20 at 19:38
  • Thanks for adding the code. Made it sooo much easier to help you! :-) Please see my answer below. – Hendrik May 04 '20 at 10:01

1 Answers1

1

You assume that the audio is stored in two blocks:

  1. All left samples
  2. All right samples

Instead, audio is usually stored in an interleaved fashion.

That is:

  1. One left sample
  2. One right sample
  3. One left sample
  4. One right sample
  5. ... (you get the idea)

In order to separate the two streams you'll have to do something like this:

// just to show which types are used
// i.e., initialization is not shown
AudioFormat destaf;
ByteArrayOutputStream leftbaos;
ByteArrayOutputStream rightbaos;
AudioInputStream signedBigEndianInputStream;
byte[] bytes;

[...]

final int bytesPerSample = destaf.getSampleSizeInBits() / 8;
final int channels = 2;

while (true) {
    int readsize = 0;
    try {
        readsize = signedBigEndianInputStream.read(bytes);
    } catch (IOException e) {
        e.printStackTrace();
    }  

    if (readsize==-1){
        break;
    }
    for (int sample=0; sample<readsize/channels/bytesPerSample;sample++) {
        final int offset = sample * bytesPerSample * channels;
        leftbaos.write(bytes, offset, bytesPerSample);
        rightbaos.write(bytes, offset + bytesPerSample, bytesPerSample);
    }    
}
[...]
Hendrik
  • 5,085
  • 24
  • 56