WebAudio / web-audio-api

The Web Audio API v1.0, developed by the W3C Audio WG
https://webaudio.github.io/web-audio-api/
Other
1.04k stars 165 forks source link

Support Nesting AudioNodes #2436

Open fabb opened 5 years ago

fabb commented 5 years ago

It would be helpful for modularization if there was some kind of „ContainerNode“ that can contain an arbitrary network of AuudioNodes and can be connected like an AudioNode on the outside. That would make it possible to distribute synthesizers including effects like reverb and delay as npm packages, that can be connected to an existing AudioNode network in a client project easily.

notthetup commented 5 years ago

I remember there were a bunch of experiments to try to this using JS libraries. I can't seem to find any references (I'm just getting rusty), but I remember seeing a bunch of blog posts/talks about this in the past.

We kind of tried to do this (a long time ago) with https://github.com/sonoport/soundmodels, where the "SoundModels" are Javascript objects behaved almost like WebAudio Source Nodes. They can be connected to and connected form. They had play and other methods including parameter automation methods. (https://github.com/Sonoport/soundmodels/blob/master/core/SPAudioBufferSourceNode.js)

The trick is to use GainNodes at the beginning and end of your chain of AudioNodes to help with the connections.

hoch commented 5 years ago

It was discussed in the past (see WebAudio/web-audio-api#251) but the discussion ended up with "use AudioWorklet".

There have been multiple attempts to do this properly, but we have not seen anything worthy of standardization. So people are using homemade solutions in their projects. As @notthetup mentioned above, the trick is to proxy input/output with the gain node. I personally prefer Max's inlet/outlet approach for this kind of thing.

Perhaps something like this:

class AmpNode extends ContainerNode {
  #amp;

  constructor(context, container) {
    super(context);
    this.#amp = new GainNode(context);
    container.inlet.connect(this.#amp).connect(container.outlet);
    container.registerParameter('volume', this.#amp.gain);
  }
}

const context = new AudioContext();
const osc = new OscillatorNode(context);
const amp = new AmpNode(context);

osc.connect(amp).connect(context.destination);

amp.volume; // is AudioParam.

Obviously, this approach needs new APIs (container.inlet, container.outlet, container.registerParameter) and other side effects of these additions must be explored/discussed. Also I doubt that this can be a V1 issue considering we started V2 brainstorming now.

hoch commented 4 years ago

Action items from TPAC: Experiment with extending GanNode and investigate if it's feasible.