1nikolas
1nikolas

Reputation: 103

How to split PCM stream to AudioInputStreams for each channel?

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!

Upvotes: 1

Views: 764

Answers (1)

Hendrik
Hendrik

Reputation: 5310

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);
    }    
}
[...]

Upvotes: 1

Related Questions