content/media/webaudio/OscillatorNode.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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 "OscillatorNode.h"
michael@0 8 #include "AudioNodeEngine.h"
michael@0 9 #include "AudioNodeStream.h"
michael@0 10 #include "AudioDestinationNode.h"
michael@0 11 #include "WebAudioUtils.h"
michael@0 12 #include "blink/PeriodicWave.h"
michael@0 13
michael@0 14 namespace mozilla {
michael@0 15 namespace dom {
michael@0 16
michael@0 17 NS_IMPL_CYCLE_COLLECTION_INHERITED(OscillatorNode, AudioNode,
michael@0 18 mPeriodicWave, mFrequency, mDetune)
michael@0 19
michael@0 20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
michael@0 21 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
michael@0 22
michael@0 23 NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
michael@0 24 NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
michael@0 25
michael@0 26 static const float sLeakTriangle = 0.995f;
michael@0 27 static const float sLeak = 0.999f;
michael@0 28
michael@0 29 class DCBlocker
michael@0 30 {
michael@0 31 public:
michael@0 32 // These are sane defauts when the initial mPhase is zero
michael@0 33 DCBlocker(float aLastInput = 0.0f,
michael@0 34 float aLastOutput = 0.0f,
michael@0 35 float aPole = 0.995)
michael@0 36 :mLastInput(aLastInput),
michael@0 37 mLastOutput(aLastOutput),
michael@0 38 mPole(aPole)
michael@0 39 {
michael@0 40 MOZ_ASSERT(aPole > 0);
michael@0 41 }
michael@0 42
michael@0 43 inline float Process(float aInput)
michael@0 44 {
michael@0 45 float out;
michael@0 46
michael@0 47 out = mLastOutput * mPole + aInput - mLastInput;
michael@0 48 mLastOutput = out;
michael@0 49 mLastInput = aInput;
michael@0 50
michael@0 51 return out;
michael@0 52 }
michael@0 53 private:
michael@0 54 float mLastInput;
michael@0 55 float mLastOutput;
michael@0 56 float mPole;
michael@0 57 };
michael@0 58
michael@0 59
michael@0 60 class OscillatorNodeEngine : public AudioNodeEngine
michael@0 61 {
michael@0 62 public:
michael@0 63 OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
michael@0 64 : AudioNodeEngine(aNode)
michael@0 65 , mSource(nullptr)
michael@0 66 , mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
michael@0 67 , mStart(-1)
michael@0 68 , mStop(TRACK_TICKS_MAX)
michael@0 69 // Keep the default values in sync with OscillatorNode::OscillatorNode.
michael@0 70 , mFrequency(440.f)
michael@0 71 , mDetune(0.f)
michael@0 72 , mType(OscillatorType::Sine)
michael@0 73 , mPhase(0.)
michael@0 74 // mSquare, mTriangle, and mSaw are not used for default type "sine".
michael@0 75 // They are initialized if and when switching to the OscillatorTypes that
michael@0 76 // use them.
michael@0 77 // mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
michael@0 78 // mPhaseIncrement, and mPhaseWrap are initialized in
michael@0 79 // UpdateParametersIfNeeded() when mRecomputeParameters is set.
michael@0 80 , mRecomputeParameters(true)
michael@0 81 , mCustomLength(0)
michael@0 82 {
michael@0 83 }
michael@0 84
michael@0 85 void SetSourceStream(AudioNodeStream* aSource)
michael@0 86 {
michael@0 87 mSource = aSource;
michael@0 88 }
michael@0 89
michael@0 90 enum Parameters {
michael@0 91 FREQUENCY,
michael@0 92 DETUNE,
michael@0 93 TYPE,
michael@0 94 PERIODICWAVE,
michael@0 95 START,
michael@0 96 STOP,
michael@0 97 };
michael@0 98 void SetTimelineParameter(uint32_t aIndex,
michael@0 99 const AudioParamTimeline& aValue,
michael@0 100 TrackRate aSampleRate) MOZ_OVERRIDE
michael@0 101 {
michael@0 102 mRecomputeParameters = true;
michael@0 103 switch (aIndex) {
michael@0 104 case FREQUENCY:
michael@0 105 MOZ_ASSERT(mSource && mDestination);
michael@0 106 mFrequency = aValue;
michael@0 107 WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
michael@0 108 break;
michael@0 109 case DETUNE:
michael@0 110 MOZ_ASSERT(mSource && mDestination);
michael@0 111 mDetune = aValue;
michael@0 112 WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
michael@0 113 break;
michael@0 114 default:
michael@0 115 NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
michael@0 116 }
michael@0 117 }
michael@0 118
michael@0 119 virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
michael@0 120 {
michael@0 121 switch (aIndex) {
michael@0 122 case START: mStart = aParam; break;
michael@0 123 case STOP: mStop = aParam; break;
michael@0 124 default:
michael@0 125 NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
michael@0 126 }
michael@0 127 }
michael@0 128
michael@0 129 virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
michael@0 130 {
michael@0 131 switch (aIndex) {
michael@0 132 case TYPE:
michael@0 133 // Set the new type.
michael@0 134 mType = static_cast<OscillatorType>(aParam);
michael@0 135 if (mType != OscillatorType::Custom) {
michael@0 136 // Forget any previous custom data.
michael@0 137 mCustomLength = 0;
michael@0 138 mCustom = nullptr;
michael@0 139 mPeriodicWave = nullptr;
michael@0 140 mRecomputeParameters = true;
michael@0 141 }
michael@0 142 // Update BLIT integrators with the new initial conditions.
michael@0 143 switch (mType) {
michael@0 144 case OscillatorType::Sine:
michael@0 145 mPhase = 0.0;
michael@0 146 break;
michael@0 147 case OscillatorType::Square:
michael@0 148 mPhase = 0.0;
michael@0 149 // Initial integration condition is -0.5, because our
michael@0 150 // square has 50% duty cycle.
michael@0 151 mSquare = -0.5;
michael@0 152 break;
michael@0 153 case OscillatorType::Triangle:
michael@0 154 // Initial mPhase and related integration condition so the
michael@0 155 // triangle is in the middle of the first upward slope.
michael@0 156 // XXX actually do the maths and put the right number here.
michael@0 157 mPhase = (float)(M_PI / 2);
michael@0 158 mSquare = 0.5;
michael@0 159 mTriangle = 0.0;
michael@0 160 break;
michael@0 161 case OscillatorType::Sawtooth:
michael@0 162 // Initial mPhase so the oscillator starts at the
michael@0 163 // middle of the ramp, per spec.
michael@0 164 mPhase = (float)(M_PI / 2);
michael@0 165 // mSaw = 0 when mPhase = pi/2.
michael@0 166 mSaw = 0.0;
michael@0 167 break;
michael@0 168 case OscillatorType::Custom:
michael@0 169 // Custom waveforms don't use BLIT.
michael@0 170 break;
michael@0 171 default:
michael@0 172 NS_ERROR("Bad OscillatorNodeEngine type parameter.");
michael@0 173 }
michael@0 174 // End type switch.
michael@0 175 break;
michael@0 176 case PERIODICWAVE:
michael@0 177 MOZ_ASSERT(aParam >= 0, "negative custom array length");
michael@0 178 mCustomLength = static_cast<uint32_t>(aParam);
michael@0 179 break;
michael@0 180 default:
michael@0 181 NS_ERROR("Bad OscillatorNodeEngine Int32Parameter.");
michael@0 182 }
michael@0 183 // End index switch.
michael@0 184 }
michael@0 185
michael@0 186 virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
michael@0 187 {
michael@0 188 MOZ_ASSERT(mCustomLength, "Custom buffer sent before length");
michael@0 189 mCustom = aBuffer;
michael@0 190 MOZ_ASSERT(mCustom->GetChannels() == 2,
michael@0 191 "PeriodicWave should have sent two channels");
michael@0 192 mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(),
michael@0 193 mCustom->GetData(0), mCustom->GetData(1), mCustomLength);
michael@0 194 }
michael@0 195
michael@0 196 void IncrementPhase()
michael@0 197 {
michael@0 198 mPhase += mPhaseIncrement;
michael@0 199 if (mPhase > mPhaseWrap) {
michael@0 200 mPhase -= mPhaseWrap;
michael@0 201 }
michael@0 202 }
michael@0 203
michael@0 204 // Square and triangle are using a bipolar band-limited impulse train, saw is
michael@0 205 // using a normal band-limited impulse train.
michael@0 206 bool UsesBipolarBLIT() {
michael@0 207 return mType == OscillatorType::Square || mType == OscillatorType::Triangle;
michael@0 208 }
michael@0 209
michael@0 210 void UpdateParametersIfNeeded(TrackTicks ticks, size_t count)
michael@0 211 {
michael@0 212 double frequency, detune;
michael@0 213
michael@0 214 bool simpleFrequency = mFrequency.HasSimpleValue();
michael@0 215 bool simpleDetune = mDetune.HasSimpleValue();
michael@0 216
michael@0 217 // Shortcut if frequency-related AudioParam are not automated, and we
michael@0 218 // already have computed the frequency information and related parameters.
michael@0 219 if (simpleFrequency && simpleDetune && !mRecomputeParameters) {
michael@0 220 return;
michael@0 221 }
michael@0 222
michael@0 223 if (simpleFrequency) {
michael@0 224 frequency = mFrequency.GetValue();
michael@0 225 } else {
michael@0 226 frequency = mFrequency.GetValueAtTime(ticks, count);
michael@0 227 }
michael@0 228 if (simpleDetune) {
michael@0 229 detune = mDetune.GetValue();
michael@0 230 } else {
michael@0 231 detune = mDetune.GetValueAtTime(ticks, count);
michael@0 232 }
michael@0 233
michael@0 234 mFinalFrequency = frequency * pow(2., detune / 1200.);
michael@0 235 mRecomputeParameters = false;
michael@0 236
michael@0 237 // When using bipolar BLIT, we divide the signal period by two, because we
michael@0 238 // are using two BLIT out of phase.
michael@0 239 mSignalPeriod = UsesBipolarBLIT() ? 0.5 * mSource->SampleRate() / mFinalFrequency
michael@0 240 : mSource->SampleRate() / mFinalFrequency;
michael@0 241 // Wrap the phase accordingly:
michael@0 242 mPhaseWrap = UsesBipolarBLIT() || mType == OscillatorType::Sine ? 2 * M_PI
michael@0 243 : M_PI;
michael@0 244 // Even number of harmonics for bipolar blit, odd otherwise.
michael@0 245 mNumberOfHarmonics = UsesBipolarBLIT() ? 2 * floor(0.5 * mSignalPeriod)
michael@0 246 : 2 * floor(0.5 * mSignalPeriod) + 1;
michael@0 247 mPhaseIncrement = mType == OscillatorType::Sine ? 2 * M_PI / mSignalPeriod
michael@0 248 : M_PI / mSignalPeriod;
michael@0 249 mAmplitudeAtZero = mNumberOfHarmonics / mSignalPeriod;
michael@0 250 }
michael@0 251
michael@0 252 void FillBounds(float* output, TrackTicks ticks,
michael@0 253 uint32_t& start, uint32_t& end)
michael@0 254 {
michael@0 255 MOZ_ASSERT(output);
michael@0 256 static_assert(TrackTicks(WEBAUDIO_BLOCK_SIZE) < UINT_MAX,
michael@0 257 "WEBAUDIO_BLOCK_SIZE overflows interator bounds.");
michael@0 258 start = 0;
michael@0 259 if (ticks < mStart) {
michael@0 260 start = mStart - ticks;
michael@0 261 for (uint32_t i = 0; i < start; ++i) {
michael@0 262 output[i] = 0.0;
michael@0 263 }
michael@0 264 }
michael@0 265 end = WEBAUDIO_BLOCK_SIZE;
michael@0 266 if (ticks + end > mStop) {
michael@0 267 end = mStop - ticks;
michael@0 268 for (uint32_t i = end; i < WEBAUDIO_BLOCK_SIZE; ++i) {
michael@0 269 output[i] = 0.0;
michael@0 270 }
michael@0 271 }
michael@0 272 }
michael@0 273
michael@0 274 float BipolarBLIT()
michael@0 275 {
michael@0 276 float blit;
michael@0 277 float denom = sin(mPhase);
michael@0 278
michael@0 279 if (fabs(denom) < std::numeric_limits<float>::epsilon()) {
michael@0 280 if (mPhase < 0.1f || mPhase > 2 * M_PI - 0.1f) {
michael@0 281 blit = mAmplitudeAtZero;
michael@0 282 } else {
michael@0 283 blit = -mAmplitudeAtZero;
michael@0 284 }
michael@0 285 } else {
michael@0 286 blit = sin(mNumberOfHarmonics * mPhase);
michael@0 287 blit /= mSignalPeriod * denom;
michael@0 288 }
michael@0 289 return blit;
michael@0 290 }
michael@0 291
michael@0 292 float UnipolarBLIT()
michael@0 293 {
michael@0 294 float blit;
michael@0 295 float denom = sin(mPhase);
michael@0 296
michael@0 297 if (fabs(denom) <= std::numeric_limits<float>::epsilon()) {
michael@0 298 blit = mAmplitudeAtZero;
michael@0 299 } else {
michael@0 300 blit = sin(mNumberOfHarmonics * mPhase);
michael@0 301 blit /= mSignalPeriod * denom;
michael@0 302 }
michael@0 303
michael@0 304 return blit;
michael@0 305 }
michael@0 306
michael@0 307 void ComputeSine(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
michael@0 308 {
michael@0 309 for (uint32_t i = aStart; i < aEnd; ++i) {
michael@0 310 UpdateParametersIfNeeded(ticks, i);
michael@0 311
michael@0 312 aOutput[i] = sin(mPhase);
michael@0 313
michael@0 314 IncrementPhase();
michael@0 315 }
michael@0 316 }
michael@0 317
michael@0 318 void ComputeSquare(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
michael@0 319 {
michael@0 320 for (uint32_t i = aStart; i < aEnd; ++i) {
michael@0 321 UpdateParametersIfNeeded(ticks, i);
michael@0 322 // Integration to get us a square. It turns out we can have a
michael@0 323 // pure integrator here.
michael@0 324 mSquare = mSquare * sLeak + BipolarBLIT();
michael@0 325 aOutput[i] = mSquare;
michael@0 326 // maybe we want to apply a gain, the wg has not decided yet
michael@0 327 aOutput[i] *= 1.5;
michael@0 328 IncrementPhase();
michael@0 329 }
michael@0 330 }
michael@0 331
michael@0 332 void ComputeSawtooth(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
michael@0 333 {
michael@0 334 float dcoffset;
michael@0 335 for (uint32_t i = aStart; i < aEnd; ++i) {
michael@0 336 UpdateParametersIfNeeded(ticks, i);
michael@0 337 // DC offset so the Saw does not ramp up to infinity when integrating.
michael@0 338 dcoffset = mFinalFrequency / mSource->SampleRate();
michael@0 339 // Integrate and offset so we get mAmplitudeAtZero sawtooth. We have a
michael@0 340 // very low frequency component somewhere here, but I'm not sure where.
michael@0 341 mSaw = mSaw * sLeak + (UnipolarBLIT() - dcoffset);
michael@0 342 // reverse the saw so we are spec compliant
michael@0 343 aOutput[i] = -mSaw * 1.5;
michael@0 344
michael@0 345 IncrementPhase();
michael@0 346 }
michael@0 347 }
michael@0 348
michael@0 349 void ComputeTriangle(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
michael@0 350 {
michael@0 351 for (uint32_t i = aStart; i < aEnd; ++i) {
michael@0 352 UpdateParametersIfNeeded(ticks, i);
michael@0 353 // Integrate to get a square
michael@0 354 mSquare += BipolarBLIT();
michael@0 355 // Leaky integrate to get a triangle. We get too much dc offset if we don't
michael@0 356 // leaky integrate here.
michael@0 357 // C6 = k0 / period
michael@0 358 // (period is samplingrate / frequency, k0 = (PI/2)/(2*PI)) = 0.25
michael@0 359 float C6 = 0.25 / (mSource->SampleRate() / mFinalFrequency);
michael@0 360 mTriangle = mTriangle * sLeakTriangle + mSquare + C6;
michael@0 361 // DC Block, and scale back to [-1.0; 1.0]
michael@0 362 aOutput[i] = mDCBlocker.Process(mTriangle) / (mSignalPeriod/2) * 1.5;
michael@0 363
michael@0 364 IncrementPhase();
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 void ComputeCustom(float* aOutput,
michael@0 369 TrackTicks ticks,
michael@0 370 uint32_t aStart,
michael@0 371 uint32_t aEnd)
michael@0 372 {
michael@0 373 MOZ_ASSERT(mPeriodicWave, "No custom waveform data");
michael@0 374
michael@0 375 uint32_t periodicWaveSize = mPeriodicWave->periodicWaveSize();
michael@0 376 // Mask to wrap wave data indices into the range [0,periodicWaveSize).
michael@0 377 uint32_t indexMask = periodicWaveSize - 1;
michael@0 378 MOZ_ASSERT(periodicWaveSize && (periodicWaveSize & indexMask) == 0,
michael@0 379 "periodicWaveSize must be power of 2");
michael@0 380 float* higherWaveData = nullptr;
michael@0 381 float* lowerWaveData = nullptr;
michael@0 382 float tableInterpolationFactor;
michael@0 383 // Phase increment at frequency of 1 Hz.
michael@0 384 // mPhase runs [0,periodicWaveSize) here instead of [0,2*M_PI).
michael@0 385 float basePhaseIncrement =
michael@0 386 static_cast<float>(periodicWaveSize) / mSource->SampleRate();
michael@0 387
michael@0 388 for (uint32_t i = aStart; i < aEnd; ++i) {
michael@0 389 UpdateParametersIfNeeded(ticks, i);
michael@0 390 mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency,
michael@0 391 lowerWaveData,
michael@0 392 higherWaveData,
michael@0 393 tableInterpolationFactor);
michael@0 394 // Bilinear interpolation between adjacent samples in each table.
michael@0 395 float floorPhase = floorf(mPhase);
michael@0 396 uint32_t j1 = floorPhase;
michael@0 397 j1 &= indexMask;
michael@0 398 uint32_t j2 = j1 + 1;
michael@0 399 j2 &= indexMask;
michael@0 400
michael@0 401 float sampleInterpolationFactor = mPhase - floorPhase;
michael@0 402
michael@0 403 float lower = (1.0f - sampleInterpolationFactor) * lowerWaveData[j1] +
michael@0 404 sampleInterpolationFactor * lowerWaveData[j2];
michael@0 405 float higher = (1.0f - sampleInterpolationFactor) * higherWaveData[j1] +
michael@0 406 sampleInterpolationFactor * higherWaveData[j2];
michael@0 407 aOutput[i] = (1.0f - tableInterpolationFactor) * lower +
michael@0 408 tableInterpolationFactor * higher;
michael@0 409
michael@0 410 // Calculate next phase position from wrapped value j1 to avoid loss of
michael@0 411 // precision at large values.
michael@0 412 mPhase =
michael@0 413 j1 + sampleInterpolationFactor + basePhaseIncrement * mFinalFrequency;
michael@0 414 }
michael@0 415 }
michael@0 416
michael@0 417 void ComputeSilence(AudioChunk *aOutput)
michael@0 418 {
michael@0 419 aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
michael@0 420 }
michael@0 421
michael@0 422 virtual void ProcessBlock(AudioNodeStream* aStream,
michael@0 423 const AudioChunk& aInput,
michael@0 424 AudioChunk* aOutput,
michael@0 425 bool* aFinished) MOZ_OVERRIDE
michael@0 426 {
michael@0 427 MOZ_ASSERT(mSource == aStream, "Invalid source stream");
michael@0 428
michael@0 429 TrackTicks ticks = aStream->GetCurrentPosition();
michael@0 430 if (mStart == -1) {
michael@0 431 ComputeSilence(aOutput);
michael@0 432 return;
michael@0 433 }
michael@0 434
michael@0 435 if (ticks >= mStop) {
michael@0 436 // We've finished playing.
michael@0 437 ComputeSilence(aOutput);
michael@0 438 *aFinished = true;
michael@0 439 return;
michael@0 440 }
michael@0 441 if (ticks + WEBAUDIO_BLOCK_SIZE < mStart) {
michael@0 442 // We're not playing yet.
michael@0 443 ComputeSilence(aOutput);
michael@0 444 return;
michael@0 445 }
michael@0 446
michael@0 447 AllocateAudioBlock(1, aOutput);
michael@0 448 float* output = static_cast<float*>(
michael@0 449 const_cast<void*>(aOutput->mChannelData[0]));
michael@0 450
michael@0 451 uint32_t start, end;
michael@0 452 FillBounds(output, ticks, start, end);
michael@0 453
michael@0 454 // Synthesize the correct waveform.
michael@0 455 switch(mType) {
michael@0 456 case OscillatorType::Sine:
michael@0 457 ComputeSine(output, ticks, start, end);
michael@0 458 break;
michael@0 459 case OscillatorType::Square:
michael@0 460 ComputeSquare(output, ticks, start, end);
michael@0 461 break;
michael@0 462 case OscillatorType::Triangle:
michael@0 463 ComputeTriangle(output, ticks, start, end);
michael@0 464 break;
michael@0 465 case OscillatorType::Sawtooth:
michael@0 466 ComputeSawtooth(output, ticks, start, end);
michael@0 467 break;
michael@0 468 case OscillatorType::Custom:
michael@0 469 ComputeCustom(output, ticks, start, end);
michael@0 470 break;
michael@0 471 default:
michael@0 472 ComputeSilence(aOutput);
michael@0 473 };
michael@0 474
michael@0 475 }
michael@0 476
michael@0 477 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 478 {
michael@0 479 size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
michael@0 480
michael@0 481 // Not owned:
michael@0 482 // - mSource
michael@0 483 // - mDestination
michael@0 484 // - mFrequency (internal ref owned by node)
michael@0 485 // - mDetune (internal ref owned by node)
michael@0 486
michael@0 487 if (mCustom) {
michael@0 488 amount += mCustom->SizeOfIncludingThis(aMallocSizeOf);
michael@0 489 }
michael@0 490
michael@0 491 if (mPeriodicWave) {
michael@0 492 amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf);
michael@0 493 }
michael@0 494
michael@0 495 return amount;
michael@0 496 }
michael@0 497
michael@0 498 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
michael@0 499 {
michael@0 500 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 501 }
michael@0 502
michael@0 503 DCBlocker mDCBlocker;
michael@0 504 AudioNodeStream* mSource;
michael@0 505 AudioNodeStream* mDestination;
michael@0 506 TrackTicks mStart;
michael@0 507 TrackTicks mStop;
michael@0 508 AudioParamTimeline mFrequency;
michael@0 509 AudioParamTimeline mDetune;
michael@0 510 OscillatorType mType;
michael@0 511 float mPhase;
michael@0 512 float mFinalFrequency;
michael@0 513 uint32_t mNumberOfHarmonics;
michael@0 514 float mSignalPeriod;
michael@0 515 float mAmplitudeAtZero;
michael@0 516 float mPhaseIncrement;
michael@0 517 float mSquare;
michael@0 518 float mTriangle;
michael@0 519 float mSaw;
michael@0 520 float mPhaseWrap;
michael@0 521 bool mRecomputeParameters;
michael@0 522 nsRefPtr<ThreadSharedFloatArrayBufferList> mCustom;
michael@0 523 uint32_t mCustomLength;
michael@0 524 nsAutoPtr<WebCore::PeriodicWave> mPeriodicWave;
michael@0 525 };
michael@0 526
michael@0 527 OscillatorNode::OscillatorNode(AudioContext* aContext)
michael@0 528 : AudioNode(aContext,
michael@0 529 2,
michael@0 530 ChannelCountMode::Max,
michael@0 531 ChannelInterpretation::Speakers)
michael@0 532 , mType(OscillatorType::Sine)
michael@0 533 , mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
michael@0 534 SendFrequencyToStream, 440.0f))
michael@0 535 , mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
michael@0 536 SendDetuneToStream, 0.0f))
michael@0 537 , mStartCalled(false)
michael@0 538 , mStopped(false)
michael@0 539 {
michael@0 540 OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
michael@0 541 mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
michael@0 542 engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
michael@0 543 mStream->AddMainThreadListener(this);
michael@0 544 }
michael@0 545
michael@0 546 OscillatorNode::~OscillatorNode()
michael@0 547 {
michael@0 548 }
michael@0 549
michael@0 550 size_t
michael@0 551 OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 552 {
michael@0 553 size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
michael@0 554
michael@0 555 // For now only report if we know for sure that it's not shared.
michael@0 556 amount += mPeriodicWave->SizeOfExcludingThisIfNotShared(aMallocSizeOf);
michael@0 557 amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
michael@0 558 amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
michael@0 559 return amount;
michael@0 560 }
michael@0 561
michael@0 562 size_t
michael@0 563 OscillatorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
michael@0 564 {
michael@0 565 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
michael@0 566 }
michael@0 567
michael@0 568 JSObject*
michael@0 569 OscillatorNode::WrapObject(JSContext* aCx)
michael@0 570 {
michael@0 571 return OscillatorNodeBinding::Wrap(aCx, this);
michael@0 572 }
michael@0 573
michael@0 574 void
michael@0 575 OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
michael@0 576 {
michael@0 577 OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
michael@0 578 SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
michael@0 579 }
michael@0 580
michael@0 581 void
michael@0 582 OscillatorNode::SendDetuneToStream(AudioNode* aNode)
michael@0 583 {
michael@0 584 OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
michael@0 585 SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
michael@0 586 }
michael@0 587
michael@0 588 void
michael@0 589 OscillatorNode::SendTypeToStream()
michael@0 590 {
michael@0 591 if (mType == OscillatorType::Custom) {
michael@0 592 // The engine assumes we'll send the custom data before updating the type.
michael@0 593 SendPeriodicWaveToStream();
michael@0 594 }
michael@0 595 SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
michael@0 596 }
michael@0 597
michael@0 598 void OscillatorNode::SendPeriodicWaveToStream()
michael@0 599 {
michael@0 600 NS_ASSERTION(mType == OscillatorType::Custom,
michael@0 601 "Sending custom waveform to engine thread with non-custom type");
michael@0 602 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
michael@0 603 MOZ_ASSERT(ns, "Missing node stream.");
michael@0 604 MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
michael@0 605 SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE,
michael@0 606 mPeriodicWave->DataLength());
michael@0 607 nsRefPtr<ThreadSharedFloatArrayBufferList> data =
michael@0 608 mPeriodicWave->GetThreadSharedBuffer();
michael@0 609 ns->SetBuffer(data.forget());
michael@0 610 }
michael@0 611
michael@0 612 void
michael@0 613 OscillatorNode::Start(double aWhen, ErrorResult& aRv)
michael@0 614 {
michael@0 615 if (!WebAudioUtils::IsTimeValid(aWhen)) {
michael@0 616 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
michael@0 617 return;
michael@0 618 }
michael@0 619
michael@0 620 if (mStartCalled) {
michael@0 621 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 622 return;
michael@0 623 }
michael@0 624 mStartCalled = true;
michael@0 625
michael@0 626 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
michael@0 627 if (!ns) {
michael@0 628 // Nothing to play, or we're already dead for some reason
michael@0 629 return;
michael@0 630 }
michael@0 631
michael@0 632 // TODO: Perhaps we need to do more here.
michael@0 633 ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
michael@0 634 Context(), aWhen);
michael@0 635
michael@0 636 MarkActive();
michael@0 637 }
michael@0 638
michael@0 639 void
michael@0 640 OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
michael@0 641 {
michael@0 642 if (!WebAudioUtils::IsTimeValid(aWhen)) {
michael@0 643 aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
michael@0 644 return;
michael@0 645 }
michael@0 646
michael@0 647 if (!mStartCalled) {
michael@0 648 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
michael@0 649 return;
michael@0 650 }
michael@0 651
michael@0 652 AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
michael@0 653 if (!ns || !Context()) {
michael@0 654 // We've already stopped and had our stream shut down
michael@0 655 return;
michael@0 656 }
michael@0 657
michael@0 658 // TODO: Perhaps we need to do more here.
michael@0 659 ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
michael@0 660 Context(), std::max(0.0, aWhen));
michael@0 661 }
michael@0 662
michael@0 663 void
michael@0 664 OscillatorNode::NotifyMainThreadStateChanged()
michael@0 665 {
michael@0 666 if (mStream->IsFinished()) {
michael@0 667 class EndedEventDispatcher : public nsRunnable
michael@0 668 {
michael@0 669 public:
michael@0 670 explicit EndedEventDispatcher(OscillatorNode* aNode)
michael@0 671 : mNode(aNode) {}
michael@0 672 NS_IMETHODIMP Run()
michael@0 673 {
michael@0 674 // If it's not safe to run scripts right now, schedule this to run later
michael@0 675 if (!nsContentUtils::IsSafeToRunScript()) {
michael@0 676 nsContentUtils::AddScriptRunner(this);
michael@0 677 return NS_OK;
michael@0 678 }
michael@0 679
michael@0 680 mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
michael@0 681 return NS_OK;
michael@0 682 }
michael@0 683 private:
michael@0 684 nsRefPtr<OscillatorNode> mNode;
michael@0 685 };
michael@0 686 if (!mStopped) {
michael@0 687 // Only dispatch the ended event once
michael@0 688 NS_DispatchToMainThread(new EndedEventDispatcher(this));
michael@0 689 mStopped = true;
michael@0 690 }
michael@0 691
michael@0 692 // Drop the playing reference
michael@0 693 // Warning: The below line might delete this.
michael@0 694 MarkInactive();
michael@0 695 }
michael@0 696 }
michael@0 697
michael@0 698 }
michael@0 699 }
michael@0 700

mercurial