- From: Hongchan Choi <hongchan@google.com>
- Date: Wed, 01 Jun 2016 18:27:16 +0000
- To: Joe Berkovitz <joe@noteflight.com>
- Cc: Audio Working Group <public-audio@w3.org>
- Message-ID: <CAGJqXNsnvr3j9zvFBQVOZZwfGcukikoctF81AS9jOSd10NywAA@mail.gmail.com>
> But with AudioWorkletNodes, we can't take this nice behavior for granted. Because of the synchronous nature of Node creation (you don't wait for a Promise), the Processor won't exist yet at the exact moment the Node is created. I want the construction of AudioWorkletNode to be synchronous. This must be ensured by design. We only need the promise for importing a script, not instantiating of a node. In that sense, `created()` method might not be necessary after all. I am doing more research on ES6 class pattern, and I believe there are some more features coming up soon (const/static) that might be beneficial for our use case. I think we should explore both directions: the implicit/terse way and the explicit/verbose way. On Wed, Jun 1, 2016 at 11:18 AM Joe Berkovitz <joe@noteflight.com> wrote: > . . . . . ...Joe > > Joe Berkovitz > President > Noteflight LLC > > +1 978 314 6271 > > 49R Day Street > Somerville MA 02144 > USA > > "Bring music to life" > www.noteflight.com > > On Wed, Jun 1, 2016 at 1:42 PM, Hongchan Choi <hongchan@google.com> wrote: > >> It took me a day to think about questions you raised. I think we have an >> important decision to make. Please find my responses inline: >> >> >> Hi Hongchan, >>> >>> Thanks for responding! I will attempt some stunted responses from my >>> phone since I'm traveling today, to keep the discussion going. >>> >>> Based on our previous discussion, I'd expect that there needs to be a >>>> subclass constructor that explicitly copies these values out of the >>>> AudioNodeOptions into the new node's AudioParams, like this: >>>> >>>> class BitcrusherNode extends AudioWorkletNode { >>>> constructor(ctx, options) { >>>> super(ctx, 'Bitcrusher', options); >>>> if (options.hasOwnProperty('bitDepth') { >>>> this.bitDepth.value = options.bitDepth; >>>> } >>>> // ...initialize other params, initial state up front, possibly >>>> utilizing postMessage()... >>>> } >>>> >>> >>> Just to confirm: so this is a node definition, not the processor? >>> Initializing these values from the main thread is a definitely better idea, >>> but the pattern here is quite involved and verbose. I think we can start >>> from this idea and try to simplify the syntax to find a sweet spot. I >>> really need to think about having two definitions for both threads. Very >>> explicit, but that also makes us diverge from Worklet infrastructure which >>> I am a bit afraid of. >>> >>> >>> Sorry, I don't see where this diverges from Worklets -- what am I >>> missing? >>> >> >> Worklet spec does not specify (nor expose) main thread interface. The >> class definition is solely for off-main-thread processing. Now I am >> realizing this is the biggest difference between AudioWorklet and other >> worklet variants. Let's say you can define the node representation as shown >> above, how can the browser match the corresponding processor object? That >> part seems also magical to me. In that case, we might have to introduce a >> new method that runs within global scope to define a `name` and to match >> the processor accordingly. Is that okay? >> > > The only magic is in the second argument to the AudioWorkletNode > constructor (DOMString name), that determines which registered Processor is > to be used on the audio side. > > The only thing new I've done here is say: it's useful to extend > AudioWorkletNode into a subclass (here, that's BitcrusherNode). The > subclass calls the superclass constructor, which has the same signature it > always had: [Constructor(AudioContext audioContext, DOMString name, > optional AudioNodeOptions options)]. The "name" argument is just filled in > by the subclass, so that the person instantiating the subclass doesn't need > to know it. In my above reworked example, it's 'Bitcrusher', just as it was > in your original example where you call new AudioWorkletNode(ctx, > 'Bitcrusher', {...}). > > >> >> The reason I made this a subclass is because otherwise I did not see how >>> to make use of the custom AudioNodeOptions entries that your example >>> employed to initialize the bitcrusher AudioParams. The options are >>> constructor args, so unless there is some behavior in AudioWorkletNode that >>> automagically initializes AudioParams with matching names, I don't see how >>> else it can happen. >>> >> >>> If you do envision this init behavior then please make it explicit. >>> Would it apply only to AudioParams? (I assume so because nothing else about >>> a Processor can be declared) >>> >>> Of course one could leave the Params out of the ctor options argument >>> and just initialize them after creating the AudioWorkletNode. >>> >> >> One thing I also want to avoid is over-complicating the definition of >> AudioWorkletNode. Your idea makes perfect sense to me, but I think it is >> too burdensome to developers having to define a node and a processor only >> for the initialization of AudioParams. Beyond the parameter initialization, >> what else can we do? You can define a new method, but it is just a wrapper >> function of postMessage(). I agree that being explicit always win at the >> end, so this is something we have to decide. >> > > I want to make way for the group to chime in here. Personally [not as > chair] I think it's best if everything about initialization is explicit. > >> >> >>> The only opportunity we have to discover parameter descriptors is before >>>> the success of the promise from the script import for a node type. >>>> >>> So *maybe* it works like this: after the script is imported for an >>>> AudioWorkletProcessor, the Processor gets instantiated once prior to the >>>> success of the Promise and its paramDescriptors getter is examined; the >>>> result determines the set of parameters exposed by subsequent nodes of that >>>> type. The Processor instance is then thrown away; its only purpose was to >>>> figure out what params exist for this node type. >>>> >>> >>> Can you elaborate on this a bit more? I am not sure if I am following >>> you. >>> >>> >>> Along the lines of my previous response, I was asking myself how the API >>> can know what parameters exist for a Node whose Processor doesn't exist >>> yet. Yet it is the Processor that exposes the param descriptors. >>> >> >>> The only way I see for this to happen is for a Processor to be created >>> as part it's registration, solely for the purpose of interrogating its >>> param descriptors. >>> >> >> I am still not sure about what is being asked, but let me try; you cannot >> know the parameters of a node before its instantiation. Isn't this how a >> regular audio node works? After the construction and when created() method >> gets called, the node reference and AudioParams within will be accessible. >> Or are you asking we implement the new feature for AudioWorkletNode? I can >> see the benefit of figuring out parameters from a node that hasn't been >> created, but I believe it is slightly off-topic. >> > > I'm not saying one needs to know the parameters of a Node *before* its > instantiation. > > I'm saying one needs to know the parameters of a Node *immediately > following* its instantiation. You can do this with a regular node: > > var g = new GainNode(ctx); > g.gain.value = 1.5; // g.gain exists right away! > > But with AudioWorkletNodes, we can't take this nice behavior for granted. > Because of the synchronous nature of Node creation (you don't wait for a > Promise), the Processor won't exist yet at the exact moment the Node is > created. We want to be able to do this: > > var bc = new AudioWorkletNode(ctx, 'Bitcrusher'); // or, new > BitcrusherNode(ctx) in my version > bc.bitDepth.value = 1; // bc.bitDepth is there right away! > > It seems to me that bc.bitDepth can only exist as an AudioParam at that > point if we already know the parameter descriptors declared by the > Processor registered for 'Bitcrusher'. And no one on the main thread side > knows exactly when the AudioWorkletProcessor will be created. That is why I > think this information needs to be made available to the main thread side > somehow, as a byproduct of registering the AudioWorkletProcessor on the > audio side. > >
Received on Wednesday, 1 June 2016 18:27:58 UTC