Skip to content

Async encode/decode, and encoding an AudioBuffer #1

Description

@eliot-akira

Hello, thank you for creating this library. I had some questions about how to use it.

From what I understand in wasm/index.js, the encode/decode functions are synchronous and they block the main thread in browser/node. Is that true?

If so, is it possible to load this library from a worker, to make it async/non-blocking? I'll see if I can achieve it and report back here if successful.

Looking at other similar libraries, wasm-media-encoders, wasm-audio-decoders, and opusscript, I see their API is synchronous and blocking also. ffmpeg.wasm and lame-wasm are async.

That's interesting because I would have expected, for most purposes, async encode/decode is preferred for both browser and server side, so as not to block the main thread. For example, how AudioContext.decodeAudioData() works.


Currently, I'm encountering some difficulties trying to encode (and decode back) an instance of AudioBuffer.

First, I noticed that if the sample rate is 44100, the Encoder/Decoder constructor throws an error, opus: invalid argument.

const { encode } = new Encoder({
  channels: audioBuffer.numberOfChannels,
  sample_rate: audioBuffer.sampleRate
})

If I force it to 48000, it doesn't throw.

Next, I learned how to extract PCM data from an AudioBuffer using getChannelData().

const buffers = [] // Float32Array per channel

for (let i=0, len=audioBuffer.numberOfChannels; i < len; i++) {
  buffers.push(audioBuffer.getChannelData(i))
}

When I pass a channel's data to encode(), it throws RangeError: source array is too long.

The error is from the first line of RawEncoder.encode() in @evan/wasm/target/opus/deno.js.

encode(buffer) {
    bptrs.set(buffer = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength));
    return pptrs.slice(0, err(wasm.opus_encode(this.#ptr, bptr, buffer.length / 2 / this.channels, pptr, pptrl)));
}

..So I should convert my Float32Array to an Uint8Array before passing it to encode. Is that correct?

const channelData = buffers[0].buffer // Get ArrayBuffer from Float32Array
const pcm = new Uint8Array(channelData)
const opusData = encode(pcm)

The conversion seems to work, but still results in the same error, RangeError: source array is too long.

EDIT: I'm guessing it's because I set the wrong sample rate when instantiating the encoder. And probably it's impossible (or unwise, haha) to try to up-scale from 44.1 to 48 kHz.


Well, I'll try to figure out these questions on my own, but if you have any thoughts, I'd appreciate it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions