1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/WaveShaperNode.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,338 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "WaveShaperNode.h" 1.11 +#include "mozilla/dom/WaveShaperNodeBinding.h" 1.12 +#include "AudioNode.h" 1.13 +#include "AudioNodeEngine.h" 1.14 +#include "AudioNodeStream.h" 1.15 +#include "mozilla/PodOperations.h" 1.16 +#include "speex/speex_resampler.h" 1.17 + 1.18 +namespace mozilla { 1.19 +namespace dom { 1.20 + 1.21 +NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode) 1.22 + 1.23 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode) 1.24 + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1.25 + tmp->ClearCurve(); 1.26 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.27 + 1.28 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WaveShaperNode, AudioNode) 1.29 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.30 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.31 + 1.32 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(WaveShaperNode) 1.33 + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 1.34 + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCurve) 1.35 +NS_IMPL_CYCLE_COLLECTION_TRACE_END 1.36 + 1.37 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WaveShaperNode) 1.38 +NS_INTERFACE_MAP_END_INHERITING(AudioNode) 1.39 + 1.40 +NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode) 1.41 +NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode) 1.42 + 1.43 +static uint32_t ValueOf(OverSampleType aType) 1.44 +{ 1.45 + switch (aType) { 1.46 + case OverSampleType::None: return 1; 1.47 + case OverSampleType::_2x: return 2; 1.48 + case OverSampleType::_4x: return 4; 1.49 + default: 1.50 + NS_NOTREACHED("We should never reach here"); 1.51 + return 1; 1.52 + } 1.53 +} 1.54 + 1.55 +class Resampler 1.56 +{ 1.57 +public: 1.58 + Resampler() 1.59 + : mType(OverSampleType::None) 1.60 + , mUpSampler(nullptr) 1.61 + , mDownSampler(nullptr) 1.62 + , mChannels(0) 1.63 + , mSampleRate(0) 1.64 + { 1.65 + } 1.66 + 1.67 + ~Resampler() 1.68 + { 1.69 + Destroy(); 1.70 + } 1.71 + 1.72 + void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType) 1.73 + { 1.74 + if (aChannels == mChannels && 1.75 + aSampleRate == mSampleRate && 1.76 + aType == mType) { 1.77 + return; 1.78 + } 1.79 + 1.80 + mChannels = aChannels; 1.81 + mSampleRate = aSampleRate; 1.82 + mType = aType; 1.83 + 1.84 + Destroy(); 1.85 + 1.86 + if (aType == OverSampleType::None) { 1.87 + mBuffer.Clear(); 1.88 + return; 1.89 + } 1.90 + 1.91 + mUpSampler = speex_resampler_init(aChannels, 1.92 + aSampleRate, 1.93 + aSampleRate * ValueOf(aType), 1.94 + SPEEX_RESAMPLER_QUALITY_DEFAULT, 1.95 + nullptr); 1.96 + mDownSampler = speex_resampler_init(aChannels, 1.97 + aSampleRate * ValueOf(aType), 1.98 + aSampleRate, 1.99 + SPEEX_RESAMPLER_QUALITY_DEFAULT, 1.100 + nullptr); 1.101 + mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE*ValueOf(aType)); 1.102 + } 1.103 + 1.104 + float* UpSample(uint32_t aChannel, const float* aInputData, uint32_t aBlocks) 1.105 + { 1.106 + uint32_t inSamples = WEBAUDIO_BLOCK_SIZE; 1.107 + uint32_t outSamples = WEBAUDIO_BLOCK_SIZE*aBlocks; 1.108 + float* outputData = mBuffer.Elements(); 1.109 + 1.110 + MOZ_ASSERT(mBuffer.Length() == outSamples); 1.111 + 1.112 + WebAudioUtils::SpeexResamplerProcess(mUpSampler, aChannel, 1.113 + aInputData, &inSamples, 1.114 + outputData, &outSamples); 1.115 + 1.116 + MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE && outSamples == WEBAUDIO_BLOCK_SIZE*aBlocks); 1.117 + 1.118 + return outputData; 1.119 + } 1.120 + 1.121 + void DownSample(uint32_t aChannel, float* aOutputData, uint32_t aBlocks) 1.122 + { 1.123 + uint32_t inSamples = WEBAUDIO_BLOCK_SIZE*aBlocks; 1.124 + uint32_t outSamples = WEBAUDIO_BLOCK_SIZE; 1.125 + const float* inputData = mBuffer.Elements(); 1.126 + 1.127 + MOZ_ASSERT(mBuffer.Length() == inSamples); 1.128 + 1.129 + WebAudioUtils::SpeexResamplerProcess(mDownSampler, aChannel, 1.130 + inputData, &inSamples, 1.131 + aOutputData, &outSamples); 1.132 + 1.133 + MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE*aBlocks && outSamples == WEBAUDIO_BLOCK_SIZE); 1.134 + } 1.135 + 1.136 + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.137 + { 1.138 + size_t amount = 0; 1.139 + // Future: properly measure speex memory 1.140 + amount += aMallocSizeOf(mUpSampler); 1.141 + amount += aMallocSizeOf(mDownSampler); 1.142 + amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf); 1.143 + return amount; 1.144 + } 1.145 + 1.146 +private: 1.147 + void Destroy() 1.148 + { 1.149 + if (mUpSampler) { 1.150 + speex_resampler_destroy(mUpSampler); 1.151 + mUpSampler = nullptr; 1.152 + } 1.153 + if (mDownSampler) { 1.154 + speex_resampler_destroy(mDownSampler); 1.155 + mDownSampler = nullptr; 1.156 + } 1.157 + } 1.158 + 1.159 +private: 1.160 + OverSampleType mType; 1.161 + SpeexResamplerState* mUpSampler; 1.162 + SpeexResamplerState* mDownSampler; 1.163 + uint32_t mChannels; 1.164 + TrackRate mSampleRate; 1.165 + nsTArray<float> mBuffer; 1.166 +}; 1.167 + 1.168 +class WaveShaperNodeEngine : public AudioNodeEngine 1.169 +{ 1.170 +public: 1.171 + explicit WaveShaperNodeEngine(AudioNode* aNode) 1.172 + : AudioNodeEngine(aNode) 1.173 + , mType(OverSampleType::None) 1.174 + { 1.175 + } 1.176 + 1.177 + enum Parameteres { 1.178 + TYPE 1.179 + }; 1.180 + 1.181 + virtual void SetRawArrayData(nsTArray<float>& aCurve) MOZ_OVERRIDE 1.182 + { 1.183 + mCurve.SwapElements(aCurve); 1.184 + } 1.185 + 1.186 + virtual void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_OVERRIDE 1.187 + { 1.188 + switch (aIndex) { 1.189 + case TYPE: 1.190 + mType = static_cast<OverSampleType>(aValue); 1.191 + break; 1.192 + default: 1.193 + NS_ERROR("Bad WaveShaperNode Int32Parameter"); 1.194 + } 1.195 + } 1.196 + 1.197 + template <uint32_t blocks> 1.198 + void ProcessCurve(const float* aInputBuffer, float* aOutputBuffer) 1.199 + { 1.200 + for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE*blocks; ++j) { 1.201 + // Index into the curve array based on the amplitude of the 1.202 + // incoming signal by clamping the amplitude to [-1, 1] and 1.203 + // performing a linear interpolation of the neighbor values. 1.204 + float index = std::max(0.0f, std::min(float(mCurve.Length() - 1), 1.205 + mCurve.Length() * (aInputBuffer[j] + 1) / 2)); 1.206 + uint32_t indexLower = uint32_t(index); 1.207 + uint32_t indexHigher = uint32_t(index + 1.0f); 1.208 + if (indexHigher == mCurve.Length()) { 1.209 + aOutputBuffer[j] = mCurve[indexLower]; 1.210 + } else { 1.211 + float interpolationFactor = index - indexLower; 1.212 + aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] + 1.213 + interpolationFactor * mCurve[indexHigher]; 1.214 + } 1.215 + } 1.216 + } 1.217 + 1.218 + virtual void ProcessBlock(AudioNodeStream* aStream, 1.219 + const AudioChunk& aInput, 1.220 + AudioChunk* aOutput, 1.221 + bool* aFinished) 1.222 + { 1.223 + uint32_t channelCount = aInput.mChannelData.Length(); 1.224 + if (!mCurve.Length() || !channelCount) { 1.225 + // Optimize the case where we don't have a curve buffer, 1.226 + // or the input is null. 1.227 + *aOutput = aInput; 1.228 + return; 1.229 + } 1.230 + 1.231 + AllocateAudioBlock(channelCount, aOutput); 1.232 + for (uint32_t i = 0; i < channelCount; ++i) { 1.233 + const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]); 1.234 + float* outputBuffer = const_cast<float*> (static_cast<const float*>(aOutput->mChannelData[i])); 1.235 + float* sampleBuffer; 1.236 + 1.237 + switch (mType) { 1.238 + case OverSampleType::None: 1.239 + mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::None); 1.240 + ProcessCurve<1>(inputBuffer, outputBuffer); 1.241 + break; 1.242 + case OverSampleType::_2x: 1.243 + mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_2x); 1.244 + sampleBuffer = mResampler.UpSample(i, inputBuffer, 2); 1.245 + ProcessCurve<2>(sampleBuffer, sampleBuffer); 1.246 + mResampler.DownSample(i, outputBuffer, 2); 1.247 + break; 1.248 + case OverSampleType::_4x: 1.249 + mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_4x); 1.250 + sampleBuffer = mResampler.UpSample(i, inputBuffer, 4); 1.251 + ProcessCurve<4>(sampleBuffer, sampleBuffer); 1.252 + mResampler.DownSample(i, outputBuffer, 4); 1.253 + break; 1.254 + default: 1.255 + NS_NOTREACHED("We should never reach here"); 1.256 + } 1.257 + } 1.258 + } 1.259 + 1.260 + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.261 + { 1.262 + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 1.263 + amount += mCurve.SizeOfExcludingThis(aMallocSizeOf); 1.264 + amount += mResampler.SizeOfExcludingThis(aMallocSizeOf); 1.265 + return amount; 1.266 + } 1.267 + 1.268 + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.269 + { 1.270 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.271 + } 1.272 + 1.273 +private: 1.274 + nsTArray<float> mCurve; 1.275 + OverSampleType mType; 1.276 + Resampler mResampler; 1.277 +}; 1.278 + 1.279 +WaveShaperNode::WaveShaperNode(AudioContext* aContext) 1.280 + : AudioNode(aContext, 1.281 + 2, 1.282 + ChannelCountMode::Max, 1.283 + ChannelInterpretation::Speakers) 1.284 + , mCurve(nullptr) 1.285 + , mType(OverSampleType::None) 1.286 +{ 1.287 + mozilla::HoldJSObjects(this); 1.288 + 1.289 + WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this); 1.290 + mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM); 1.291 +} 1.292 + 1.293 +WaveShaperNode::~WaveShaperNode() 1.294 +{ 1.295 + ClearCurve(); 1.296 +} 1.297 + 1.298 +void 1.299 +WaveShaperNode::ClearCurve() 1.300 +{ 1.301 + mCurve = nullptr; 1.302 + mozilla::DropJSObjects(this); 1.303 +} 1.304 + 1.305 +JSObject* 1.306 +WaveShaperNode::WrapObject(JSContext *aCx) 1.307 +{ 1.308 + return WaveShaperNodeBinding::Wrap(aCx, this); 1.309 +} 1.310 + 1.311 +void 1.312 +WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve) 1.313 +{ 1.314 + nsTArray<float> curve; 1.315 + if (!aCurve.IsNull()) { 1.316 + const Float32Array& floats = aCurve.Value(); 1.317 + 1.318 + mCurve = floats.Obj(); 1.319 + 1.320 + floats.ComputeLengthAndData(); 1.321 + 1.322 + curve.SetLength(floats.Length()); 1.323 + PodCopy(curve.Elements(), floats.Data(), floats.Length()); 1.324 + } else { 1.325 + mCurve = nullptr; 1.326 + } 1.327 + 1.328 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.329 + MOZ_ASSERT(ns, "Why don't we have a stream here?"); 1.330 + ns->SetRawArrayData(curve); 1.331 +} 1.332 + 1.333 +void 1.334 +WaveShaperNode::SetOversample(OverSampleType aType) 1.335 +{ 1.336 + mType = aType; 1.337 + SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType)); 1.338 +} 1.339 + 1.340 +} 1.341 +}