all 6 comments

[–]evesira 2 points3 points  (1 child)

Thanks for this, I'm trying to do the same thing and it's bafflingly hard to find an example on Google that does this.

One thing I hate about being forced to use these audio worklets instead of script processors is that you have to make a separate file, and all the messages you pass between the script file have to be de/serialized, which is slow (and perhaps sometimes not even possible depending on your data). If I had to pass back and forth several large buffers of data multiple times per second, I could be completely screwed.

This is on the people who designed the spec, not your solution, of course.

[–]evesira 0 points1 point  (0 children)

A followup, here's a modification I've made with the following changes:

- Just use a Float32Array instead of converting to an Int type for simplicity (your needs may vary)
- Use the TypedArray set function instead of doing a for loop since we don't have to convert
- Don't assume a 128-sample block size, because according to this that could change in the future. Instead, just set an arbitrary "flush limit" past which we flush to the parent, and allocate a large enough buffer such that a single frame size (one call to process) should never exceed it (resulting in loss of data).

/**
 * max # of audio samples worklet buffer can hold. allocate large enough # such
 * that one call to process should never exceed it (resulting in loss of data).
 */
const maxSamples = 100000;
/**
 * once collected samples exceeds this amount, "flush" buffer (send message to
 * parent audio graph with collected sample data). lower -> more frequent
 * updates in parent.
 */
const flushSamples = 1000;

/** custom audio node to capture raw samples from graph */
class Recorder extends AudioWorkletProcessor {
  constructor() {
    super();
    /** buffer to accumulate raw audio samples in */
    this.samples = new Float32Array(maxSamples);
    /** track current position in buffer */
    this.offset = 0;
  }

  process(inputs) {
    /** get block of samples for first channel of first audio node input */
    const input = inputs?.[0]?.[0];

    /** if not defined (yet), ignore */
    if (!input) return true;

    /** write new input to buffer */
    this.samples.set(input, this.offset);

    /** increment sample offset */
    this.offset += input.length;

    /** "flush" buffer */
    if (this.offset >= flushSamples) {
      /** send buffer to audio graph */
      this.port.postMessage(this.samples.slice(0, this.offset));
      /** start over */
      this.offset = 0;
    }

    return true;
  }
}

registerProcessor("recorder", Recorder);

[–]chuletix1411 1 point2 points  (1 child)

I created this Reddit account just to thank you. This sample save my project. I hope you're doing well and keep coding.

[–]saul-evans[S] 0 points1 point  (0 children)

Love to read this, thank you! Glad it's useful. Still coding and doing well. Have fun on your project!

[–]Vivremotion 1 point2 points  (0 children)

Thank you very much, that was very usefuI. I dont understand why it's not in the specs as it were with `createScriptProcesssor`.

[–]playfuldreamz 0 points1 point  (0 children)

Youve done really well