elsmr / mp3-mediarecorder

šŸŽ™MediaRecorder ponyfill that records audio as mp3

Home Page:https://mp3-mediarecorder.elsmr.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Post-processing

giuseppeg opened this issue Ā· comments

Hi there this library looks dope!

How hard would it be to do the conversion later instead of doing it in real time?
I assume I'd need to derive the solution from https://github.com/elsmr/mp3-mediarecorder/blob/master/src/worker/index.ts

I want to encode an audio file acquired with the native MediaRecorder API, so I am using this library worker with a custom "frontend" see implementation below and the issue right after

// Given:
// recording = [Blob]

const worker = new Worker(new URL("../lib/worker.ts", import.meta.url));
const PostMessageType = {
  DATA_AVAILABLE: "DATA_AVAILABLE",
  START_RECORDING: "START_RECORDING",
  STOP_RECORDING: "STOP_RECORDING",
  ERROR: "ERROR",
  BLOB_READY: "BLOB_READY",
  WORKER_RECORDING: "WORKER_RECORDING",
};

let onComplete;
const audioBlobPromise = new Promise((resolve) => {
  onComplete = resolve;
});

worker.addEventListener("message", (event) => {
  const message = event.data;
  switch (message.type) {
    case PostMessageType.WORKER_RECORDING: {
      // Send the chunks received by the MediaRecorder to the worker for encoding
      // recording = [Blob]
      const context = new AudioContext();
      Promise.all(
        recording.map((r) =>
          r.arrayBuffer().then((arrayBuffer) => {
            let onSuccess, onError;
            const decodeAudioDataPromise = new Promise(
              (resolve, reject) => {
                onSuccess = (buffer) => {
                  const data = buffer.getChannelData(0);
                  worker.postMessage({
                    type: PostMessageType.DATA_AVAILABLE,
                    data,
                  });
                  Promise.resolve().then(() => {
                    resolve();
                  });
                };
                onError = () => {
                  reject();
                };
              }
            );
            context.decodeAudioData(arrayBuffer, onSuccess, onError);
            return decodeAudioDataPromise;
          })
        )
      )
        .then(() => {
          // End the encoding. It will result in PostMessageType.BLOB_READY.
          setTimeout(() => {
            worker.postMessage({ type: PostMessageType.STOP_RECORDING });
          }, 1000);
        })
        .finally(() => {
          context.close();
        });
      break;
    }
    case PostMessageType.BLOB_READY: {
      mimeType = "audio/mpeg";
      onComplete([message.blob]);
      break;
    }
    default: {
      console.error(
        "An error occured while encoding the recording ",
        message.error || "unknown error"
      );
      onComplete(recording);
    }
  }
});

// Start the encoder
worker.postMessage({
  type: PostMessageType.START_RECORDING,
  config: { sampleRate: 44100 },
});

const audioBlob = await audioBlobPromise;
worker.terminate();
console.log({ audioBlob });

new File(audioBlob, `recording.mp3`, {
  lastModified: Date.now(),
  type: mimeType,
})

With this when I send data to the worker, the handler throws an error here because encodedBytesAmount is -1:

const encodedBytesAmount = vmsgInstance.vmsg_encode(vmsgRef, data.length);
if (encodedBytesAmount < 0) {
throw new Error('encoding_failed');
}

{ data: Float32Array(246077)}, dataLength: 246077, encodedBytesAmount: -1 }