W3C home > Mailing lists > Public > public-audio@w3.org > July to September 2013

Re: New proposal for fixing race conditions

From: Ehsan Akhgari <ehsan.akhgari@gmail.com>
Date: Fri, 26 Jul 2013 15:00:38 -0400
Message-ID: <CANTur_4HiFsNawRv-JSj8si-fAS+iYZvXN+e2xZQL4rswhRduQ@mail.gmail.com>
To: Chris Wilson <cwilso@google.com>
Cc: "Robert O'Callahan" <robert@ocallahan.org>, Chris Rogers <crogers@google.com>, Marcus Geelnard <mage@opera.com>, Jer Noble <jer.noble@apple.com>, Russell McClellan <russell@motu.com>, WG <public-audio@w3.org>
On Fri, Jul 26, 2013 at 12:18 PM, Chris Wilson <cwilso@google.com> wrote:

> On Tue, Jul 23, 2013 at 3:26 PM, Robert O'Callahan <robert@ocallahan.org>wrote:
>> On Wed, Jul 24, 2013 at 9:18 AM, Chris Rogers <crogers@google.com> wrote:
>>> On Tue, Jul 23, 2013 at 1:10 PM, Chris Wilson <cwilso@google.com> wrote:
>>>> OK.  I want to load an audio file, perform some custom analysis on it
>>>> (e.g. determine average volume), perform some custom (offline) processing
>>>> on the buffer based on that analysis (e.g. soft limiting), and then play
>>>> the resulting buffer.
>> My original proposal (as implemented in Gecko) makes no extra copies
>> here. The play operation neuters the JS array(s) and migrates the data to
>> the audio thread.
> I must be looking at the wrong proposal. (
> http://lists.w3.org/Archives/Public/public-audio/2013AprJun/0644.html)
> implies the load (which would create an AudioBuffer) creates one instance
> of the data, and the "acquire contents" (in order to perform analysis and
> processing) would create a copy.

This is the correct link.  See this prose:

[IMPORTANT: Implementations can and should optimize this operation so that
a) multiple "acquire contents" operations on the same AudioBuffer (with no
intervening calls to getChannelData) return the same shared data; b)
replacing the internal Float32Arrays with new Float32Arrays happens lazily
at the next getChannelData (if any); and thus c) no data copying actually
happens during an "acquire contents" operation. Let me know if this is
unclear; it's terrifically important.]

Let me try to illustrate this with an example, as I believe there is a lot
of confusion over this point, given the number of times we've discussed
memcpy's in this thread and others.  :-)

var buffer = ctx.createBuffer(...); // creates internal typed array(s) to
represent the channel data
var leftChannel = buffer.getChannelData(0); // grabs a reference to one of
those typed arrays
for (var i = 0; i < length; ++i) {
  leftChannel[i] = foo(); // fill up leftChannel
bufferSourceNode.buffer = buffer; // This "acquires the contents" of
buffer.  No memcpy happens here, and leftChannel gets neutered, so future
accesses to it will throw

Now, *if* you call buffer.getChannelData() *after* the last line of code
above, you will get a *new* typed array back with a copy of the original
data (this is where a memcpy happens) and modifying the contents of that
array has no effect on the buffer that the source node is working on.

Since it is expected that most applications will not call getChannelData()
after they have finished filling up their buffers, there will be no memcpy
involved in the "normal" usage of the API.

This is already implemented in Gecko, you can try it out yourself to see
there are no memory spikes if your test case doesn't have the second call
to getChannelData().


Received on Friday, 26 July 2013 19:01:46 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 19:03:23 UTC