Re: asynchronous vs synchronous instantiation of AudioWorkerNodes

On Nov 6, 2014, at 7:42 PM, Robert O'Callahan <robert@ocallahan.org> wrote:
> On Fri, Nov 7, 2014 at 6:09 AM, Joseph Berkovitz <joe@noteflight.com> wrote:
> - Have createAudioWorker() return a Promise that yields a factory object with a method that synchronously creates nodes, with each node having its own AudioWorkerGlobalScope based on the already-loaded script... [snip]
> 
> - Have createAudioWorker() return a Promise that yields a factory object containing a live AudioWorkerGlobalScope, and allow further nodes to be instantiated which share this same scope. Script loading and execution both occur once per factory, because the scope is shared. However this programming model does not isolate node instances’ state from each other. (This is my understanding of a proposal that Jussi made, I haven’t been able to track down the original. Jussi has said that some simple use of closures would make the state bookkeeping easy but I don’t think an example was provided.)
> 
> I prefer the latter, although the former works too. The ability to share resources easily in the latter approach seems worth having.
> 

I am starting to feel that a Jussi-style proposal is better myself, although I originally felt otherwise. For instance, a node with substantial preloaded data (as today’s native HRTF implementation) would surely not want to load it up once per node.

Also, the value of preloading the script in my first proposal feels questionable if the script still has to be parsed and then fired up into a private scope on every node instantiation. In the latter approach, scripts are loaded, parsed and activated once and then it’s all over. Seems cleaner.

On Nov 6, 2014, at 7:44 PM, Robert O'Callahan <robert@ocallahan.org> wrote:
> On Fri, Nov 7, 2014 at 11:43 AM, Chris Wilson <cwilso@google.com> wrote:
> I DON'T like the proposal of sharing the scope across node instances, because I don't see how you could store state then; but on the other hand, some state MIGHT want to be shared across nodes (e.g. HRTF database).  Hmm.  Complexifies the design quite a bit to have a global scope and a node scope, though.
> 
> One option would be to have the spec *required* that AudioProcessEvent.parameters is the same object every time the event fires for a given Node. Then the worker script can attach its own per-node state to the parameters object (using regular JS expandos).

I agree with RoC’s direction: something like this can make the design reasonably simple and make it clear to developers. I’ve arrived at a slightly different flavor of this idea, which I’ll try to describe here.

The spec could state that the “target” and “currentTarget” properties of all events delivered to this script are an object of type AudioWorkerProcessor. This object's identity must be preserved across *all events that pertain to any given node*. You can think of it as an audio-thread proxy for the node; it is also considered to be the dispatcher of node-related audio events.

The IDL for it might look like this:

interface AudioWorkerProcessor : EventTarget
 {
    attribute object data;        // this is initially an empty object; the script can store arbitrary state in its properties
    void postMessage(…);     // posts a message back to the main-thread AudioWorkerNode
    void terminate();               // kills audio-engine reference to node resources; no further events will be dispatched and GC is enabled
};

In this scheme, the AudioWorkerGlobalScope handlers onmessage and onaudioprocess continue to be common to all nodes, but they deliver events that have different AudioWorkerProcessors as targets.

If you don’t like common handlers, a variation of this approach could move onaudioprocess and onmessage into the AudioWorkerProcessor as EventHandler attributes.  A shared oncreate handler in the AudioWorkerGlobalScope would then set up these handlers for each node, when it was created. (This would even allow different nodes created by the same script to behave differently, if the oncreate event were able to communicate some node-construction parameter from the main thread.)

For this to work, I think the Promise from the original createAudioWorker() needs to yield something like this:

interface AudioWorkerNodeFactory
{
    AudioWorkerNode createAudioWorkerNode(…);
}

I’m not being fussy about all of these names, by the way, I assume they’d all need to be improved.

…Joe

Received on Friday, 7 November 2014 18:13:28 UTC