- From: Ehsan Akhgari <ehsan.akhgari@gmail.com>
- Date: Fri, 26 Jul 2013 15:00:38 -0400
- 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>
- Message-ID: <CANTur_4HiFsNawRv-JSj8si-fAS+iYZvXN+e2xZQL4rswhRduQ@mail.gmail.com>
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(). Cheers, -- Ehsan <http://ehsanakhgari.org/>
Received on Friday, 26 July 2013 19:01:46 UTC