Closed Bug 1391482 Opened 7 years ago Closed 7 years ago

delay conversion of output of decodeAudioData() from int16_t to float until necessary

Categories

(Core :: Web Audio, enhancement, P1)

ARM
Android
enhancement

Tracking

()

RESOLVED FIXED
mozilla57
Tracking Status
firefox57 --- fixed

People

(Reporter: karlt, Assigned: karlt)

References

Details

Attachments

(15 files)

59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
59 bytes, text/x-review-board-request
padenot
: review+
Details
Some web audio game client authors have observed significantly increased memory requirements of AudioBuffers storing Float32 data compared with native games typically using signed 16-bit. This issue is particularly problematic on mobile devices, where typically less memory is available. Initially raised at https://lists.w3.org/Archives/Public/public-audio/2013OctDec/0294.html Gecko defines MOZ_SAMPLE_TYPE_S16 on mobile platforms, in which case decode and resampling for decodeAudioData() is already performed in 16-bit arithmetic. Instead of converting to float for storage, this conversion can be performed on demand. In AudioBufferSourceNode usage (the typical case), only one block need be converted at a time. Any buffer generated by decodeAudioData() will be stored in 16-bit format until and unless JS requests it in Float32 ArrayBuffer format via getChannelData(). Other platforms decode and resample in float format. I do not intend to convert back to signed 16-bit for those platforms.
Most of the changes here are about passing a more generic type than ThreadSharedFloatArrayBufferList, one that can contain either float or 16-bit data. AudioChunk already exists for that and has the nice advantage that it keeps the length together in the same struct.
Rank: 15
Priority: -- → P1
Comment on attachment 8898567 [details] bug 1391482 use AudioChunk to generalize AudioBuffer shared channel storage https://reviewboard.mozilla.org/r/169932/#review178034
Attachment #8898567 - Flags: review?(padenot) → review+
Comment on attachment 8898568 [details] bug 1391482 add a mechanism to pass an AudioChunk from node to engine https://reviewboard.mozilla.org/r/169934/#review178036
Attachment #8898568 - Flags: review?(padenot) → review+
Comment on attachment 8898569 [details] bug 1391482 generalize shared channel data from AudioBuffer as AudioChunk https://reviewboard.mozilla.org/r/169936/#review178038
Attachment #8898569 - Flags: review?(padenot) → review+
Comment on attachment 8898570 [details] bug 1391482 add a fallible SharedBuffer::Create() https://reviewboard.mozilla.org/r/169938/#review178010 ::: dom/media/SharedBuffer.h:67 (Diff revision 1) > + return InternalCreate(&malloc, aSize); > + } > + > static already_AddRefed<SharedBuffer> Create(size_t aSize) > { > + // Use moz_x_malloc() to include its diagnostic message indicating the s/moz_x_malloc/moz_xmalloc/ ::: dom/media/SharedBuffer.h:86 (Diff revision 1) > CheckedInt<size_t> size = sizeof(SharedBuffer); > size += aSize; > if (!size.isValid()) { > MOZ_CRASH(); > } > - void* m = moz_xmalloc(size.value()); > + void* m = (*aMalloc)(size.value()); I thought we had a style guide entry stating that `new` should be preferred in new code. I can't find it though. Here it does not matter too much because we're allocating bytes and not elements of a bigger size, so we can't forget the multiplication.
Attachment #8898570 - Flags: review?(padenot) → review+
Comment on attachment 8898571 [details] bug 1391482 keep custom oscillator data buffer on graph thread only long enough to initialize the PeriodicWave https://reviewboard.mozilla.org/r/169940/#review178040
Attachment #8898571 - Flags: review?(padenot) → review+
Comment on attachment 8898572 [details] bug 1391482 use AudioChunk to store and pass PeriodicWave data to engine https://reviewboard.mozilla.org/r/169942/#review178022 ::: commit-message-9d9c4:1 (Diff revision 1) > +bug 1391482 use AudioChunk to store and pass PeriodicWave data to engine r?padenot A bit weird to have an `AudioChunk` containing Fourier coefficients, but it makes the code cleaner indeed.
Attachment #8898572 - Flags: review?(padenot) → review+
Comment on attachment 8898573 [details] bug 1391482 remove now-unused ThreadSharedFloatArrayBufferList SetBuffer() variant https://reviewboard.mozilla.org/r/169944/#review178042
Attachment #8898573 - Flags: review?(padenot) → review+
Comment on attachment 8898574 [details] bug 1391482 accept int16_t sample buffers in AudioBufferSourceNode https://reviewboard.mozilla.org/r/169946/#review178030 ::: dom/media/webaudio/AudioBufferSourceNode.cpp:308 (Diff revision 1) > aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock; > > + if (mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) { > + const float* inputData = > + mBuffer.ChannelData<float>()[i] + mBufferPosition; > - WebAudioUtils::SpeexResamplerProcess(resampler, i, > + WebAudioUtils::SpeexResamplerProcess(resampler, i, This will allocate if the resampling rate is less than 0.25 and the platform uses int16 for audio output, and the buffer is originally in float32. One one hand, this whole patch set would be amazing on desktop (and we don't have to change the spec to implement it), and this would mean we allocate a bit more in some case. On the other hand, solving the memory problem can also be done with explicit in-memory compression, and would be better. This is likely to require a spec change. The allocation can also be removed by compiling two copies of the resampler, one for int, one for float.
Attachment #8898574 - Flags: review?(padenot) → review+
Attachment #8898575 - Flags: review?(padenot) → review+
Comment on attachment 8898576 [details] bug 1391482 move AudioBuffer parameter checking to constructor https://reviewboard.mozilla.org/r/169950/#review178046
Attachment #8898576 - Flags: review?(padenot) → review+
Comment on attachment 8898577 [details] bug 1391482 add a method to create an AudioBuffer from AudioChunk data https://reviewboard.mozilla.org/r/169952/#review178048
Attachment #8898577 - Flags: review?(padenot) → review+
Comment on attachment 8898578 [details] bug 1391482 accept int16_t-sample initialization of AudioBuffer https://reviewboard.mozilla.org/r/169954/#review178052
Attachment #8898578 - Flags: review?(padenot) → review+
Comment on attachment 8898579 [details] bug 1391482 permit AudioChunk channel data initialization when mBuffer is not shared https://reviewboard.mozilla.org/r/169956/#review178054
Attachment #8898579 - Flags: review?(padenot) → review+
Comment on attachment 8898580 [details] bug 1391482 generalize WebAudioDecodeJob buffer as AudioChunk https://reviewboard.mozilla.org/r/169958/#review178056
Attachment #8898580 - Flags: review?(padenot) → review+
Comment on attachment 8898581 [details] bug 1391482 delay conversion of output of decodeAudioData() from int16_t to float until necessary https://reviewboard.mozilla.org/r/169960/#review178058
Attachment #8898581 - Flags: review?(padenot) → review+
Comment on attachment 8898570 [details] bug 1391482 add a fallible SharedBuffer::Create() https://reviewboard.mozilla.org/r/169938/#review178010 > I thought we had a style guide entry stating that `new` should be preferred in new code. I can't find it though. > > Here it does not matter too much because we're allocating bytes and not elements of a bigger size, so we can't forget the multiplication. I filed bug 1394341 on the existing mismatch between moz_xmalloc and delete. Using vector new char[] here would still mismatch with scalar delete SharedBuffer*. I'd prefer ::operator new() and ::operator delete() over new char[] and delete[] char*, because it is bytes not chars being allocated and I'd like to avoids reinterpret_cast from char*. However, I think it is easier to share code with malloc/moz_x_malloc than ::operator new(size_t) and ::operator new(size_t, const mozilla::fallible_t). malloc and free are also better understood by everybody. I'll drop this issue and follow up in bug 1394341.
Comment on attachment 8898572 [details] bug 1391482 use AudioChunk to store and pass PeriodicWave data to engine https://reviewboard.mozilla.org/r/169942/#review178022 > A bit weird to have an `AudioChunk` containing Fourier coefficients, but it makes the code cleaner indeed. Yes, I guess there is the possibility of wanting to pass non-audio data buffers through this mechanism, which would be a bit odd. I don't feel so bad about Fourier coefficients because they are frequency-domain audio with the same units and types as time domain audio. (AudioChunk has neither a fundamental frequency, nor a sample rate member.) I'll drop this issue because I don't think you expecting this to be addressed, and I don't have an better ideas.
Comment on attachment 8898574 [details] bug 1391482 accept int16_t sample buffers in AudioBufferSourceNode https://reviewboard.mozilla.org/r/169946/#review178030 > This will allocate if the resampling rate is less than 0.25 and the platform uses int16 for audio output, and the buffer is originally in float32. > > One one hand, this whole patch set would be amazing on desktop (and we don't have to change the spec to implement it), and this would mean we allocate a bit more in some case. > > On the other hand, solving the memory problem can also be done with explicit in-memory compression, and would be better. This is likely to require a spec change. > > The allocation can also be removed by compiling two copies of the resampler, one for int, one for float. Perhaps I'm missing your point here. Yes, there can be an allocation when the resampler works in int16. On such platforms, having an int16 source buffer removes that allocation. When the buffer is in float on int16 platforms, this allocation could be avoided by clamping inputLimit to WEBAUDIO_BLOCK_SIZE*4. I'll leave that to a separate bug, as that is no worse with these changes. If something similar were to be done on desktop, then that allocation could also happen on desktop, and so that should first be addressed. On desktop, int16 buffers should probably only be generated from encodings that are intended for int16 output, such as int16 wav. I'll drop this issue, because I don't think you are requesting anything in this bug, but please speak up if I misunderstand. Thanks for the reviews!
Yes, to be clear, everything here is r+ and can land, I was merely thinking out loud.
Pushed by ktomlinson@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/ca07d3a43b11 use AudioChunk to generalize AudioBuffer shared channel storage r=padenot https://hg.mozilla.org/integration/autoland/rev/fea3e5cd3590 add a mechanism to pass an AudioChunk from node to engine r=padenot https://hg.mozilla.org/integration/autoland/rev/a9c7abf92455 generalize shared channel data from AudioBuffer as AudioChunk r=padenot https://hg.mozilla.org/integration/autoland/rev/527ea972cdf3 add a fallible SharedBuffer::Create() r=padenot https://hg.mozilla.org/integration/autoland/rev/fc35c12c815f keep custom oscillator data buffer on graph thread only long enough to initialize the PeriodicWave r=padenot https://hg.mozilla.org/integration/autoland/rev/7ffc044e742a use AudioChunk to store and pass PeriodicWave data to engine r=padenot https://hg.mozilla.org/integration/autoland/rev/0e12a1bdb2fa remove now-unused ThreadSharedFloatArrayBufferList SetBuffer() variant r=padenot https://hg.mozilla.org/integration/autoland/rev/ee13302be6c8 accept int16_t sample buffers in AudioBufferSourceNode r=padenot https://hg.mozilla.org/integration/autoland/rev/6feba222e86b accept int16_t sample buffers in ConvolverNode r=padenot https://hg.mozilla.org/integration/autoland/rev/b9a522cc88c9 move AudioBuffer parameter checking to constructor r=padenot https://hg.mozilla.org/integration/autoland/rev/cb6ac4267562 add a method to create an AudioBuffer from AudioChunk data r=padenot https://hg.mozilla.org/integration/autoland/rev/497e04031fc3 accept int16_t-sample initialization of AudioBuffer r=padenot https://hg.mozilla.org/integration/autoland/rev/7f096b0d1d0e permit AudioChunk channel data initialization when mBuffer is not shared r=padenot https://hg.mozilla.org/integration/autoland/rev/c02da061fc56 generalize WebAudioDecodeJob buffer as AudioChunk r=padenot https://hg.mozilla.org/integration/autoland/rev/4d4ed9b64bdb delay conversion of output of decodeAudioData() from int16_t to float until necessary r=padenot
Flags: in-testsuite-
The rooting analysis does not analyze the values of function pointers, and so flags a hazard because it doesn't know that it is not a hazard. I'll rearrange SharedBuffer::Create() to work around this: Function '_ZN7mozilla3dom12nsSpeechTask9SendAudioEN2JS6HandleINS2_5ValueEEES5_P9JSContext$uint32 mozilla::dom::nsSpeechTask::SendAudio(class JS::Handle<JS::Value>, class JS::Handle<JS::Value>, JSContext*)' has unrooted 'nogc' of type 'JS::AutoCheckCannotGC' live across GC call '_ZN7mozilla3domL11makeSamplesEPsj$nsSpeechTask.cpp:RefPtr<mozilla::SharedBuffer> mozilla::dom::makeSamples(int16_t*, uint32_t)' at dom/media/webspeech/synth/nsSpeechTask.cpp:303 nsSpeechTask.cpp:296: Call(59,60, nogc.AutoCheckCannotGC(0)) nsSpeechTask.cpp:298: Call(60,61, __temp_24 := tsrc.operator 111()) nsSpeechTask.cpp:298: Call(61,62, data := JS_GetInt16ArrayData(__temp_24**,isShared,nogc)) nsSpeechTask.cpp:299: Assume(62,69, isShared*, false) nsSpeechTask.cpp:303: Call(69,70, __temp_25 := makeSamples(data*,dataLen*)) [[GC call]] nsSpeechTask.cpp:303: Call(70,71, samples.operator=(__temp_25)) nsSpeechTask.cpp:303: Call(71,72, __temp_25.~SharedBuffer>()) nsSpeechTask.cpp:303: Call(72,73, nogc.~AutoCheckCannotGC()) GC Function: _ZN7mozilla3domL11makeSamplesEPsj$nsSpeechTask.cpp:RefPtr<mozilla::SharedBuffer> mozilla::dom::makeSamples(int16_t*, uint32_t) static already_AddRefed<mozilla::SharedBuffer> mozilla::SharedBuffer::Create(size_t) static already_AddRefed<mozilla::SharedBuffer> mozilla::SharedBuffer::InternalCreate(void* (*)(size_t), size_t) IndirectCall: aMalloc
Comment on attachment 8898570 [details] bug 1391482 add a fallible SharedBuffer::Create() https://reviewboard.mozilla.org/r/169938/#review178010 > I filed bug 1394341 on the existing mismatch between moz_xmalloc and delete. > > Using vector new char[] here would still mismatch with scalar delete > SharedBuffer*. > > I'd prefer ::operator new() and ::operator delete() over new char[] and > delete[] char*, because it is bytes not chars being allocated and I'd like to > avoids reinterpret_cast from char*. > > However, I think it is easier to share code with malloc/moz_x_malloc than > ::operator new(size_t) and ::operator new(size_t, const mozilla::fallible_t). > > malloc and free are also better understood by everybody. > > I'll drop this issue and follow up in bug 1394341. The changes required to avoid the function pointer also make it easy to use different variants of operator new. Using the non-array operator new balances with the non-array operator delete called when the ref count drops to zero, making a class operator delete unnecessary. This is different enough that I'll request review again.
Attachment #8898570 - Flags: review+ → review?(padenot)
Blocks: 1394341
Attachment #8898570 - Flags: review?(padenot) → review+
Pushed by ktomlinson@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/f8fc4ff76ed9 use AudioChunk to generalize AudioBuffer shared channel storage r=padenot https://hg.mozilla.org/integration/autoland/rev/043d871b9b81 add a mechanism to pass an AudioChunk from node to engine r=padenot https://hg.mozilla.org/integration/autoland/rev/4192bfee266c generalize shared channel data from AudioBuffer as AudioChunk r=padenot https://hg.mozilla.org/integration/autoland/rev/d5cc91a56399 add a fallible SharedBuffer::Create() r=padenot https://hg.mozilla.org/integration/autoland/rev/529abb028f5a keep custom oscillator data buffer on graph thread only long enough to initialize the PeriodicWave r=padenot https://hg.mozilla.org/integration/autoland/rev/1967d3942792 use AudioChunk to store and pass PeriodicWave data to engine r=padenot https://hg.mozilla.org/integration/autoland/rev/927b04969910 remove now-unused ThreadSharedFloatArrayBufferList SetBuffer() variant r=padenot https://hg.mozilla.org/integration/autoland/rev/bea3d472cab9 accept int16_t sample buffers in AudioBufferSourceNode r=padenot https://hg.mozilla.org/integration/autoland/rev/ffef0925fba1 accept int16_t sample buffers in ConvolverNode r=padenot https://hg.mozilla.org/integration/autoland/rev/8654ca00ee9f move AudioBuffer parameter checking to constructor r=padenot https://hg.mozilla.org/integration/autoland/rev/b643a1847ffd add a method to create an AudioBuffer from AudioChunk data r=padenot https://hg.mozilla.org/integration/autoland/rev/c949d19c37d0 accept int16_t-sample initialization of AudioBuffer r=padenot https://hg.mozilla.org/integration/autoland/rev/480d761a45ae permit AudioChunk channel data initialization when mBuffer is not shared r=padenot https://hg.mozilla.org/integration/autoland/rev/76419265414e generalize WebAudioDecodeJob buffer as AudioChunk r=padenot https://hg.mozilla.org/integration/autoland/rev/a3aa8b09341f delay conversion of output of decodeAudioData() from int16_t to float until necessary r=padenot
Depends on: 1404220
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: