bug 1391482 use AudioChunk to generalize AudioBuffer shared channel storage r?padenot
Although the AudioChunk buffer is still always a
ThreadSharedFloatArrayBufferList, buffers with 16-bit data will be permitted
in a future patch.
MozReview-Commit-ID: FEGKMiQOCpR
--- a/dom/media/SharedBuffer.h
+++ b/dom/media/SharedBuffer.h
@@ -8,28 +8,33 @@
#include "mozilla/CheckedInt.h"
#include "mozilla/mozalloc.h"
#include "nsCOMPtr.h"
namespace mozilla {
class AudioBlockBuffer;
+class ThreadSharedFloatArrayBufferList;
/**
* Base class for objects with a thread-safe refcount and a virtual
* destructor.
*/
class ThreadSharedObject {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSharedObject)
bool IsShared() { return mRefCnt.get() > 1; }
virtual AudioBlockBuffer* AsAudioBlockBuffer() { return nullptr; };
+ virtual ThreadSharedFloatArrayBufferList* AsThreadSharedFloatArrayBufferList()
+ {
+ return nullptr;
+ };
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
return 0;
}
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
--- a/dom/media/webaudio/AudioBuffer.cpp
+++ b/dom/media/webaudio/AudioBuffer.cpp
@@ -154,26 +154,21 @@ AudioBufferMemoryTracker::CollectReports
"Memory used by AudioBuffer objects (Web Audio).");
return NS_OK;
}
AudioBuffer::AudioBuffer(nsPIDOMWindowInner* aWindow,
uint32_t aNumberOfChannels,
uint32_t aLength,
- float aSampleRate,
- already_AddRefed<ThreadSharedFloatArrayBufferList>
- aInitialContents)
+ float aSampleRate)
: mOwnerWindow(do_GetWeakReference(aWindow)),
- mSharedChannels(aInitialContents),
- mLength(aLength),
mSampleRate(aSampleRate)
{
- MOZ_ASSERT(!mSharedChannels ||
- mSharedChannels->GetChannels() == aNumberOfChannels);
+ mSharedChannels.mDuration = aLength;
mJSChannels.SetLength(aNumberOfChannels);
mozilla::HoldJSObjects(this);
AudioBufferMemoryTracker::RegisterAudioBuffer(this);
}
AudioBuffer::~AudioBuffer()
{
AudioBufferMemoryTracker::UnregisterAudioBuffer(this);
@@ -199,37 +194,58 @@ AudioBuffer::Constructor(const GlobalObj
}
void
AudioBuffer::ClearJSChannels()
{
mJSChannels.Clear();
}
+void
+AudioBuffer::SetSharedChannels(
+ already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
+{
+ RefPtr<ThreadSharedFloatArrayBufferList> buffer = aBuffer;
+ uint32_t channelCount = buffer->GetChannels();
+ mSharedChannels.mChannelData.SetLength(channelCount);
+ for (uint32_t i = 0; i < channelCount; ++i) {
+ mSharedChannels.mChannelData[i] = buffer->GetData(i);
+ }
+ mSharedChannels.mBuffer = buffer.forget();
+ mSharedChannels.mVolume = 1.0f;
+ mSharedChannels.mBufferFormat = AUDIO_FORMAT_FLOAT32;
+}
+
/* static */ already_AddRefed<AudioBuffer>
AudioBuffer::Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
uint32_t aLength, float aSampleRate,
already_AddRefed<ThreadSharedFloatArrayBufferList>
aInitialContents,
ErrorResult& aRv)
{
+ RefPtr<ThreadSharedFloatArrayBufferList> initialContents = aInitialContents;
+
// Note that a buffer with zero channels is permitted here for the sake of
// AudioProcessingEvent, where channel counts must match parameters passed
// to createScriptProcessor(), one of which may be zero.
if (aSampleRate < WebAudioUtils::MinSampleRate ||
aSampleRate > WebAudioUtils::MaxSampleRate ||
aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
!aLength || aLength > INT32_MAX) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return nullptr;
}
RefPtr<AudioBuffer> buffer =
- new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate,
- Move(aInitialContents));
+ new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate);
+
+ if (initialContents) {
+ MOZ_ASSERT(initialContents->GetChannels() == aNumberOfChannels);
+ buffer->SetSharedChannels(initialContents.forget());
+ }
return buffer.forget();
}
JSObject*
AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return AudioBufferBinding::Wrap(aCx, this, aGivenProto);
@@ -243,69 +259,73 @@ AudioBuffer::RestoreJSChannelData(JSCont
// Already have data in JS array.
continue;
}
// The following code first zeroes the array and then copies our data
// into it. We could avoid this with additional JS APIs to construct
// an array (or ArrayBuffer) containing initial data.
JS::Rooted<JSObject*> array(aJSContext,
- JS_NewFloat32Array(aJSContext, mLength));
+ JS_NewFloat32Array(aJSContext, Length()));
if (!array) {
return false;
}
- if (mSharedChannels) {
+ if (!mSharedChannels.IsNull()) {
// "4. Attach ArrayBuffers containing copies of the data to the
// AudioBuffer, to be returned by the next call to getChannelData."
- const float* data = mSharedChannels->GetData(i);
+ MOZ_ASSERT(mSharedChannels.mVolume == 1.0f);
+ const float* data = mSharedChannels.ChannelData<float>()[i];
JS::AutoCheckCannotGC nogc;
bool isShared;
- mozilla::PodCopy(JS_GetFloat32ArrayData(array, &isShared, nogc), data, mLength);
+ mozilla::PodCopy(JS_GetFloat32ArrayData(array, &isShared, nogc),
+ data, Length());
MOZ_ASSERT(!isShared); // Was created as unshared above
}
mJSChannels[i] = array;
}
- mSharedChannels = nullptr;
+ mSharedChannels.mBuffer = nullptr;
+ mSharedChannels.mChannelData.Clear();
return true;
}
void
AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
uint32_t aStartInChannel, ErrorResult& aRv)
{
aDestination.ComputeLengthAndData();
uint32_t length = aDestination.Length();
CheckedInt<uint32_t> end = aStartInChannel;
end += length;
if (aChannelNumber >= NumberOfChannels() ||
- !end.isValid() || end.value() > mLength) {
+ !end.isValid() || end.value() > Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
JS::AutoCheckCannotGC nogc;
JSObject* channelArray = mJSChannels[aChannelNumber];
const float* sourceData = nullptr;
if (channelArray) {
- if (JS_GetTypedArrayLength(channelArray) != mLength) {
+ if (JS_GetTypedArrayLength(channelArray) != Length()) {
// The array's buffer was detached.
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
bool isShared = false;
sourceData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
// The sourceData arrays should all have originated in
// RestoreJSChannelData, where they are created unshared.
MOZ_ASSERT(!isShared);
- } else if (mSharedChannels) {
- sourceData = mSharedChannels->GetData(aChannelNumber);
+ } else if (!mSharedChannels.IsNull()) {
+ MOZ_ASSERT(mSharedChannels.mVolume == 1.0f);
+ sourceData = mSharedChannels.ChannelData<float>()[aChannelNumber];
}
if (sourceData) {
PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
} else {
PodZero(aDestination.Data(), length);
}
}
@@ -316,29 +336,29 @@ AudioBuffer::CopyToChannel(JSContext* aJ
ErrorResult& aRv)
{
aSource.ComputeLengthAndData();
uint32_t length = aSource.Length();
CheckedInt<uint32_t> end = aStartInChannel;
end += length;
if (aChannelNumber >= NumberOfChannels() ||
- !end.isValid() || end.value() > mLength) {
+ !end.isValid() || end.value() > Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
if (!RestoreJSChannelData(aJSContext)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
JS::AutoCheckCannotGC nogc;
JSObject* channelArray = mJSChannels[aChannelNumber];
- if (JS_GetTypedArrayLength(channelArray) != mLength) {
+ if (JS_GetTypedArrayLength(channelArray) != Length()) {
// The array's buffer was detached.
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
bool isShared = false;
float* channelData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
// The channelData arrays should all have originated in
@@ -368,17 +388,17 @@ AudioBuffer::GetChannelData(JSContext* a
already_AddRefed<ThreadSharedFloatArrayBufferList>
AudioBuffer::StealJSArrayDataIntoSharedChannels(JSContext* aJSContext)
{
// "1. If any of the AudioBuffer's ArrayBuffer have been detached, abort
// these steps, and return a zero-length channel data buffers to the
// invoker."
for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
JSObject* channelArray = mJSChannels[i];
- if (!channelArray || mLength != JS_GetTypedArrayLength(channelArray)) {
+ if (!channelArray || Length() != JS_GetTypedArrayLength(channelArray)) {
// Either empty buffer or one of the arrays' buffers was detached.
return nullptr;
}
}
// "2. Detach all ArrayBuffers for arrays previously returned by
// getChannelData on this AudioBuffer."
// "3. Retain the underlying data buffers from those ArrayBuffers and return
@@ -412,28 +432,32 @@ AudioBuffer::StealJSArrayDataIntoSharedC
}
return result.forget();
}
ThreadSharedFloatArrayBufferList*
AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
{
- if (!mSharedChannels) {
- mSharedChannels = StealJSArrayDataIntoSharedChannels(aJSContext);
+ if (mSharedChannels.IsNull()) {
+ // mDuration is set in constructor
+ RefPtr<ThreadSharedFloatArrayBufferList> buffer =
+ StealJSArrayDataIntoSharedChannels(aJSContext);
+
+ if (buffer) {
+ SetSharedChannels(buffer.forget());
+ }
}
- return mSharedChannels;
+ return mSharedChannels.mBuffer->AsThreadSharedFloatArrayBufferList();
}
size_t
AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = aMallocSizeOf(this);
amount += mJSChannels.ShallowSizeOfExcludingThis(aMallocSizeOf);
- if (mSharedChannels) {
- amount += mSharedChannels->SizeOfIncludingThis(aMallocSizeOf);
- }
+ amount += mSharedChannels.SizeOfExcludingThis(aMallocSizeOf, false);
return amount;
}
} // namespace dom
} // namespace mozilla
--- a/dom/media/webaudio/AudioBuffer.h
+++ b/dom/media/webaudio/AudioBuffer.h
@@ -2,16 +2,17 @@
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef AudioBuffer_h_
#define AudioBuffer_h_
+#include "AudioSegment.h"
#include "nsWrapperCache.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/StaticMutex.h"
#include "nsTArray.h"
#include "js/TypeDecls.h"
#include "mozilla/MemoryReporting.h"
@@ -72,22 +73,22 @@ public:
float SampleRate() const
{
return mSampleRate;
}
uint32_t Length() const
{
- return mLength;
+ return mSharedChannels.mDuration;
}
double Duration() const
{
- return mLength / static_cast<double> (mSampleRate);
+ return Length() / static_cast<double> (mSampleRate);
}
uint32_t NumberOfChannels() const
{
return mJSChannels.Length();
}
/**
@@ -107,36 +108,37 @@ public:
/**
* Returns a ThreadSharedFloatArrayBufferList containing the sample data.
* Can return null if there is no data.
*/
ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext);
protected:
AudioBuffer(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
- uint32_t aLength, float aSampleRate,
- already_AddRefed<ThreadSharedFloatArrayBufferList>
- aInitialContents);
+ uint32_t aLength, float aSampleRate);
~AudioBuffer();
+ void
+ SetSharedChannels(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer);
+
bool RestoreJSChannelData(JSContext* aJSContext);
already_AddRefed<ThreadSharedFloatArrayBufferList>
StealJSArrayDataIntoSharedChannels(JSContext* aJSContext);
void ClearJSChannels();
- nsWeakPtr mOwnerWindow;
// Float32Arrays
AutoTArray<JS::Heap<JSObject*>, 2> mJSChannels;
+ // mSharedChannels aggregates the data from mJSChannels. This is non-null
+ // if and only if the mJSChannels' buffers are detached, but its mDuration
+ // member keeps the buffer length regardless of whether the buffer is
+ // provided by mJSChannels or mSharedChannels.
+ AudioChunk mSharedChannels;
- // mSharedChannels aggregates the data from mJSChannels. This is non-null
- // if and only if the mJSChannels' buffers are detached.
- RefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels;
-
- uint32_t mLength;
+ nsWeakPtr mOwnerWindow;
float mSampleRate;
};
} // namespace dom
} // namespace mozilla
#endif
--- a/dom/media/webaudio/AudioNodeEngine.h
+++ b/dom/media/webaudio/AudioNodeEngine.h
@@ -43,16 +43,22 @@ public:
/**
* Create with buffers suitable for transfer to
* JS_NewArrayBufferWithContents(). The buffer contents are uninitialized
* and so should be set using GetDataForWrite().
*/
static already_AddRefed<ThreadSharedFloatArrayBufferList>
Create(uint32_t aChannelCount, size_t aLength, const mozilla::fallible_t&);
+ ThreadSharedFloatArrayBufferList*
+ AsThreadSharedFloatArrayBufferList() override
+ {
+ return this;
+ };
+
struct Storage final
{
Storage() :
mDataToFree(nullptr),
mFree(nullptr),
mSampleData(nullptr)
{}
~Storage() {