content/media/webaudio/WaveShaperNode.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial