Decrease bitrate on WAV file created with recorder

2020-01-29 04:43发布

问题:

I'm trying to use recorderjs on an app engine site where users upload short audio recordings (say, 1 to a dozen seconds long). I've noticed that the WAV files I'm uploading are much larger than I expected. For example, I just created a recording that lasts roughly 9 seconds, and the uploaded blob is 1736769 bytes, which is > 1.5 megabytes.

Question:

How do I modify the recorderjs code (or my own code -- maybe I'm using recorderjs incorrectly) so that my audio blobs have a lower bitrate? I'd like a 10 second recording to be safely under 1 MB.

My guess is that I would need to modify the encodeWAV function in here, or maybe exportWAV, but I'm not sure how. Would it make sense to just drop every other element of the interleaved buffer in exportWAV? Is there a more intelligent way to do it? How does the bitrate of the exported WAV depend on properties of my computer (e.g. the sampling rate of my soundcard)?

I can add some details on my own code if it might be helpful.

Edit: if you'd like to see a live example, install google chrome beta and try this page. On my computer, a recording 5-10 seconds long is over 1 MB.

Many thanks,

Adrian

回答1:

You could try a couple of things. First off, I think you're on to something about "dropping every other element of the interleaved buffer" (converting the sound to mono).

For that you could choose to keep the left or the right channel. You could change the "interleave" function to be:

function interleave(inputL, inputR){
  return inputL; // or inputR
}

If you wanted to keep both channels, but "pan" them both center (to the single mono channel), you could do something like:

function interleave(inputL, inputR){
  var result = new Float32Array(inputL.length);
  for (var i = 0; i < inputL.length; ++i)
    result[i] = 0.5 * (inputL[i] + inputR[i]);
  return result;
}

That being said, there are potentially a lot of other placed you would have to change the encoded audio from being denoted as stereo to mono. However, my guess is (and I haven't used recorder.js, so I don't know it's inner workings), line 113/114 in the recorderWorker could probably changed to 1.

My guess is that you could get away with just changing the two places mentioned here (the interleave function, and the place where channel-count is set [line 114]) because: interleave and encodeWAV are only called through the exportWAV function, so not touching how the original worker has been recorder audio (and it's been recording stereo) hopefully won't break it. We would in that case only be making changes to the audio that was stored.



回答2:

In my case Chrome records audio at 96kHz and Firefox at 44.1kHz, that makes huge WAV files. I implemented a downsampling function inside recorderWorker.js where you can select the sample ratio you want, like 16000.

function downsampleBuffer(buffer, rate) {
    if (rate == sampleRate) {
        return buffer;
    }
    if (rate > sampleRate) {
        throw "downsampling rate show be smaller than original sample rate";
    }
    var sampleRateRatio = sampleRate / rate;
    var newLength = Math.round(buffer.length / sampleRateRatio);
    var result = new Float32Array(newLength);
    var offsetResult = 0;
    var offsetBuffer = 0;
    while (offsetResult < result.length) {
        var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
        var accum = 0, count = 0;
        for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
            accum += buffer[i];
            count++;
        }
        result[offsetResult] = accum / count;
        offsetResult++;
        offsetBuffer = nextOffsetBuffer;
    }
    return result;
}

and i call it when exporting the wav file:

function exportWAV(rate, type) {
    var bufferL = mergeBuffers(recBuffersL, recLength);
    var bufferR = mergeBuffers(recBuffersR, recLength);
    var interleaved = interleave(bufferL, bufferR);
    var downsampledBuffer = downsampleBuffer(interleaved, rate);
    var dataview = encodeWAV(rate, downsampledBuffer, false);
    var audioBlob = new Blob([ dataview ], {
        type : type
    });

    this.postMessage(audioBlob);
}


回答3:

I'm using the same recorder code and I needed to lower the bit rate. My solution produces a 11025Hz mono file. it's not very elegant so I'll be glad if someone has corrections for me.

First I change sample rate in the init function to be 11025 instead of the audio context's bit rate (this is the non elegant part since the context might not be 44100Hz).

I replace the interleave function content with this

var length = inputL.length / 4;
var result = new Float32Array(length);

var index = 0,
  inputIndex = 0;

while (index < length) {
    result[index++] = 0.25 * (inputL[inputIndex++] + inputL[inputIndex++] +
                              inputL[inputIndex++] + inputL[inputIndex++]);
}

return result;

This takes only the left channel and turns every 4 buffer samples into 1 in the result so it takes up less memory. if the bit rate is changed by the same ratio (divided by 4 e.g. 11025), the file will sound the same but will be much smaller.

I also changed the channel count in encodeWAV to one

/* channel count */
 view.setUint16(22, 1, true);

The recording will be 1/8 in size compared to originally produced file.