Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
7 #include "WaveShaperNode.h"
8 #include "mozilla/dom/WaveShaperNodeBinding.h"
9 #include "AudioNode.h"
10 #include "AudioNodeEngine.h"
11 #include "AudioNodeStream.h"
12 #include "mozilla/PodOperations.h"
13 #include "speex/speex_resampler.h"
15 namespace mozilla {
16 namespace dom {
18 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
20 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
21 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
22 tmp->ClearCurve();
23 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode)
26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
27 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
29 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode)
30 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
31 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCurve)
32 NS_IMPL_CYCLE_COLLECTION_TRACE_END
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WaveShaperNode)
35 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
37 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode)
38 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode)
40 static uint32_t ValueOf(OverSampleType aType)
41 {
42 switch (aType) {
43 case OverSampleType::None: return 1;
44 case OverSampleType::_2x: return 2;
45 case OverSampleType::_4x: return 4;
46 default:
47 NS_NOTREACHED("We should never reach here");
48 return 1;
49 }
50 }
52 class Resampler
53 {
54 public:
55 Resampler()
56 : mType(OverSampleType::None)
57 , mUpSampler(nullptr)
58 , mDownSampler(nullptr)
59 , mChannels(0)
60 , mSampleRate(0)
61 {
62 }
64 ~Resampler()
65 {
66 Destroy();
67 }
69 void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType)
70 {
71 if (aChannels == mChannels &&
72 aSampleRate == mSampleRate &&
73 aType == mType) {
74 return;
75 }
77 mChannels = aChannels;
78 mSampleRate = aSampleRate;
79 mType = aType;
81 Destroy();
83 if (aType == OverSampleType::None) {
84 mBuffer.Clear();
85 return;
86 }
88 mUpSampler = speex_resampler_init(aChannels,
89 aSampleRate,
90 aSampleRate * ValueOf(aType),
91 SPEEX_RESAMPLER_QUALITY_DEFAULT,
92 nullptr);
93 mDownSampler = speex_resampler_init(aChannels,
94 aSampleRate * ValueOf(aType),
95 aSampleRate,
96 SPEEX_RESAMPLER_QUALITY_DEFAULT,
97 nullptr);
98 mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE*ValueOf(aType));
99 }
101 float* UpSample(uint32_t aChannel, const float* aInputData, uint32_t aBlocks)
102 {
103 uint32_t inSamples = WEBAUDIO_BLOCK_SIZE;
104 uint32_t outSamples = WEBAUDIO_BLOCK_SIZE*aBlocks;
105 float* outputData = mBuffer.Elements();
107 MOZ_ASSERT(mBuffer.Length() == outSamples);
109 WebAudioUtils::SpeexResamplerProcess(mUpSampler, aChannel,
110 aInputData, &inSamples,
111 outputData, &outSamples);
113 MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE && outSamples == WEBAUDIO_BLOCK_SIZE*aBlocks);
115 return outputData;
116 }
118 void DownSample(uint32_t aChannel, float* aOutputData, uint32_t aBlocks)
119 {
120 uint32_t inSamples = WEBAUDIO_BLOCK_SIZE*aBlocks;
121 uint32_t outSamples = WEBAUDIO_BLOCK_SIZE;
122 const float* inputData = mBuffer.Elements();
124 MOZ_ASSERT(mBuffer.Length() == inSamples);
126 WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel,
127 inputData, &inSamples,
128 aOutputData, &outSamples);
130 MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE*aBlocks && outSamples == WEBAUDIO_BLOCK_SIZE);
131 }
133 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
134 {
135 size_t amount = 0;
136 // Future: properly measure speex memory
137 amount += aMallocSizeOf(mUpSampler);
138 amount += aMallocSizeOf(mDownSampler);
139 amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
140 return amount;
141 }
143 private:
144 void Destroy()
145 {
146 if (mUpSampler) {
147 speex_resampler_destroy(mUpSampler);
148 mUpSampler = nullptr;
149 }
150 if (mDownSampler) {
151 speex_resampler_destroy(mDownSampler);
152 mDownSampler = nullptr;
153 }
154 }
156 private:
157 OverSampleType mType;
158 SpeexResamplerState* mUpSampler;
159 SpeexResamplerState* mDownSampler;
160 uint32_t mChannels;
161 TrackRate mSampleRate;
162 nsTArray<float> mBuffer;
163 };
165 class WaveShaperNodeEngine : public AudioNodeEngine
166 {
167 public:
168 explicit WaveShaperNodeEngine(AudioNode* aNode)
169 : AudioNodeEngine(aNode)
170 , mType(OverSampleType::None)
171 {
172 }
174 enum Parameteres {
175 TYPE
176 };
178 virtual void SetRawArrayData(nsTArray<float>& aCurve) MOZ_OVERRIDE
179 {
180 mCurve.SwapElements(aCurve);
181 }
183 virtual void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_OVERRIDE
184 {
185 switch (aIndex) {
186 case TYPE:
187 mType = static_cast<OverSampleType>(aValue);
188 break;
189 default:
190 NS_ERROR("Bad WaveShaperNode Int32Parameter");
191 }
192 }
194 template <uint32_t blocks>
195 void ProcessCurve(const float* aInputBuffer, float* aOutputBuffer)
196 {
197 for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE*blocks; ++j) {
198 // Index into the curve array based on the amplitude of the
199 // incoming signal by clamping the amplitude to [-1, 1] and
200 // performing a linear interpolation of the neighbor values.
201 float index = std::max(0.0f, std::min(float(mCurve.Length() - 1),
202 mCurve.Length() * (aInputBuffer[j] + 1) / 2));
203 uint32_t indexLower = uint32_t(index);
204 uint32_t indexHigher = uint32_t(index + 1.0f);
205 if (indexHigher == mCurve.Length()) {
206 aOutputBuffer[j] = mCurve[indexLower];
207 } else {
208 float interpolationFactor = index - indexLower;
209 aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] +
210 interpolationFactor * mCurve[indexHigher];
211 }
212 }
213 }
215 virtual void ProcessBlock(AudioNodeStream* aStream,
216 const AudioChunk& aInput,
217 AudioChunk* aOutput,
218 bool* aFinished)
219 {
220 uint32_t channelCount = aInput.mChannelData.Length();
221 if (!mCurve.Length() || !channelCount) {
222 // Optimize the case where we don't have a curve buffer,
223 // or the input is null.
224 *aOutput = aInput;
225 return;
226 }
228 AllocateAudioBlock(channelCount, aOutput);
229 for (uint32_t i = 0; i < channelCount; ++i) {
230 const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]);
231 float* outputBuffer = const_cast<float*> (static_cast<const float*>(aOutput->mChannelData[i]));
232 float* sampleBuffer;
234 switch (mType) {
235 case OverSampleType::None:
236 mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::None);
237 ProcessCurve<1>(inputBuffer, outputBuffer);
238 break;
239 case OverSampleType::_2x:
240 mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_2x);
241 sampleBuffer = mResampler.UpSample(i, inputBuffer, 2);
242 ProcessCurve<2>(sampleBuffer, sampleBuffer);
243 mResampler.DownSample(i, outputBuffer, 2);
244 break;
245 case OverSampleType::_4x:
246 mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_4x);
247 sampleBuffer = mResampler.UpSample(i, inputBuffer, 4);
248 ProcessCurve<4>(sampleBuffer, sampleBuffer);
249 mResampler.DownSample(i, outputBuffer, 4);
250 break;
251 default:
252 NS_NOTREACHED("We should never reach here");
253 }
254 }
255 }
257 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
258 {
259 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
260 amount += mCurve.SizeOfExcludingThis(aMallocSizeOf);
261 amount += mResampler.SizeOfExcludingThis(aMallocSizeOf);
262 return amount;
263 }
265 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
266 {
267 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
268 }
270 private:
271 nsTArray<float> mCurve;
272 OverSampleType mType;
273 Resampler mResampler;
274 };
276 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
277 : AudioNode(aContext,
278 2,
279 ChannelCountMode::Max,
280 ChannelInterpretation::Speakers)
281 , mCurve(nullptr)
282 , mType(OverSampleType::None)
283 {
284 mozilla::HoldJSObjects(this);
286 WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
287 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
288 }
290 WaveShaperNode::~WaveShaperNode()
291 {
292 ClearCurve();
293 }
295 void
296 WaveShaperNode::ClearCurve()
297 {
298 mCurve = nullptr;
299 mozilla::DropJSObjects(this);
300 }
302 JSObject*
303 WaveShaperNode::WrapObject(JSContext *aCx)
304 {
305 return WaveShaperNodeBinding::Wrap(aCx, this);
306 }
308 void
309 WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve)
310 {
311 nsTArray<float> curve;
312 if (!aCurve.IsNull()) {
313 const Float32Array& floats = aCurve.Value();
315 mCurve = floats.Obj();
317 floats.ComputeLengthAndData();
319 curve.SetLength(floats.Length());
320 PodCopy(curve.Elements(), floats.Data(), floats.Length());
321 } else {
322 mCurve = nullptr;
323 }
325 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
326 MOZ_ASSERT(ns, "Why don't we have a stream here?");
327 ns->SetRawArrayData(curve);
328 }
330 void
331 WaveShaperNode::SetOversample(OverSampleType aType)
332 {
333 mType = aType;
334 SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType));
335 }
337 }
338 }