How to convert pcm16Audio to .wav then to a base64 string

131 Views Asked by At

I'm using a voice capture SDK that returns a pcm16Audio representation of the capture. I need to convert this to wav, then convert the wav to base64 to send to an API.

This is the function in the SDK that returns the pcm16Audio:

async getRecordedAudioPcm16Samples() {
let audio = new Int16Array(this.numRecordedSamples);
    let offset = 0;
    for (let i = 0; i < this.audioBuffers.length; i++) {
      audio.set(this.audioBuffers[i], offset);
      offset += this.audioBuffers[i].length;
    }
    return audio;
  }

I believe the way to do this is to use 'audiobuffer-to-wav' from npm then convert the wav to base64:

import toWav from 'audiobuffer-to-wav';

const audio = sdk.getRecordedAudioPcm16Samples();
audio.then((pcm16Audio) => {

    const audioContext = new (window.AudioContext || window.webkitAudioContext)();
    audioContext.decodeAudioData(pcm16Audio.buffer, (buffer) => {
              // encode AudioBuffer to WAV
              const wav = toWav(buffer);
              // convert wav to base64...

I get this error when calling decodeAudioData in Firefox: 'DOMException: The buffer passed to decodeAudioData contains an unknown content type.'

I get this error in Chrome: 'DOMException: Failed to execute 'decodeAudioData' on 'BaseAudioContext': Unable to decode audio data'

Am I on the right track? Thank you for any help!

1

There are 1 best solutions below

0
chrisguttandin On

decodeAudioData() is very limited. It only works with a few file types and support is very inconsistent across browsers. As far as I know there is no browser which decodes raw PCM data.

But it can be done manually in a fairly straightforward way. An AudioBuffer with a sampleRate of 48kHz and a length of a second can for example be created like this:

new AudioBuffer({ length: 48000, sampleRate: 48000 });

You can then copy the samples by filling a Float32Array with values between -1 and 1. A value stored as int16 can usually be converted like this:

const f32 = int16 < 0 ? int16 / 32768 : int16 / 32767;

The last step is to use the Float32Array to copy the samples onto the AudioBuffer. Here is a full example for one second of audio stored as int16.

const length = 48000;
const audioBuffer = new AudioBuffer({ length, sampleRate: 48000 });
const channelData = new Float32Array(length);

for (let i = 0; i < length; i += 1) {
    const int16 = THE_VALUES_AS_INT_16[i];
    const f32 = int16 < 0 ? int16 / 32768 : int16 / 32767;

    channelData[i] = f32;
}

audioBuffer.copyFromChannel(channelData, 0);

You can then use that audioBuffer and convert it to a wav file with the package you mentioned above.