2

I am trying to do bass boost for my audio input to 12 dB. Here is the code

public class BassBoost {
    double xn1, xn2, yn1, yn2;
    double omega, sn, cs, a, shape, beta, b0, b1, b2, a0, a1, a2;

    public BassBoost(int frequency, int dB_boost, int sampleRate) {
        xn1 = 0;
        xn2 = 0;
        yn1 = 0;
        yn2 = 0;

        omega = 2 * Utils.pi * frequency / sampleRate;
        sn = Math.sin(omega);
        cs = Math.cos(omega);
        a = Math.exp(Math.log(10.0) * dB_boost / 40);
        shape = 1.0;
        beta = Math.sqrt((a * a + 1) / shape - (Math.pow((a - 1), 2)));
        /* Coefficients */
        b0 = a * ((a + 1) - (a - 1) * cs + beta * sn);
        b1 = 2 * a * ((a - 1) - (a + 1) * cs);
        b2 = a * ((a + 1) - (a - 1) * cs - beta * sn);
        a0 = ((a + 1) + (a - 1) * cs + beta * sn);
        a1 = -2 * ((a - 1) + (a + 1) * cs);
        a2 = (a + 1) + (a - 1) * cs - beta * sn;
    }

    public void process(double[] buffer, int length) {
        double out, in = 0;

        for (int i = 0; i < length; i++) {
            in = buffer[i];
            out = (b0 * in + b1 * xn1 + b2 * xn2 - a1 * yn1 - a2 * yn2) / a0;
            xn2 = xn1;
            xn1 = in;
            yn2 = yn1;
            yn1 = out;

            if (out < -1.0)
                out = -1.0;
            else if (out > 1.0)
                out = 1.0; // Prevents clipping

            buffer[i] = out;
        }
    }
}

And Here is the pseudo code which calls the process

Audio is recorded and stored in short buffer. Buffer divided into small array and copied to double array for filter processing. If I don't divide I am getting OutofMemory as I am trying this on android phones. Filtering is applied (LPF, HPF) Output double buffer is amplified with following code before writing to Byte array.

void doOutput(int outlen, boolean maxGain) {
    int qi;
    int i, i2;

    while (true) {
        int max = 0;
        i = outbp;
        for (i2 = 0; i2 < outlen; i2 += 4) {
            qi = (int) (leftBuffer[i] * outputGain);
            if (qi > max)
                max = qi;
            if (qi < -max)
                max = -qi;
            ob[i2 + 1] = (byte) (qi >> 8);
            ob[i2] = (byte) qi;
            i = (i + 1) & fbufmask;
        }
        i = outbp;
        for (i2 = 2; i2 < outlen; i2 += 4) {
            qi = (int) (rightBuffer[i] * outputGain);
            if (qi > max)
                max = qi;
            if (qi < -max)
                max = -qi;
            ob[i2 + 1] = (byte) (qi >> 8);
            ob[i2] = (byte) qi;
            i = (i + 1) & fbufmask;
        }
        // if we're getting overflow, adjust the gain
        if (max > 32767) {
            outputGain *= 30000. / max;
            if (outputGain < 1e-8 || Double.isInfinite(outputGain)) {
                unstable = true;
                break;
            }
            continue;
        } else if (maxGain && max < 24000) {
            if (max == 0) {
                if (outputGain == 1)
                    break;
                outputGain = 1;
            } else
                outputGain *= 30000. / max;
            continue;
        }
        break;
    }
    if (unstable)
        return;
    outbp = i;

out.write(ob, 0, outlen);
}

This code is for android so it has to be memory efficient. I tried using Arraylist but that was also going outofmemory.

Question is To apply consistent outputGain though out the buffer, I am currently applying filtering twice and writing to byteOutputStream once. I tried using Arraylist to store output buffer before applying single outputGain but that was going outofmemory to store entire Double buffer (1 min recording). Also for the BassBoost I want to apply it once to entire output. Is there memory efficient way to do it. Using ArrayList is out of question. To get a consistent outputGain, I have to go through the entire output buffer and apply it with single outputGain.

Want to make the above sample code efficient so that I do not have to go through the buffer twice. Also Bass Boost if I apply it to chunks gives me breaks in the continuous sound.

Thanks

Anonymous Me
  • 1,108
  • 2
  • 9
  • 26
  • 2
    So, you think that the use of an `ArrayList` is the problem yet, amongst the large amount of code you've posted, I don't see any of the related code. Could you edit your question to contain the sections of code? – marko Aug 05 '13 at 20:54
  • How big are these double[] ? And why wouldnt you use float[]? In all of my audio coding I've always used float[] (when in java). – Nick Aug 05 '13 at 21:14

1 Answers1

0

A short[] for one minute of 44.1kHz stereo sounds takes 44100 * 2 * 2 * 60 = 10584000, or roughly 10MB of memory.

That's just for a plain short buffer.

Since you are using an ArrayList<Double>, as per your comment, it gets a lot bigger. Java adds a bit of overhead to each Object, and this arraylist is going to have about 5 million objects. I'm pretty sure the amount of overhead varies by VM.

I believe, however, that the overhead is at least 8 bytes per Object. With your 5 million Shorts, that (8 + 8) * 5M = 80MB at a minimum.

See where this is going?

I don't think you want to be creating 5 million objects. The numbers I gave were just for the samples themselves. If you're using extra memory for the processing stage, it's going to be even worse. That's why basically every audio app does processing in chunks. There is a huge amount of data there, even for relatively "short" clips.

Community
  • 1
  • 1
Geobits
  • 22,218
  • 6
  • 59
  • 103
  • I am storing it in the ArrayList of double. since the processed buffer is double. – Anonymous Me Aug 05 '13 at 21:47
  • Oh. Well a double takes 8 bytes, not 2 like a short. Let me edit in some math to make your numbers even higher. The moral remains the same, though. It's probably too big to fit in your heap. – Geobits Aug 05 '13 at 21:53
  • Currently also I am processing in chunks, so how to do it efficiently without storing and processing twice. – Anonymous Me Aug 05 '13 at 21:57
  • I'm not sure I understand why you're processing twice in the first place. – Geobits Aug 05 '13 at 21:59
  • So as to apply one outputGain through out the audio. Otherwise one chunk will have one gain second will have other. For the silent chunk, gain was coming out to be more so it sounded distorted. Later it settles down. – Anonymous Me Aug 05 '13 at 22:05
  • @SalmanSiddiqui It is not possible to have an ArrayList of double, but only of Double. Java collections can not hold primitive types. – le3th4x0rbot Aug 05 '13 at 22:08
  • If you want constant gain, you can *read* the whole file, chunk at a time. This way you can calculate what kind of total gain you need. Then you can go back through the chunks and change them in the next pass. – Geobits Aug 05 '13 at 22:10
  • If I am getting it right, for next pass, I need to store the output buffer chunk somewhere. – Anonymous Me Aug 05 '13 at 22:16
  • Right, just not in memory. A temp file may work, but that may also cause a bottleneck. Try it and see. – Geobits Aug 05 '13 at 22:20
  • 1
    And For god's sake use an array of doubles not an array list of Doubles. – Bjorn Roche Aug 06 '13 at 00:46
  • As in the above code, I am not using Arraylist, it was just for exhausting the options. Is temporary file my only option for second pass? Also for BassBoost which process double array, Do I need to divide it into chunks after reading from temp file for processing? – Anonymous Me Aug 06 '13 at 05:03