arseneyr / wasm-media-encoders

MP3 and Ogg Vorbis encoders for the browser and Node
MIT License
35 stars 4 forks source link

Web Workers support #17

Open vyconm opened 2 months ago

vyconm commented 2 months ago

Awesome little package - I am currently experimenting with converting audio files to smaller versions for use in our AI platform, and this fits just the case!

As that can obviously heavily block the main thread, I am trying to create a client side worker for this, but seem to be running into Browser API issues:

Uncaught (in promise) TypeError: (self.AudioContext || self.webkitAudioContext) is not a constructor at self.onmessage (encodeMp3Worker.js:6:24) self.onmessage @ encodeMp3Worker.js:6

We are using SvelteKit 2 with Svelte 5, which is all based around Vite. My code looks like this:

import { createEncoder } from "wasm-media-encoders";

self.onmessage = async function (e) {
  const { pcmLeft, pcmRight, sampleRate, numberOfChannels } = e.data;

  try {
    const encoder = await createEncoder(
      "audio/mpeg",
      new URL("wasm-media-encoder/wasm/mp3", import.meta.url),
    );

    encoder.configure({
      sampleRate: sampleRate,
      channels: numberOfChannels,
      vbrQuality: 6,
    });

    let outBuffer = new Uint8Array(1024 * 1024);
    let offset = 0;
    let moreData = true;

    while (true) {
      const mp3Data = moreData
        ? encoder.encode([pcmLeft, pcmRight])
        : encoder.finalize();

      if (mp3Data.length + offset > outBuffer.length) {
        const newBuffer = new Uint8Array(mp3Data.length + offset);
        newBuffer.set(outBuffer);
        outBuffer = newBuffer;
      }

      outBuffer.set(mp3Data, offset);
      offset += mp3Data.length;

      if (!moreData) {
        break;
      }

      moreData = false;
    }

    const mp3Blob = new Blob([new Uint8Array(outBuffer.buffer, 0, offset)], {
      type: "audio/mp3",
    });

    self.postMessage({
      mp3Blob,
    });
  } catch (error) {
    console.error("Error during MP3 encoding:", error);
    self.postMessage({
      error: error.message,
    });
  }
};

I was under the impression, that the imported package should basically only be WASM, yet it is searching for Browser API types - is this expected behaviour?

Thank you for the help!

arseneyr commented 1 month ago

No, this is not expected. This package is just a WASM binary with some JS glue code, but the JS doesn't do anything too fancy. Definitely nothing with AudioContexts.

If I had to guess, either Vite or SvelteKit or a combination of both is pulling in some polyfills for WebAudio stuff that is not supported inside web workers. You would have to look at the transpiled code to see what's actually getting run though.