WaveShaperNode.cpp (12319B)
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 "WaveShaperNode.h" 8 9 #include "AlignmentUtils.h" 10 #include "AudioNode.h" 11 #include "AudioNodeEngine.h" 12 #include "AudioNodeTrack.h" 13 #include "Tracing.h" 14 #include "mozilla/PodOperations.h" 15 #include "mozilla/dom/WaveShaperNodeBinding.h" 16 17 namespace mozilla::dom { 18 19 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode) 20 21 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode) 22 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 23 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 24 25 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode) 26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 27 28 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode) 29 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 30 NS_IMPL_CYCLE_COLLECTION_TRACE_END 31 32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WaveShaperNode) 33 NS_INTERFACE_MAP_END_INHERITING(AudioNode) 34 35 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode) 36 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode) 37 38 static uint32_t ValueOf(OverSampleType aType) { 39 switch (aType) { 40 case OverSampleType::None: 41 return 1; 42 case OverSampleType::_2x: 43 return 2; 44 case OverSampleType::_4x: 45 return 4; 46 default: 47 MOZ_ASSERT_UNREACHABLE("We should never reach here"); 48 return 1; 49 } 50 } 51 52 class Resampler final { 53 public: 54 Resampler() 55 : mType(OverSampleType::None), 56 mUpSampler(nullptr), 57 mDownSampler(nullptr), 58 mChannels(0), 59 mSampleRate(0) {} 60 61 ~Resampler() { Destroy(); } 62 63 void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType) { 64 if (aChannels == mChannels && aSampleRate == mSampleRate && 65 aType == mType) { 66 return; 67 } 68 69 mChannels = aChannels; 70 mSampleRate = aSampleRate; 71 mType = aType; 72 73 Destroy(); 74 75 if (aType == OverSampleType::None) { 76 mBuffer.Clear(); 77 return; 78 } 79 80 mUpSampler = speex_resampler_init(aChannels, aSampleRate, 81 aSampleRate * ValueOf(aType), 82 SPEEX_RESAMPLER_QUALITY_MIN, nullptr); 83 mDownSampler = 84 speex_resampler_init(aChannels, aSampleRate * ValueOf(aType), 85 aSampleRate, SPEEX_RESAMPLER_QUALITY_MIN, nullptr); 86 mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE * ValueOf(aType)); 87 } 88 89 float* UpSample(uint32_t aChannel, const float* aInputData, 90 uint32_t aBlocks) { 91 uint32_t inSamples = WEBAUDIO_BLOCK_SIZE; 92 uint32_t outSamples = WEBAUDIO_BLOCK_SIZE * aBlocks; 93 float* outputData = mBuffer.Elements(); 94 95 MOZ_ASSERT(mBuffer.Length() == outSamples); 96 97 WebAudioUtils::SpeexResamplerProcess(mUpSampler, aChannel, aInputData, 98 &inSamples, outputData, &outSamples); 99 100 MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE && 101 outSamples == WEBAUDIO_BLOCK_SIZE * aBlocks); 102 103 return outputData; 104 } 105 106 void DownSample(uint32_t aChannel, float* aOutputData, uint32_t aBlocks) { 107 uint32_t inSamples = WEBAUDIO_BLOCK_SIZE * aBlocks; 108 uint32_t outSamples = WEBAUDIO_BLOCK_SIZE; 109 const float* inputData = mBuffer.Elements(); 110 111 MOZ_ASSERT(mBuffer.Length() == inSamples); 112 113 WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel, inputData, 114 &inSamples, aOutputData, &outSamples); 115 116 MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE * aBlocks && 117 outSamples == WEBAUDIO_BLOCK_SIZE); 118 } 119 120 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { 121 size_t amount = 0; 122 // Future: properly measure speex memory 123 amount += aMallocSizeOf(mUpSampler); 124 amount += aMallocSizeOf(mDownSampler); 125 amount += mBuffer.ShallowSizeOfExcludingThis(aMallocSizeOf); 126 return amount; 127 } 128 129 private: 130 void Destroy() { 131 if (mUpSampler) { 132 speex_resampler_destroy(mUpSampler); 133 mUpSampler = nullptr; 134 } 135 if (mDownSampler) { 136 speex_resampler_destroy(mDownSampler); 137 mDownSampler = nullptr; 138 } 139 } 140 141 private: 142 OverSampleType mType; 143 SpeexResamplerState* mUpSampler; 144 SpeexResamplerState* mDownSampler; 145 uint32_t mChannels; 146 TrackRate mSampleRate; 147 nsTArray<float> mBuffer; 148 }; 149 150 class WaveShaperNodeEngine final : public AudioNodeEngine { 151 public: 152 explicit WaveShaperNodeEngine(AudioNode* aNode) 153 : AudioNodeEngine(aNode), mType(OverSampleType::None) {} 154 155 enum Parameters { TYPE }; 156 157 void SetRawArrayData(nsTArray<float>&& aCurve) override { 158 mCurve = std::move(aCurve); 159 } 160 161 void SetInt32Parameter(uint32_t aIndex, int32_t aValue) override { 162 switch (aIndex) { 163 case TYPE: 164 mType = static_cast<OverSampleType>(aValue); 165 break; 166 default: 167 NS_ERROR("Bad WaveShaperNode Int32Parameter"); 168 } 169 } 170 171 template <uint32_t blocks> 172 void ProcessCurve(const float* aInputBuffer, float* aOutputBuffer) { 173 for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE * blocks; ++j) { 174 // Index into the curve array based on the amplitude of the 175 // incoming signal by using an amplitude range of [-1, 1] and 176 // performing a linear interpolation of the neighbor values. 177 float index = (mCurve.Length() - 1) * (aInputBuffer[j] + 1.0f) / 2.0f; 178 if (index < 0.0f) { 179 aOutputBuffer[j] = mCurve[0]; 180 } else { 181 int32_t indexLower = index; 182 if (static_cast<uint32_t>(indexLower) >= mCurve.Length() - 1) { 183 aOutputBuffer[j] = mCurve[mCurve.Length() - 1]; 184 } else { 185 uint32_t indexHigher = indexLower + 1; 186 float interpolationFactor = index - indexLower; 187 aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] + 188 interpolationFactor * mCurve[indexHigher]; 189 } 190 } 191 } 192 } 193 194 void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom, 195 const AudioBlock& aInput, AudioBlock* aOutput, 196 bool* aFinished) override { 197 TRACE("WaveShaperNodeEngine::ProcessBlock"); 198 199 uint32_t channelCount = aInput.ChannelCount(); 200 if (!mCurve.Length()) { 201 // Optimize the case where we don't have a curve buffer 202 *aOutput = aInput; 203 return; 204 } 205 206 // If the input is null, check to see if non-null output will be produced 207 bool nullInput = false; 208 if (channelCount == 0) { 209 float index = (mCurve.Length() - 1) * 0.5; 210 uint32_t indexLower = index; 211 uint32_t indexHigher = indexLower + 1; 212 float interpolationFactor = index - indexLower; 213 if ((1.0f - interpolationFactor) * mCurve[indexLower] + 214 interpolationFactor * mCurve[indexHigher] == 215 0.0) { 216 *aOutput = aInput; 217 return; 218 } 219 nullInput = true; 220 channelCount = 1; 221 } 222 223 aOutput->AllocateChannels(channelCount); 224 for (uint32_t i = 0; i < channelCount; ++i) { 225 const float* inputSamples; 226 float scaledInput[WEBAUDIO_BLOCK_SIZE + 4]; 227 float* alignedScaledInput = ALIGNED16(scaledInput); 228 ASSERT_ALIGNED16(alignedScaledInput); 229 if (!nullInput) { 230 if (aInput.mVolume != 1.0f) { 231 AudioBlockCopyChannelWithScale( 232 static_cast<const float*>(aInput.mChannelData[i]), aInput.mVolume, 233 alignedScaledInput); 234 inputSamples = alignedScaledInput; 235 } else { 236 inputSamples = static_cast<const float*>(aInput.mChannelData[i]); 237 } 238 } else { 239 PodZero(alignedScaledInput, WEBAUDIO_BLOCK_SIZE); 240 inputSamples = alignedScaledInput; 241 } 242 float* outputBuffer = aOutput->ChannelFloatsForWrite(i); 243 float* sampleBuffer; 244 245 switch (mType) { 246 case OverSampleType::None: 247 mResampler.Reset(channelCount, aTrack->mSampleRate, 248 OverSampleType::None); 249 ProcessCurve<1>(inputSamples, outputBuffer); 250 break; 251 case OverSampleType::_2x: 252 mResampler.Reset(channelCount, aTrack->mSampleRate, 253 OverSampleType::_2x); 254 sampleBuffer = mResampler.UpSample(i, inputSamples, 2); 255 ProcessCurve<2>(sampleBuffer, sampleBuffer); 256 mResampler.DownSample(i, outputBuffer, 2); 257 break; 258 case OverSampleType::_4x: 259 mResampler.Reset(channelCount, aTrack->mSampleRate, 260 OverSampleType::_4x); 261 sampleBuffer = mResampler.UpSample(i, inputSamples, 4); 262 ProcessCurve<4>(sampleBuffer, sampleBuffer); 263 mResampler.DownSample(i, outputBuffer, 4); 264 break; 265 default: 266 MOZ_ASSERT_UNREACHABLE("We should never reach here"); 267 } 268 } 269 } 270 271 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override { 272 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 273 amount += mCurve.ShallowSizeOfExcludingThis(aMallocSizeOf); 274 amount += mResampler.SizeOfExcludingThis(aMallocSizeOf); 275 return amount; 276 } 277 278 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { 279 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 280 } 281 282 private: 283 nsTArray<float> mCurve; 284 OverSampleType mType; 285 Resampler mResampler; 286 }; 287 288 WaveShaperNode::WaveShaperNode(AudioContext* aContext) 289 : AudioNode(aContext, 2, ChannelCountMode::Max, 290 ChannelInterpretation::Speakers), 291 mType(OverSampleType::None) { 292 WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this); 293 mTrack = AudioNodeTrack::Create( 294 aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph()); 295 } 296 297 /* static */ 298 already_AddRefed<WaveShaperNode> WaveShaperNode::Create( 299 AudioContext& aAudioContext, const WaveShaperOptions& aOptions, 300 ErrorResult& aRv) { 301 RefPtr<WaveShaperNode> audioNode = new WaveShaperNode(&aAudioContext); 302 303 audioNode->Initialize(aOptions, aRv); 304 if (NS_WARN_IF(aRv.Failed())) { 305 return nullptr; 306 } 307 308 if (aOptions.mCurve.WasPassed()) { 309 audioNode->SetCurveInternal(aOptions.mCurve.Value(), aRv); 310 if (NS_WARN_IF(aRv.Failed())) { 311 return nullptr; 312 } 313 } 314 315 audioNode->SetOversample(aOptions.mOversample); 316 return audioNode.forget(); 317 } 318 319 JSObject* WaveShaperNode::WrapObject(JSContext* aCx, 320 JS::Handle<JSObject*> aGivenProto) { 321 return WaveShaperNode_Binding::Wrap(aCx, this, aGivenProto); 322 } 323 324 void WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve, 325 ErrorResult& aRv) { 326 // Let's purge the cached value for the curve attribute. 327 WaveShaperNode_Binding::ClearCachedCurveValue(this); 328 329 if (aCurve.IsNull()) { 330 CleanCurveInternal(); 331 return; 332 } 333 334 nsTArray<float> curve; 335 if (!aCurve.Value().AppendDataTo(curve)) { 336 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 337 return; 338 } 339 340 SetCurveInternal(curve, aRv); 341 } 342 343 void WaveShaperNode::SetCurveInternal(const nsTArray<float>& aCurve, 344 ErrorResult& aRv) { 345 if (aCurve.Length() < 2) { 346 aRv.ThrowInvalidStateError("Must have at least two entries"); 347 return; 348 } 349 350 mCurve = aCurve.Clone(); 351 SendCurveToTrack(); 352 } 353 354 void WaveShaperNode::CleanCurveInternal() { 355 mCurve.Clear(); 356 SendCurveToTrack(); 357 } 358 359 void WaveShaperNode::SendCurveToTrack() { 360 AudioNodeTrack* ns = mTrack; 361 MOZ_ASSERT(ns, "Why don't we have a track here?"); 362 363 nsTArray<float> copyCurve(mCurve.Clone()); 364 ns->SetRawArrayData(std::move(copyCurve)); 365 } 366 367 void WaveShaperNode::GetCurve(JSContext* aCx, 368 JS::MutableHandle<JSObject*> aRetval, 369 ErrorResult& aError) { 370 // Let's return a null value if the list is empty. 371 if (mCurve.IsEmpty()) { 372 aRetval.set(nullptr); 373 return; 374 } 375 376 MOZ_ASSERT(mCurve.Length() >= 2); 377 JSObject* curve = Float32Array::Create(aCx, this, mCurve, aError); 378 if (aError.Failed()) { 379 return; 380 } 381 aRetval.set(curve); 382 } 383 384 void WaveShaperNode::SetOversample(OverSampleType aType) { 385 mType = aType; 386 SendInt32ParameterToTrack(WaveShaperNodeEngine::TYPE, 387 static_cast<int32_t>(aType)); 388 } 389 390 } // namespace mozilla::dom