content/media/webaudio/AudioBuffer.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:e8f464d28b41
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "AudioBuffer.h"
8 #include "mozilla/dom/AudioBufferBinding.h"
9 #include "jsfriendapi.h"
10 #include "mozilla/ErrorResult.h"
11 #include "AudioSegment.h"
12 #include "AudioChannelFormat.h"
13 #include "mozilla/PodOperations.h"
14 #include "mozilla/CheckedInt.h"
15 #include "AudioNodeEngine.h"
16
17 namespace mozilla {
18 namespace dom {
19
20 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBuffer)
21
22 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBuffer)
23 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
24 NS_IMPL_CYCLE_COLLECTION_UNLINK(mJSChannels)
25 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
26 tmp->ClearJSChannels();
27 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
28
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AudioBuffer)
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
33
34 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(AudioBuffer)
35 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
36 for (uint32_t i = 0; i < tmp->mJSChannels.Length(); ++i) {
37 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mJSChannels[i])
38 }
39 NS_IMPL_CYCLE_COLLECTION_TRACE_END
40
41 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AudioBuffer, AddRef)
42 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AudioBuffer, Release)
43
44 AudioBuffer::AudioBuffer(AudioContext* aContext, uint32_t aNumberOfChannels,
45 uint32_t aLength, float aSampleRate)
46 : mContext(aContext),
47 mLength(aLength),
48 mSampleRate(aSampleRate)
49 {
50 mJSChannels.SetCapacity(aNumberOfChannels);
51 SetIsDOMBinding();
52 mozilla::HoldJSObjects(this);
53 }
54
55 AudioBuffer::~AudioBuffer()
56 {
57 ClearJSChannels();
58 }
59
60 void
61 AudioBuffer::ClearJSChannels()
62 {
63 mJSChannels.Clear();
64 mozilla::DropJSObjects(this);
65 }
66
67 /* static */ already_AddRefed<AudioBuffer>
68 AudioBuffer::Create(AudioContext* aContext, uint32_t aNumberOfChannels,
69 uint32_t aLength, float aSampleRate,
70 JSContext* aJSContext, ErrorResult& aRv)
71 {
72 // Note that a buffer with zero channels is permitted here for the sake of
73 // AudioProcessingEvent, where channel counts must match parameters passed
74 // to createScriptProcessor(), one of which may be zero.
75 if (aSampleRate < WebAudioUtils::MinSampleRate ||
76 aSampleRate > WebAudioUtils::MaxSampleRate ||
77 aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
78 !aLength || aLength > INT32_MAX) {
79 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
80 return nullptr;
81 }
82
83 nsRefPtr<AudioBuffer> buffer =
84 new AudioBuffer(aContext, aNumberOfChannels, aLength, aSampleRate);
85
86 for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
87 JS::Rooted<JSObject*> array(aJSContext,
88 JS_NewFloat32Array(aJSContext, aLength));
89 if (!array) {
90 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
91 return nullptr;
92 }
93 buffer->mJSChannels.AppendElement(array.get());
94 }
95
96 return buffer.forget();
97 }
98
99 JSObject*
100 AudioBuffer::WrapObject(JSContext* aCx)
101 {
102 return AudioBufferBinding::Wrap(aCx, this);
103 }
104
105 bool
106 AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
107 {
108 if (mSharedChannels) {
109 for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
110 const float* data = mSharedChannels->GetData(i);
111 // The following code first zeroes the array and then copies our data
112 // into it. We could avoid this with additional JS APIs to construct
113 // an array (or ArrayBuffer) containing initial data.
114 JS::Rooted<JSObject*> array(aJSContext,
115 JS_NewFloat32Array(aJSContext, mLength));
116 if (!array) {
117 return false;
118 }
119 memcpy(JS_GetFloat32ArrayData(array), data, sizeof(float)*mLength);
120 mJSChannels[i] = array;
121 }
122
123 mSharedChannels = nullptr;
124 }
125
126 return true;
127 }
128
129 void
130 AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
131 uint32_t aStartInChannel, ErrorResult& aRv)
132 {
133 aDestination.ComputeLengthAndData();
134
135 uint32_t length = aDestination.Length();
136 CheckedInt<uint32_t> end = aStartInChannel;
137 end += length;
138 if (aChannelNumber >= NumberOfChannels() ||
139 !end.isValid() || end.value() > mLength) {
140 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
141 return;
142 }
143
144 if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
145 // The array was probably neutered
146 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
147 return;
148 }
149
150 const float* sourceData = mSharedChannels ?
151 mSharedChannels->GetData(aChannelNumber) :
152 JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]);
153 PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
154 }
155
156 void
157 AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
158 uint32_t aChannelNumber, uint32_t aStartInChannel,
159 ErrorResult& aRv)
160 {
161 aSource.ComputeLengthAndData();
162
163 uint32_t length = aSource.Length();
164 CheckedInt<uint32_t> end = aStartInChannel;
165 end += length;
166 if (aChannelNumber >= NumberOfChannels() ||
167 !end.isValid() || end.value() > mLength) {
168 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
169 return;
170 }
171
172 if (!mSharedChannels && JS_GetTypedArrayLength(mJSChannels[aChannelNumber]) != mLength) {
173 // The array was probably neutered
174 aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
175 return;
176 }
177
178 if (!RestoreJSChannelData(aJSContext)) {
179 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
180 return;
181 }
182
183 PodMove(JS_GetFloat32ArrayData(mJSChannels[aChannelNumber]) + aStartInChannel,
184 aSource.Data(), length);
185 }
186
187 void
188 AudioBuffer::SetRawChannelContents(JSContext* aJSContext, uint32_t aChannel,
189 float* aContents)
190 {
191 PodCopy(JS_GetFloat32ArrayData(mJSChannels[aChannel]), aContents, mLength);
192 }
193
194 void
195 AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
196 JS::MutableHandle<JSObject*> aRetval,
197 ErrorResult& aRv)
198 {
199 if (aChannel >= NumberOfChannels()) {
200 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
201 return;
202 }
203
204 if (!RestoreJSChannelData(aJSContext)) {
205 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
206 return;
207 }
208
209 if (mJSChannels[aChannel]) {
210 JS::ExposeObjectToActiveJS(mJSChannels[aChannel]);
211 }
212 aRetval.set(mJSChannels[aChannel]);
213 }
214
215 static already_AddRefed<ThreadSharedFloatArrayBufferList>
216 StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
217 const nsTArray<JSObject*>& aJSArrays)
218 {
219 nsRefPtr<ThreadSharedFloatArrayBufferList> result =
220 new ThreadSharedFloatArrayBufferList(aJSArrays.Length());
221 for (uint32_t i = 0; i < aJSArrays.Length(); ++i) {
222 JS::Rooted<JSObject*> arrayBuffer(aJSContext,
223 JS_GetArrayBufferViewBuffer(aJSContext, aJSArrays[i]));
224 uint8_t* stolenData = arrayBuffer
225 ? (uint8_t*) JS_StealArrayBufferContents(aJSContext, arrayBuffer)
226 : nullptr;
227 if (stolenData) {
228 result->SetData(i, stolenData, reinterpret_cast<float*>(stolenData));
229 } else {
230 return nullptr;
231 }
232 }
233 return result.forget();
234 }
235
236 ThreadSharedFloatArrayBufferList*
237 AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
238 {
239 if (!mSharedChannels) {
240 for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
241 if (mLength != JS_GetTypedArrayLength(mJSChannels[i])) {
242 // Probably one of the arrays was neutered
243 return nullptr;
244 }
245 }
246
247 mSharedChannels =
248 StealJSArrayDataIntoThreadSharedFloatArrayBufferList(aJSContext, mJSChannels);
249 }
250
251 return mSharedChannels;
252 }
253
254 size_t
255 AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
256 {
257 size_t amount = aMallocSizeOf(this);
258 amount += mJSChannels.SizeOfExcludingThis(aMallocSizeOf);
259 if (mSharedChannels) {
260 amount += mSharedChannels->SizeOfExcludingThis(aMallocSizeOf);
261 }
262 return amount;
263 }
264
265 }
266 }

mercurial