Search code examples
javaaudiojavasoundaudio-processingjavax.sound.sampled

Calculate the level/amplitude/db of audio for two channels


I have read two posts about extracting samples from AudioInputStream and converting them in to dB.

https://stackoverflow.com/a/26576548/8428414

https://stackoverflow.com/a/26824664/8428414

As far as I understand byte[] bytes; has structure like this:

Index 0: Sample 0 (Left Channel)
Index 1: Sample 0 (Right Channel)
Index 2: Sample 1 (Left Channel)
Index 3: Sample 1 (Right Channel)
Index 4: Sample 2 (Left Channel)
Index 5: Sample 2 (Right Channel)

In the first article it shows how to get samples from one channel (mono).

So, my problem is that I want to get samples separately for the right channel and separately for the left channel in order to calculate dB for right and left channels.

Here is the code. How it can be changed to get right and left channels separately? I can't understand how the index i changes...

final byte[] buffer = new byte[2048];

float[] samples = new float[buffer.length / 2];

for (int n = 0; n != -1; n = in.read(buffer, 0, buffer.length)) {
    line.write(buffer, 0, n);

    for (int i = 0, sampleIndex = 0; i < n; ) {
        int sample = 0;

        sample |= buffer[i++] & 0xFF; // (reverse these two lines
        sample |= buffer[i++] << 8;   //  if the format is big endian)

        // normalize to range of +/-1.0f
        samples[sampleIndex++] = sample / 32768f;
    }

    float rms = 0f;
    for (float sample : samples) {
        rms += sample * sample;
    }

    rms = (float) Math.sqrt(rms / samples.length);

Hope you could help me. Thank you in advance.


Solution

  • The format the stereo signal is saved in is called interleaved. I.e., as you described correctly, it's LLRRLLRRLLRR.... SO you first need to read a left sample, then a right sample, and so on.

    I have edited your code to reflect this. However, there is some room for improvement via refactoring.

    Note: The code changes only deal with interleaving. I have not checked the rest of your code.

    final byte[] buffer = new byte[2048];
    
    // create two buffers. One for the left, one for the right channel.
    float[] leftSamples = new float[buffer.length / 4];
    float[] rightSamples = new float[buffer.length / 4];
    
    for (int n = 0; n != -1; n = in.read(buffer, 0, buffer.length)) {
        line.write(buffer, 0, n);
    
        for (int i = 0, sampleIndex = 0; i < n; ) {
            int sample = 0;
    
            leftSample |= buffer[i++] & 0xFF; // (reverse these two lines
            leftSample |= buffer[i++] << 8;   //  if the format is big endian)
    
            rightSample |= buffer[i++] & 0xFF; // (reverse these two lines
            rightSample |= buffer[i++] << 8;   //  if the format is big endian)
    
            // normalize to range of +/-1.0f
            leftSamples[sampleIndex] = leftSample / 32768f;
            rightSamples[sampleIndex] = rightSample / 32768f;
    
            sampleIndex++;
        }
    
        // now compute RMS for left
        float leftRMS = 0f;
        for (float sample : leftSamples) {
            leftRMS += sample * sample;
        }
    
        leftRMS = (float) Math.sqrt(leftRMS / leftSamples.length);
    
        // ...and right
        float rightRMS = 0f;
        for (float sample : rightSamples) {
            rightRMS += sample * sample;
        }
    
        rightRMS = (float) Math.sqrt(rightRMS / rightSamples.length);
    }