Re: WaveShaperNode.curve

To recap, here are what I understand to be the core issues with passing buffers to nodes of various types, including buffer source nodes, wave shaper nodes, convolver nodes, etc. - (please fix if you see any misunderstanding or omissions) - 
Race conditions should not be possible. Setting a buffer parameter of a node and subsequent modification of that buffer should not affect the behaviour of the node in order for the system to not be racy (note: whether to allow that modification is an orthogonal issue). Current opinion (Robert O' Callahan and Ehsan Akhgari) is that the ideal behaviour in this case is that subsequent modification of the buffer should fail dramatically instead of subtly.
Backward compatibility needs to be maintained as much as possible - current styles of usage shouldn't break.
The current standing suggestion is to neuter ArrayBuffer and Float32Arrays passed to nodes, to prevent them from being modified further. The advantage of this is that race conditions are avoided, and it can be arranged such that current interfaces aren't broken (by, for example, having getters return copies). However, buffers can no longer be shared between nodes. The disadvantage is that different nodes now MUST use different copies of a buffer, which increases demand on memory and decreases programming convenience.

Another possibility -

The proper solution to resolving race conditions due to data structures shared between threads is to use immutable data structures. The only binary data structure in Javascript that is immutable is the Blob (afaik). If the various nodes that currently use Float32Array buffer attributes are spec'd to accept Blob objects instead, perhaps with mime type "audio/x-raw;format=float32", we solve all race conditions. Data stored in a Blob object can now be safely shared between nodes. Implementations are free to choose whether to keep internal copies in nodes or not. 

However, current code would break. A Blob object "b" can be created from a Float32Array "arr" like this -

	var b = new Blob([arr], {type: "audio/x-raw;format=float32"});

If a Float32Array attribute -- ex: wave shaper node's 'curve' attribute -- is implemented to a) accept Blob buffers and b) when a Float32Array is assigned to it, it is first converted into a Blob, which is subsequently returned by the attribute's getter, we get compatibility with current code base.

The following kinds of code will work correctly without race conditions -

var shaper1 = context.createWaveShaper(...);
var shaper2 = context.createWaveShaper(...);
var buffer = new Float32Buffer(..); // fill up with something.

shaper1.curve = buffer; // First internal copy made.
shaper2.curve = buffer; // Second internal copy made.
shaper1.curve = shaper2.curve = buffer; // One internal copy made in shaper2 which is shared with shaper1.

var b = new Blob([buffer], {type: "audio/x-raw;format=float32"}); // Copy of buffer made. buffer is free to be GC'd if no other use for it.
shaper1.curve = shaper2.curve = b; // Shared immutable buffer. No race conditions.
shaper1.curve = b; // Shared immutable buffer.
shaper2.curve = b; // Shared immutable buffer.
shaper3.curve = b.slice(64, 128, b.type); // Shared slice of immutable buffer. Also no race condition.

The following kind of code will fail dramatically -

var shaper = context.createWaveShaper(...);
var buf = new Float32Buffer(100); 
shaper.curve = buf; // Internal copy made.
for (var i = 0; i < 100; ++i) {
	shaper.curve[i] = something; // Array-style indexing into Blob will fail.
}

The following kind of code is probably irrelevant since I don't expect anyone (yet) to have set a buffer without filling it first -

var shaper = context.createWaveShaper(...);
var buf = new Float32Buffer(100); 
shaper.curve = buf; // Internal copy made.
for (var i = 0; i < 100; ++i) {
	buff[i] = something; // Modifications to buff have no effect.
}

Thoughts?

Best,
-Kumar

Received on Thursday, 16 May 2013 14:17:20 UTC