1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/webaudio/OscillatorNode.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,700 @@ 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 "OscillatorNode.h" 1.11 +#include "AudioNodeEngine.h" 1.12 +#include "AudioNodeStream.h" 1.13 +#include "AudioDestinationNode.h" 1.14 +#include "WebAudioUtils.h" 1.15 +#include "blink/PeriodicWave.h" 1.16 + 1.17 +namespace mozilla { 1.18 +namespace dom { 1.19 + 1.20 +NS_IMPL_CYCLE_COLLECTION_INHERITED(OscillatorNode, AudioNode, 1.21 + mPeriodicWave, mFrequency, mDetune) 1.22 + 1.23 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode) 1.24 +NS_INTERFACE_MAP_END_INHERITING(AudioNode) 1.25 + 1.26 +NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode) 1.27 +NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode) 1.28 + 1.29 +static const float sLeakTriangle = 0.995f; 1.30 +static const float sLeak = 0.999f; 1.31 + 1.32 +class DCBlocker 1.33 +{ 1.34 +public: 1.35 + // These are sane defauts when the initial mPhase is zero 1.36 + DCBlocker(float aLastInput = 0.0f, 1.37 + float aLastOutput = 0.0f, 1.38 + float aPole = 0.995) 1.39 + :mLastInput(aLastInput), 1.40 + mLastOutput(aLastOutput), 1.41 + mPole(aPole) 1.42 + { 1.43 + MOZ_ASSERT(aPole > 0); 1.44 + } 1.45 + 1.46 + inline float Process(float aInput) 1.47 + { 1.48 + float out; 1.49 + 1.50 + out = mLastOutput * mPole + aInput - mLastInput; 1.51 + mLastOutput = out; 1.52 + mLastInput = aInput; 1.53 + 1.54 + return out; 1.55 + } 1.56 +private: 1.57 + float mLastInput; 1.58 + float mLastOutput; 1.59 + float mPole; 1.60 +}; 1.61 + 1.62 + 1.63 +class OscillatorNodeEngine : public AudioNodeEngine 1.64 +{ 1.65 +public: 1.66 + OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination) 1.67 + : AudioNodeEngine(aNode) 1.68 + , mSource(nullptr) 1.69 + , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream())) 1.70 + , mStart(-1) 1.71 + , mStop(TRACK_TICKS_MAX) 1.72 + // Keep the default values in sync with OscillatorNode::OscillatorNode. 1.73 + , mFrequency(440.f) 1.74 + , mDetune(0.f) 1.75 + , mType(OscillatorType::Sine) 1.76 + , mPhase(0.) 1.77 + // mSquare, mTriangle, and mSaw are not used for default type "sine". 1.78 + // They are initialized if and when switching to the OscillatorTypes that 1.79 + // use them. 1.80 + // mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero, 1.81 + // mPhaseIncrement, and mPhaseWrap are initialized in 1.82 + // UpdateParametersIfNeeded() when mRecomputeParameters is set. 1.83 + , mRecomputeParameters(true) 1.84 + , mCustomLength(0) 1.85 + { 1.86 + } 1.87 + 1.88 + void SetSourceStream(AudioNodeStream* aSource) 1.89 + { 1.90 + mSource = aSource; 1.91 + } 1.92 + 1.93 + enum Parameters { 1.94 + FREQUENCY, 1.95 + DETUNE, 1.96 + TYPE, 1.97 + PERIODICWAVE, 1.98 + START, 1.99 + STOP, 1.100 + }; 1.101 + void SetTimelineParameter(uint32_t aIndex, 1.102 + const AudioParamTimeline& aValue, 1.103 + TrackRate aSampleRate) MOZ_OVERRIDE 1.104 + { 1.105 + mRecomputeParameters = true; 1.106 + switch (aIndex) { 1.107 + case FREQUENCY: 1.108 + MOZ_ASSERT(mSource && mDestination); 1.109 + mFrequency = aValue; 1.110 + WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination); 1.111 + break; 1.112 + case DETUNE: 1.113 + MOZ_ASSERT(mSource && mDestination); 1.114 + mDetune = aValue; 1.115 + WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination); 1.116 + break; 1.117 + default: 1.118 + NS_ERROR("Bad OscillatorNodeEngine TimelineParameter"); 1.119 + } 1.120 + } 1.121 + 1.122 + virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam) 1.123 + { 1.124 + switch (aIndex) { 1.125 + case START: mStart = aParam; break; 1.126 + case STOP: mStop = aParam; break; 1.127 + default: 1.128 + NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter"); 1.129 + } 1.130 + } 1.131 + 1.132 + virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam) 1.133 + { 1.134 + switch (aIndex) { 1.135 + case TYPE: 1.136 + // Set the new type. 1.137 + mType = static_cast<OscillatorType>(aParam); 1.138 + if (mType != OscillatorType::Custom) { 1.139 + // Forget any previous custom data. 1.140 + mCustomLength = 0; 1.141 + mCustom = nullptr; 1.142 + mPeriodicWave = nullptr; 1.143 + mRecomputeParameters = true; 1.144 + } 1.145 + // Update BLIT integrators with the new initial conditions. 1.146 + switch (mType) { 1.147 + case OscillatorType::Sine: 1.148 + mPhase = 0.0; 1.149 + break; 1.150 + case OscillatorType::Square: 1.151 + mPhase = 0.0; 1.152 + // Initial integration condition is -0.5, because our 1.153 + // square has 50% duty cycle. 1.154 + mSquare = -0.5; 1.155 + break; 1.156 + case OscillatorType::Triangle: 1.157 + // Initial mPhase and related integration condition so the 1.158 + // triangle is in the middle of the first upward slope. 1.159 + // XXX actually do the maths and put the right number here. 1.160 + mPhase = (float)(M_PI / 2); 1.161 + mSquare = 0.5; 1.162 + mTriangle = 0.0; 1.163 + break; 1.164 + case OscillatorType::Sawtooth: 1.165 + // Initial mPhase so the oscillator starts at the 1.166 + // middle of the ramp, per spec. 1.167 + mPhase = (float)(M_PI / 2); 1.168 + // mSaw = 0 when mPhase = pi/2. 1.169 + mSaw = 0.0; 1.170 + break; 1.171 + case OscillatorType::Custom: 1.172 + // Custom waveforms don't use BLIT. 1.173 + break; 1.174 + default: 1.175 + NS_ERROR("Bad OscillatorNodeEngine type parameter."); 1.176 + } 1.177 + // End type switch. 1.178 + break; 1.179 + case PERIODICWAVE: 1.180 + MOZ_ASSERT(aParam >= 0, "negative custom array length"); 1.181 + mCustomLength = static_cast<uint32_t>(aParam); 1.182 + break; 1.183 + default: 1.184 + NS_ERROR("Bad OscillatorNodeEngine Int32Parameter."); 1.185 + } 1.186 + // End index switch. 1.187 + } 1.188 + 1.189 + virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) 1.190 + { 1.191 + MOZ_ASSERT(mCustomLength, "Custom buffer sent before length"); 1.192 + mCustom = aBuffer; 1.193 + MOZ_ASSERT(mCustom->GetChannels() == 2, 1.194 + "PeriodicWave should have sent two channels"); 1.195 + mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(), 1.196 + mCustom->GetData(0), mCustom->GetData(1), mCustomLength); 1.197 + } 1.198 + 1.199 + void IncrementPhase() 1.200 + { 1.201 + mPhase += mPhaseIncrement; 1.202 + if (mPhase > mPhaseWrap) { 1.203 + mPhase -= mPhaseWrap; 1.204 + } 1.205 + } 1.206 + 1.207 + // Square and triangle are using a bipolar band-limited impulse train, saw is 1.208 + // using a normal band-limited impulse train. 1.209 + bool UsesBipolarBLIT() { 1.210 + return mType == OscillatorType::Square || mType == OscillatorType::Triangle; 1.211 + } 1.212 + 1.213 + void UpdateParametersIfNeeded(TrackTicks ticks, size_t count) 1.214 + { 1.215 + double frequency, detune; 1.216 + 1.217 + bool simpleFrequency = mFrequency.HasSimpleValue(); 1.218 + bool simpleDetune = mDetune.HasSimpleValue(); 1.219 + 1.220 + // Shortcut if frequency-related AudioParam are not automated, and we 1.221 + // already have computed the frequency information and related parameters. 1.222 + if (simpleFrequency && simpleDetune && !mRecomputeParameters) { 1.223 + return; 1.224 + } 1.225 + 1.226 + if (simpleFrequency) { 1.227 + frequency = mFrequency.GetValue(); 1.228 + } else { 1.229 + frequency = mFrequency.GetValueAtTime(ticks, count); 1.230 + } 1.231 + if (simpleDetune) { 1.232 + detune = mDetune.GetValue(); 1.233 + } else { 1.234 + detune = mDetune.GetValueAtTime(ticks, count); 1.235 + } 1.236 + 1.237 + mFinalFrequency = frequency * pow(2., detune / 1200.); 1.238 + mRecomputeParameters = false; 1.239 + 1.240 + // When using bipolar BLIT, we divide the signal period by two, because we 1.241 + // are using two BLIT out of phase. 1.242 + mSignalPeriod = UsesBipolarBLIT() ? 0.5 * mSource->SampleRate() / mFinalFrequency 1.243 + : mSource->SampleRate() / mFinalFrequency; 1.244 + // Wrap the phase accordingly: 1.245 + mPhaseWrap = UsesBipolarBLIT() || mType == OscillatorType::Sine ? 2 * M_PI 1.246 + : M_PI; 1.247 + // Even number of harmonics for bipolar blit, odd otherwise. 1.248 + mNumberOfHarmonics = UsesBipolarBLIT() ? 2 * floor(0.5 * mSignalPeriod) 1.249 + : 2 * floor(0.5 * mSignalPeriod) + 1; 1.250 + mPhaseIncrement = mType == OscillatorType::Sine ? 2 * M_PI / mSignalPeriod 1.251 + : M_PI / mSignalPeriod; 1.252 + mAmplitudeAtZero = mNumberOfHarmonics / mSignalPeriod; 1.253 + } 1.254 + 1.255 + void FillBounds(float* output, TrackTicks ticks, 1.256 + uint32_t& start, uint32_t& end) 1.257 + { 1.258 + MOZ_ASSERT(output); 1.259 + static_assert(TrackTicks(WEBAUDIO_BLOCK_SIZE) < UINT_MAX, 1.260 + "WEBAUDIO_BLOCK_SIZE overflows interator bounds."); 1.261 + start = 0; 1.262 + if (ticks < mStart) { 1.263 + start = mStart - ticks; 1.264 + for (uint32_t i = 0; i < start; ++i) { 1.265 + output[i] = 0.0; 1.266 + } 1.267 + } 1.268 + end = WEBAUDIO_BLOCK_SIZE; 1.269 + if (ticks + end > mStop) { 1.270 + end = mStop - ticks; 1.271 + for (uint32_t i = end; i < WEBAUDIO_BLOCK_SIZE; ++i) { 1.272 + output[i] = 0.0; 1.273 + } 1.274 + } 1.275 + } 1.276 + 1.277 + float BipolarBLIT() 1.278 + { 1.279 + float blit; 1.280 + float denom = sin(mPhase); 1.281 + 1.282 + if (fabs(denom) < std::numeric_limits<float>::epsilon()) { 1.283 + if (mPhase < 0.1f || mPhase > 2 * M_PI - 0.1f) { 1.284 + blit = mAmplitudeAtZero; 1.285 + } else { 1.286 + blit = -mAmplitudeAtZero; 1.287 + } 1.288 + } else { 1.289 + blit = sin(mNumberOfHarmonics * mPhase); 1.290 + blit /= mSignalPeriod * denom; 1.291 + } 1.292 + return blit; 1.293 + } 1.294 + 1.295 + float UnipolarBLIT() 1.296 + { 1.297 + float blit; 1.298 + float denom = sin(mPhase); 1.299 + 1.300 + if (fabs(denom) <= std::numeric_limits<float>::epsilon()) { 1.301 + blit = mAmplitudeAtZero; 1.302 + } else { 1.303 + blit = sin(mNumberOfHarmonics * mPhase); 1.304 + blit /= mSignalPeriod * denom; 1.305 + } 1.306 + 1.307 + return blit; 1.308 + } 1.309 + 1.310 + void ComputeSine(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd) 1.311 + { 1.312 + for (uint32_t i = aStart; i < aEnd; ++i) { 1.313 + UpdateParametersIfNeeded(ticks, i); 1.314 + 1.315 + aOutput[i] = sin(mPhase); 1.316 + 1.317 + IncrementPhase(); 1.318 + } 1.319 + } 1.320 + 1.321 + void ComputeSquare(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd) 1.322 + { 1.323 + for (uint32_t i = aStart; i < aEnd; ++i) { 1.324 + UpdateParametersIfNeeded(ticks, i); 1.325 + // Integration to get us a square. It turns out we can have a 1.326 + // pure integrator here. 1.327 + mSquare = mSquare * sLeak + BipolarBLIT(); 1.328 + aOutput[i] = mSquare; 1.329 + // maybe we want to apply a gain, the wg has not decided yet 1.330 + aOutput[i] *= 1.5; 1.331 + IncrementPhase(); 1.332 + } 1.333 + } 1.334 + 1.335 + void ComputeSawtooth(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd) 1.336 + { 1.337 + float dcoffset; 1.338 + for (uint32_t i = aStart; i < aEnd; ++i) { 1.339 + UpdateParametersIfNeeded(ticks, i); 1.340 + // DC offset so the Saw does not ramp up to infinity when integrating. 1.341 + dcoffset = mFinalFrequency / mSource->SampleRate(); 1.342 + // Integrate and offset so we get mAmplitudeAtZero sawtooth. We have a 1.343 + // very low frequency component somewhere here, but I'm not sure where. 1.344 + mSaw = mSaw * sLeak + (UnipolarBLIT() - dcoffset); 1.345 + // reverse the saw so we are spec compliant 1.346 + aOutput[i] = -mSaw * 1.5; 1.347 + 1.348 + IncrementPhase(); 1.349 + } 1.350 + } 1.351 + 1.352 + void ComputeTriangle(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd) 1.353 + { 1.354 + for (uint32_t i = aStart; i < aEnd; ++i) { 1.355 + UpdateParametersIfNeeded(ticks, i); 1.356 + // Integrate to get a square 1.357 + mSquare += BipolarBLIT(); 1.358 + // Leaky integrate to get a triangle. We get too much dc offset if we don't 1.359 + // leaky integrate here. 1.360 + // C6 = k0 / period 1.361 + // (period is samplingrate / frequency, k0 = (PI/2)/(2*PI)) = 0.25 1.362 + float C6 = 0.25 / (mSource->SampleRate() / mFinalFrequency); 1.363 + mTriangle = mTriangle * sLeakTriangle + mSquare + C6; 1.364 + // DC Block, and scale back to [-1.0; 1.0] 1.365 + aOutput[i] = mDCBlocker.Process(mTriangle) / (mSignalPeriod/2) * 1.5; 1.366 + 1.367 + IncrementPhase(); 1.368 + } 1.369 + } 1.370 + 1.371 + void ComputeCustom(float* aOutput, 1.372 + TrackTicks ticks, 1.373 + uint32_t aStart, 1.374 + uint32_t aEnd) 1.375 + { 1.376 + MOZ_ASSERT(mPeriodicWave, "No custom waveform data"); 1.377 + 1.378 + uint32_t periodicWaveSize = mPeriodicWave->periodicWaveSize(); 1.379 + // Mask to wrap wave data indices into the range [0,periodicWaveSize). 1.380 + uint32_t indexMask = periodicWaveSize - 1; 1.381 + MOZ_ASSERT(periodicWaveSize && (periodicWaveSize & indexMask) == 0, 1.382 + "periodicWaveSize must be power of 2"); 1.383 + float* higherWaveData = nullptr; 1.384 + float* lowerWaveData = nullptr; 1.385 + float tableInterpolationFactor; 1.386 + // Phase increment at frequency of 1 Hz. 1.387 + // mPhase runs [0,periodicWaveSize) here instead of [0,2*M_PI). 1.388 + float basePhaseIncrement = 1.389 + static_cast<float>(periodicWaveSize) / mSource->SampleRate(); 1.390 + 1.391 + for (uint32_t i = aStart; i < aEnd; ++i) { 1.392 + UpdateParametersIfNeeded(ticks, i); 1.393 + mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency, 1.394 + lowerWaveData, 1.395 + higherWaveData, 1.396 + tableInterpolationFactor); 1.397 + // Bilinear interpolation between adjacent samples in each table. 1.398 + float floorPhase = floorf(mPhase); 1.399 + uint32_t j1 = floorPhase; 1.400 + j1 &= indexMask; 1.401 + uint32_t j2 = j1 + 1; 1.402 + j2 &= indexMask; 1.403 + 1.404 + float sampleInterpolationFactor = mPhase - floorPhase; 1.405 + 1.406 + float lower = (1.0f - sampleInterpolationFactor) * lowerWaveData[j1] + 1.407 + sampleInterpolationFactor * lowerWaveData[j2]; 1.408 + float higher = (1.0f - sampleInterpolationFactor) * higherWaveData[j1] + 1.409 + sampleInterpolationFactor * higherWaveData[j2]; 1.410 + aOutput[i] = (1.0f - tableInterpolationFactor) * lower + 1.411 + tableInterpolationFactor * higher; 1.412 + 1.413 + // Calculate next phase position from wrapped value j1 to avoid loss of 1.414 + // precision at large values. 1.415 + mPhase = 1.416 + j1 + sampleInterpolationFactor + basePhaseIncrement * mFinalFrequency; 1.417 + } 1.418 + } 1.419 + 1.420 + void ComputeSilence(AudioChunk *aOutput) 1.421 + { 1.422 + aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); 1.423 + } 1.424 + 1.425 + virtual void ProcessBlock(AudioNodeStream* aStream, 1.426 + const AudioChunk& aInput, 1.427 + AudioChunk* aOutput, 1.428 + bool* aFinished) MOZ_OVERRIDE 1.429 + { 1.430 + MOZ_ASSERT(mSource == aStream, "Invalid source stream"); 1.431 + 1.432 + TrackTicks ticks = aStream->GetCurrentPosition(); 1.433 + if (mStart == -1) { 1.434 + ComputeSilence(aOutput); 1.435 + return; 1.436 + } 1.437 + 1.438 + if (ticks >= mStop) { 1.439 + // We've finished playing. 1.440 + ComputeSilence(aOutput); 1.441 + *aFinished = true; 1.442 + return; 1.443 + } 1.444 + if (ticks + WEBAUDIO_BLOCK_SIZE < mStart) { 1.445 + // We're not playing yet. 1.446 + ComputeSilence(aOutput); 1.447 + return; 1.448 + } 1.449 + 1.450 + AllocateAudioBlock(1, aOutput); 1.451 + float* output = static_cast<float*>( 1.452 + const_cast<void*>(aOutput->mChannelData[0])); 1.453 + 1.454 + uint32_t start, end; 1.455 + FillBounds(output, ticks, start, end); 1.456 + 1.457 + // Synthesize the correct waveform. 1.458 + switch(mType) { 1.459 + case OscillatorType::Sine: 1.460 + ComputeSine(output, ticks, start, end); 1.461 + break; 1.462 + case OscillatorType::Square: 1.463 + ComputeSquare(output, ticks, start, end); 1.464 + break; 1.465 + case OscillatorType::Triangle: 1.466 + ComputeTriangle(output, ticks, start, end); 1.467 + break; 1.468 + case OscillatorType::Sawtooth: 1.469 + ComputeSawtooth(output, ticks, start, end); 1.470 + break; 1.471 + case OscillatorType::Custom: 1.472 + ComputeCustom(output, ticks, start, end); 1.473 + break; 1.474 + default: 1.475 + ComputeSilence(aOutput); 1.476 + }; 1.477 + 1.478 + } 1.479 + 1.480 + virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.481 + { 1.482 + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); 1.483 + 1.484 + // Not owned: 1.485 + // - mSource 1.486 + // - mDestination 1.487 + // - mFrequency (internal ref owned by node) 1.488 + // - mDetune (internal ref owned by node) 1.489 + 1.490 + if (mCustom) { 1.491 + amount += mCustom->SizeOfIncludingThis(aMallocSizeOf); 1.492 + } 1.493 + 1.494 + if (mPeriodicWave) { 1.495 + amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf); 1.496 + } 1.497 + 1.498 + return amount; 1.499 + } 1.500 + 1.501 + virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE 1.502 + { 1.503 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.504 + } 1.505 + 1.506 + DCBlocker mDCBlocker; 1.507 + AudioNodeStream* mSource; 1.508 + AudioNodeStream* mDestination; 1.509 + TrackTicks mStart; 1.510 + TrackTicks mStop; 1.511 + AudioParamTimeline mFrequency; 1.512 + AudioParamTimeline mDetune; 1.513 + OscillatorType mType; 1.514 + float mPhase; 1.515 + float mFinalFrequency; 1.516 + uint32_t mNumberOfHarmonics; 1.517 + float mSignalPeriod; 1.518 + float mAmplitudeAtZero; 1.519 + float mPhaseIncrement; 1.520 + float mSquare; 1.521 + float mTriangle; 1.522 + float mSaw; 1.523 + float mPhaseWrap; 1.524 + bool mRecomputeParameters; 1.525 + nsRefPtr<ThreadSharedFloatArrayBufferList> mCustom; 1.526 + uint32_t mCustomLength; 1.527 + nsAutoPtr<WebCore::PeriodicWave> mPeriodicWave; 1.528 +}; 1.529 + 1.530 +OscillatorNode::OscillatorNode(AudioContext* aContext) 1.531 + : AudioNode(aContext, 1.532 + 2, 1.533 + ChannelCountMode::Max, 1.534 + ChannelInterpretation::Speakers) 1.535 + , mType(OscillatorType::Sine) 1.536 + , mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.537 + SendFrequencyToStream, 440.0f)) 1.538 + , mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(), 1.539 + SendDetuneToStream, 0.0f)) 1.540 + , mStartCalled(false) 1.541 + , mStopped(false) 1.542 +{ 1.543 + OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination()); 1.544 + mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM); 1.545 + engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get())); 1.546 + mStream->AddMainThreadListener(this); 1.547 +} 1.548 + 1.549 +OscillatorNode::~OscillatorNode() 1.550 +{ 1.551 +} 1.552 + 1.553 +size_t 1.554 +OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.555 +{ 1.556 + size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); 1.557 + 1.558 + // For now only report if we know for sure that it's not shared. 1.559 + amount += mPeriodicWave->SizeOfExcludingThisIfNotShared(aMallocSizeOf); 1.560 + amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf); 1.561 + amount += mDetune->SizeOfIncludingThis(aMallocSizeOf); 1.562 + return amount; 1.563 +} 1.564 + 1.565 +size_t 1.566 +OscillatorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.567 +{ 1.568 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.569 +} 1.570 + 1.571 +JSObject* 1.572 +OscillatorNode::WrapObject(JSContext* aCx) 1.573 +{ 1.574 + return OscillatorNodeBinding::Wrap(aCx, this); 1.575 +} 1.576 + 1.577 +void 1.578 +OscillatorNode::SendFrequencyToStream(AudioNode* aNode) 1.579 +{ 1.580 + OscillatorNode* This = static_cast<OscillatorNode*>(aNode); 1.581 + SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency); 1.582 +} 1.583 + 1.584 +void 1.585 +OscillatorNode::SendDetuneToStream(AudioNode* aNode) 1.586 +{ 1.587 + OscillatorNode* This = static_cast<OscillatorNode*>(aNode); 1.588 + SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune); 1.589 +} 1.590 + 1.591 +void 1.592 +OscillatorNode::SendTypeToStream() 1.593 +{ 1.594 + if (mType == OscillatorType::Custom) { 1.595 + // The engine assumes we'll send the custom data before updating the type. 1.596 + SendPeriodicWaveToStream(); 1.597 + } 1.598 + SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType)); 1.599 +} 1.600 + 1.601 +void OscillatorNode::SendPeriodicWaveToStream() 1.602 +{ 1.603 + NS_ASSERTION(mType == OscillatorType::Custom, 1.604 + "Sending custom waveform to engine thread with non-custom type"); 1.605 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.606 + MOZ_ASSERT(ns, "Missing node stream."); 1.607 + MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object."); 1.608 + SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE, 1.609 + mPeriodicWave->DataLength()); 1.610 + nsRefPtr<ThreadSharedFloatArrayBufferList> data = 1.611 + mPeriodicWave->GetThreadSharedBuffer(); 1.612 + ns->SetBuffer(data.forget()); 1.613 +} 1.614 + 1.615 +void 1.616 +OscillatorNode::Start(double aWhen, ErrorResult& aRv) 1.617 +{ 1.618 + if (!WebAudioUtils::IsTimeValid(aWhen)) { 1.619 + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 1.620 + return; 1.621 + } 1.622 + 1.623 + if (mStartCalled) { 1.624 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.625 + return; 1.626 + } 1.627 + mStartCalled = true; 1.628 + 1.629 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.630 + if (!ns) { 1.631 + // Nothing to play, or we're already dead for some reason 1.632 + return; 1.633 + } 1.634 + 1.635 + // TODO: Perhaps we need to do more here. 1.636 + ns->SetStreamTimeParameter(OscillatorNodeEngine::START, 1.637 + Context(), aWhen); 1.638 + 1.639 + MarkActive(); 1.640 +} 1.641 + 1.642 +void 1.643 +OscillatorNode::Stop(double aWhen, ErrorResult& aRv) 1.644 +{ 1.645 + if (!WebAudioUtils::IsTimeValid(aWhen)) { 1.646 + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); 1.647 + return; 1.648 + } 1.649 + 1.650 + if (!mStartCalled) { 1.651 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.652 + return; 1.653 + } 1.654 + 1.655 + AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get()); 1.656 + if (!ns || !Context()) { 1.657 + // We've already stopped and had our stream shut down 1.658 + return; 1.659 + } 1.660 + 1.661 + // TODO: Perhaps we need to do more here. 1.662 + ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP, 1.663 + Context(), std::max(0.0, aWhen)); 1.664 +} 1.665 + 1.666 +void 1.667 +OscillatorNode::NotifyMainThreadStateChanged() 1.668 +{ 1.669 + if (mStream->IsFinished()) { 1.670 + class EndedEventDispatcher : public nsRunnable 1.671 + { 1.672 + public: 1.673 + explicit EndedEventDispatcher(OscillatorNode* aNode) 1.674 + : mNode(aNode) {} 1.675 + NS_IMETHODIMP Run() 1.676 + { 1.677 + // If it's not safe to run scripts right now, schedule this to run later 1.678 + if (!nsContentUtils::IsSafeToRunScript()) { 1.679 + nsContentUtils::AddScriptRunner(this); 1.680 + return NS_OK; 1.681 + } 1.682 + 1.683 + mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended")); 1.684 + return NS_OK; 1.685 + } 1.686 + private: 1.687 + nsRefPtr<OscillatorNode> mNode; 1.688 + }; 1.689 + if (!mStopped) { 1.690 + // Only dispatch the ended event once 1.691 + NS_DispatchToMainThread(new EndedEventDispatcher(this)); 1.692 + mStopped = true; 1.693 + } 1.694 + 1.695 + // Drop the playing reference 1.696 + // Warning: The below line might delete this. 1.697 + MarkInactive(); 1.698 + } 1.699 +} 1.700 + 1.701 +} 1.702 +} 1.703 +