2013-08-19 11:53:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "OscillatorNode.h"
|
|
|
|
#include "AudioNodeEngine.h"
|
|
|
|
#include "AudioNodeStream.h"
|
|
|
|
#include "AudioDestinationNode.h"
|
|
|
|
#include "WebAudioUtils.h"
|
2013-08-28 15:39:26 -07:00
|
|
|
#include "blink/PeriodicWave.h"
|
2013-08-19 11:53:00 -07:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
2014-04-25 09:49:00 -07:00
|
|
|
NS_IMPL_CYCLE_COLLECTION_INHERITED(OscillatorNode, AudioNode,
|
|
|
|
mPeriodicWave, mFrequency, mDetune)
|
2013-08-19 11:53:00 -07:00
|
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
|
|
|
|
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
|
|
|
|
|
|
|
NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
|
|
|
|
NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
|
|
|
|
|
2014-01-10 03:40:05 -08:00
|
|
|
static const float sLeakTriangle = 0.995f;
|
|
|
|
static const float sLeak = 0.999f;
|
2013-09-17 10:36:00 -07:00
|
|
|
|
|
|
|
class DCBlocker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
// These are sane defauts when the initial mPhase is zero
|
|
|
|
DCBlocker(float aLastInput = 0.0f,
|
|
|
|
float aLastOutput = 0.0f,
|
|
|
|
float aPole = 0.995)
|
|
|
|
:mLastInput(aLastInput),
|
|
|
|
mLastOutput(aLastOutput),
|
|
|
|
mPole(aPole)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aPole > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline float Process(float aInput)
|
|
|
|
{
|
|
|
|
float out;
|
|
|
|
|
|
|
|
out = mLastOutput * mPole + aInput - mLastInput;
|
|
|
|
mLastOutput = out;
|
|
|
|
mLastInput = aInput;
|
|
|
|
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
float mLastInput;
|
|
|
|
float mLastOutput;
|
|
|
|
float mPole;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2013-08-19 11:53:00 -07:00
|
|
|
class OscillatorNodeEngine : public AudioNodeEngine
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
|
|
|
|
: AudioNodeEngine(aNode)
|
|
|
|
, mSource(nullptr)
|
|
|
|
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
|
2013-08-26 02:46:33 -07:00
|
|
|
, mStart(-1)
|
2013-08-19 11:53:00 -07:00
|
|
|
, mStop(TRACK_TICKS_MAX)
|
2013-08-19 11:58:00 -07:00
|
|
|
// Keep the default values in sync with OscillatorNode::OscillatorNode.
|
2013-08-19 11:53:00 -07:00
|
|
|
, mFrequency(440.f)
|
|
|
|
, mDetune(0.f)
|
|
|
|
, mType(OscillatorType::Sine)
|
2013-08-19 11:56:00 -07:00
|
|
|
, mPhase(0.)
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
// mSquare, mTriangle, and mSaw are not used for default type "sine".
|
|
|
|
// They are initialized if and when switching to the OscillatorTypes that
|
|
|
|
// use them.
|
|
|
|
// mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
|
|
|
|
// mPhaseIncrement, and mPhaseWrap are initialized in
|
|
|
|
// UpdateParametersIfNeeded() when mRecomputeParameters is set.
|
|
|
|
, mRecomputeParameters(true)
|
2013-08-28 15:39:26 -07:00
|
|
|
, mCustomLength(0)
|
2013-08-19 11:53:00 -07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetSourceStream(AudioNodeStream* aSource)
|
|
|
|
{
|
|
|
|
mSource = aSource;
|
|
|
|
}
|
|
|
|
|
|
|
|
enum Parameters {
|
|
|
|
FREQUENCY,
|
|
|
|
DETUNE,
|
|
|
|
TYPE,
|
|
|
|
PERIODICWAVE,
|
|
|
|
START,
|
|
|
|
STOP,
|
|
|
|
};
|
|
|
|
void SetTimelineParameter(uint32_t aIndex,
|
|
|
|
const AudioParamTimeline& aValue,
|
|
|
|
TrackRate aSampleRate) MOZ_OVERRIDE
|
|
|
|
{
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
mRecomputeParameters = true;
|
2013-08-19 11:53:00 -07:00
|
|
|
switch (aIndex) {
|
|
|
|
case FREQUENCY:
|
|
|
|
MOZ_ASSERT(mSource && mDestination);
|
|
|
|
mFrequency = aValue;
|
|
|
|
WebAudioUtils::ConvertAudioParamToTicks(mFrequency, mSource, mDestination);
|
|
|
|
break;
|
|
|
|
case DETUNE:
|
|
|
|
MOZ_ASSERT(mSource && mDestination);
|
|
|
|
mDetune = aValue;
|
|
|
|
WebAudioUtils::ConvertAudioParamToTicks(mDetune, mSource, mDestination);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
|
|
|
|
}
|
|
|
|
}
|
2013-09-17 10:36:00 -07:00
|
|
|
|
2013-08-19 11:53:00 -07:00
|
|
|
virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
|
|
|
|
{
|
|
|
|
switch (aIndex) {
|
|
|
|
case START: mStart = aParam; break;
|
|
|
|
case STOP: mStop = aParam; break;
|
|
|
|
default:
|
|
|
|
NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
|
|
|
|
}
|
|
|
|
}
|
2013-09-17 10:36:00 -07:00
|
|
|
|
2013-08-19 11:53:00 -07:00
|
|
|
virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
|
|
|
|
{
|
2013-08-28 15:39:26 -07:00
|
|
|
switch (aIndex) {
|
|
|
|
case TYPE:
|
|
|
|
// Set the new type.
|
|
|
|
mType = static_cast<OscillatorType>(aParam);
|
|
|
|
if (mType != OscillatorType::Custom) {
|
|
|
|
// Forget any previous custom data.
|
|
|
|
mCustomLength = 0;
|
|
|
|
mCustom = nullptr;
|
|
|
|
mPeriodicWave = nullptr;
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
mRecomputeParameters = true;
|
2013-08-28 15:39:26 -07:00
|
|
|
}
|
|
|
|
// Update BLIT integrators with the new initial conditions.
|
|
|
|
switch (mType) {
|
|
|
|
case OscillatorType::Sine:
|
|
|
|
mPhase = 0.0;
|
|
|
|
break;
|
|
|
|
case OscillatorType::Square:
|
|
|
|
mPhase = 0.0;
|
|
|
|
// Initial integration condition is -0.5, because our
|
|
|
|
// square has 50% duty cycle.
|
|
|
|
mSquare = -0.5;
|
|
|
|
break;
|
|
|
|
case OscillatorType::Triangle:
|
|
|
|
// Initial mPhase and related integration condition so the
|
|
|
|
// triangle is in the middle of the first upward slope.
|
|
|
|
// XXX actually do the maths and put the right number here.
|
|
|
|
mPhase = (float)(M_PI / 2);
|
|
|
|
mSquare = 0.5;
|
|
|
|
mTriangle = 0.0;
|
|
|
|
break;
|
|
|
|
case OscillatorType::Sawtooth:
|
|
|
|
// Initial mPhase so the oscillator starts at the
|
|
|
|
// middle of the ramp, per spec.
|
|
|
|
mPhase = (float)(M_PI / 2);
|
|
|
|
// mSaw = 0 when mPhase = pi/2.
|
|
|
|
mSaw = 0.0;
|
|
|
|
break;
|
|
|
|
case OscillatorType::Custom:
|
|
|
|
// Custom waveforms don't use BLIT.
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ERROR("Bad OscillatorNodeEngine type parameter.");
|
|
|
|
}
|
|
|
|
// End type switch.
|
2013-09-17 10:36:00 -07:00
|
|
|
break;
|
2013-08-28 15:39:26 -07:00
|
|
|
case PERIODICWAVE:
|
|
|
|
MOZ_ASSERT(aParam >= 0, "negative custom array length");
|
|
|
|
mCustomLength = static_cast<uint32_t>(aParam);
|
2013-09-17 10:36:00 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NS_ERROR("Bad OscillatorNodeEngine Int32Parameter.");
|
2013-08-28 15:39:26 -07:00
|
|
|
}
|
|
|
|
// End index switch.
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mCustomLength, "Custom buffer sent before length");
|
|
|
|
mCustom = aBuffer;
|
|
|
|
MOZ_ASSERT(mCustom->GetChannels() == 2,
|
|
|
|
"PeriodicWave should have sent two channels");
|
|
|
|
mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(),
|
|
|
|
mCustom->GetData(0), mCustom->GetData(1), mCustomLength);
|
2013-09-17 10:36:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void IncrementPhase()
|
|
|
|
{
|
|
|
|
mPhase += mPhaseIncrement;
|
|
|
|
if (mPhase > mPhaseWrap) {
|
|
|
|
mPhase -= mPhaseWrap;
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
// Square and triangle are using a bipolar band-limited impulse train, saw is
|
|
|
|
// using a normal band-limited impulse train.
|
|
|
|
bool UsesBipolarBLIT() {
|
|
|
|
return mType == OscillatorType::Square || mType == OscillatorType::Triangle;
|
|
|
|
}
|
|
|
|
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
void UpdateParametersIfNeeded(TrackTicks ticks, size_t count)
|
2013-08-19 11:56:00 -07:00
|
|
|
{
|
|
|
|
double frequency, detune;
|
2013-09-17 10:36:00 -07:00
|
|
|
|
|
|
|
bool simpleFrequency = mFrequency.HasSimpleValue();
|
|
|
|
bool simpleDetune = mDetune.HasSimpleValue();
|
|
|
|
|
|
|
|
// Shortcut if frequency-related AudioParam are not automated, and we
|
|
|
|
// already have computed the frequency information and related parameters.
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
if (simpleFrequency && simpleDetune && !mRecomputeParameters) {
|
2013-09-17 10:36:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (simpleFrequency) {
|
2013-08-19 11:56:00 -07:00
|
|
|
frequency = mFrequency.GetValue();
|
|
|
|
} else {
|
|
|
|
frequency = mFrequency.GetValueAtTime(ticks, count);
|
|
|
|
}
|
2013-09-17 10:36:00 -07:00
|
|
|
if (simpleDetune) {
|
2013-08-19 11:56:00 -07:00
|
|
|
detune = mDetune.GetValue();
|
|
|
|
} else {
|
|
|
|
detune = mDetune.GetValueAtTime(ticks, count);
|
|
|
|
}
|
2013-09-17 10:36:00 -07:00
|
|
|
|
|
|
|
mFinalFrequency = frequency * pow(2., detune / 1200.);
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
mRecomputeParameters = false;
|
2013-09-17 10:36:00 -07:00
|
|
|
|
|
|
|
// When using bipolar BLIT, we divide the signal period by two, because we
|
|
|
|
// are using two BLIT out of phase.
|
|
|
|
mSignalPeriod = UsesBipolarBLIT() ? 0.5 * mSource->SampleRate() / mFinalFrequency
|
|
|
|
: mSource->SampleRate() / mFinalFrequency;
|
|
|
|
// Wrap the phase accordingly:
|
|
|
|
mPhaseWrap = UsesBipolarBLIT() || mType == OscillatorType::Sine ? 2 * M_PI
|
|
|
|
: M_PI;
|
|
|
|
// Even number of harmonics for bipolar blit, odd otherwise.
|
|
|
|
mNumberOfHarmonics = UsesBipolarBLIT() ? 2 * floor(0.5 * mSignalPeriod)
|
|
|
|
: 2 * floor(0.5 * mSignalPeriod) + 1;
|
|
|
|
mPhaseIncrement = mType == OscillatorType::Sine ? 2 * M_PI / mSignalPeriod
|
|
|
|
: M_PI / mSignalPeriod;
|
|
|
|
mAmplitudeAtZero = mNumberOfHarmonics / mSignalPeriod;
|
2013-08-19 11:56:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void FillBounds(float* output, TrackTicks ticks,
|
|
|
|
uint32_t& start, uint32_t& end)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(output);
|
|
|
|
static_assert(TrackTicks(WEBAUDIO_BLOCK_SIZE) < UINT_MAX,
|
|
|
|
"WEBAUDIO_BLOCK_SIZE overflows interator bounds.");
|
|
|
|
start = 0;
|
|
|
|
if (ticks < mStart) {
|
|
|
|
start = mStart - ticks;
|
|
|
|
for (uint32_t i = 0; i < start; ++i) {
|
|
|
|
output[i] = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end = WEBAUDIO_BLOCK_SIZE;
|
|
|
|
if (ticks + end > mStop) {
|
|
|
|
end = mStop - ticks;
|
|
|
|
for (uint32_t i = end; i < WEBAUDIO_BLOCK_SIZE; ++i) {
|
|
|
|
output[i] = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
float BipolarBLIT()
|
2013-08-19 11:56:00 -07:00
|
|
|
{
|
2013-09-17 10:36:00 -07:00
|
|
|
float blit;
|
|
|
|
float denom = sin(mPhase);
|
2013-09-17 09:14:58 -07:00
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
if (fabs(denom) < std::numeric_limits<float>::epsilon()) {
|
|
|
|
if (mPhase < 0.1f || mPhase > 2 * M_PI - 0.1f) {
|
|
|
|
blit = mAmplitudeAtZero;
|
|
|
|
} else {
|
|
|
|
blit = -mAmplitudeAtZero;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
blit = sin(mNumberOfHarmonics * mPhase);
|
|
|
|
blit /= mSignalPeriod * denom;
|
2013-08-19 11:56:00 -07:00
|
|
|
}
|
2013-09-17 10:36:00 -07:00
|
|
|
return blit;
|
2013-08-19 11:56:00 -07:00
|
|
|
}
|
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
float UnipolarBLIT()
|
2013-08-19 11:56:00 -07:00
|
|
|
{
|
2013-09-17 10:36:00 -07:00
|
|
|
float blit;
|
|
|
|
float denom = sin(mPhase);
|
2013-08-19 11:56:00 -07:00
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
if (fabs(denom) <= std::numeric_limits<float>::epsilon()) {
|
|
|
|
blit = mAmplitudeAtZero;
|
|
|
|
} else {
|
|
|
|
blit = sin(mNumberOfHarmonics * mPhase);
|
|
|
|
blit /= mSignalPeriod * denom;
|
2013-09-17 09:14:58 -07:00
|
|
|
}
|
2013-09-17 10:36:00 -07:00
|
|
|
|
|
|
|
return blit;
|
2013-08-19 11:56:00 -07:00
|
|
|
}
|
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
void ComputeSine(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
2013-08-19 11:56:00 -07:00
|
|
|
{
|
2013-09-17 10:36:00 -07:00
|
|
|
for (uint32_t i = aStart; i < aEnd; ++i) {
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
UpdateParametersIfNeeded(ticks, i);
|
2013-08-19 11:56:00 -07:00
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
aOutput[i] = sin(mPhase);
|
2013-08-19 11:56:00 -07:00
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
IncrementPhase();
|
2013-08-19 11:56:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
void ComputeSquare(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
2013-08-19 11:56:00 -07:00
|
|
|
{
|
2013-09-17 10:36:00 -07:00
|
|
|
for (uint32_t i = aStart; i < aEnd; ++i) {
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
UpdateParametersIfNeeded(ticks, i);
|
2013-09-17 10:36:00 -07:00
|
|
|
// Integration to get us a square. It turns out we can have a
|
|
|
|
// pure integrator here.
|
2014-01-10 03:40:05 -08:00
|
|
|
mSquare = mSquare * sLeak + BipolarBLIT();
|
2013-09-17 10:36:00 -07:00
|
|
|
aOutput[i] = mSquare;
|
|
|
|
// maybe we want to apply a gain, the wg has not decided yet
|
|
|
|
aOutput[i] *= 1.5;
|
|
|
|
IncrementPhase();
|
|
|
|
}
|
|
|
|
}
|
2013-08-19 11:56:00 -07:00
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
void ComputeSawtooth(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
|
|
|
{
|
|
|
|
float dcoffset;
|
|
|
|
for (uint32_t i = aStart; i < aEnd; ++i) {
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
UpdateParametersIfNeeded(ticks, i);
|
2013-09-17 10:36:00 -07:00
|
|
|
// DC offset so the Saw does not ramp up to infinity when integrating.
|
|
|
|
dcoffset = mFinalFrequency / mSource->SampleRate();
|
|
|
|
// Integrate and offset so we get mAmplitudeAtZero sawtooth. We have a
|
|
|
|
// very low frequency component somewhere here, but I'm not sure where.
|
2014-01-10 03:40:05 -08:00
|
|
|
mSaw = mSaw * sLeak + (UnipolarBLIT() - dcoffset);
|
2013-09-17 10:36:00 -07:00
|
|
|
// reverse the saw so we are spec compliant
|
|
|
|
aOutput[i] = -mSaw * 1.5;
|
|
|
|
|
|
|
|
IncrementPhase();
|
|
|
|
}
|
|
|
|
}
|
2013-08-19 11:56:00 -07:00
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
void ComputeTriangle(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
|
|
|
{
|
|
|
|
for (uint32_t i = aStart; i < aEnd; ++i) {
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
UpdateParametersIfNeeded(ticks, i);
|
2013-09-17 10:36:00 -07:00
|
|
|
// Integrate to get a square
|
|
|
|
mSquare += BipolarBLIT();
|
|
|
|
// Leaky integrate to get a triangle. We get too much dc offset if we don't
|
|
|
|
// leaky integrate here.
|
|
|
|
// C6 = k0 / period
|
|
|
|
// (period is samplingrate / frequency, k0 = (PI/2)/(2*PI)) = 0.25
|
|
|
|
float C6 = 0.25 / (mSource->SampleRate() / mFinalFrequency);
|
2014-01-10 03:40:05 -08:00
|
|
|
mTriangle = mTriangle * sLeakTriangle + mSquare + C6;
|
2013-09-17 10:36:00 -07:00
|
|
|
// DC Block, and scale back to [-1.0; 1.0]
|
|
|
|
aOutput[i] = mDCBlocker.Process(mTriangle) / (mSignalPeriod/2) * 1.5;
|
|
|
|
|
|
|
|
IncrementPhase();
|
2013-08-19 11:56:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-28 15:39:26 -07:00
|
|
|
void ComputeCustom(float* aOutput,
|
|
|
|
TrackTicks ticks,
|
|
|
|
uint32_t aStart,
|
|
|
|
uint32_t aEnd)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mPeriodicWave, "No custom waveform data");
|
|
|
|
|
|
|
|
uint32_t periodicWaveSize = mPeriodicWave->periodicWaveSize();
|
|
|
|
float* higherWaveData = nullptr;
|
|
|
|
float* lowerWaveData = nullptr;
|
|
|
|
float tableInterpolationFactor;
|
|
|
|
float rate = 1.0 / mSource->SampleRate();
|
|
|
|
|
|
|
|
for (uint32_t i = aStart; i < aEnd; ++i) {
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
UpdateParametersIfNeeded(ticks, i);
|
2013-08-28 15:39:26 -07:00
|
|
|
mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency,
|
|
|
|
lowerWaveData,
|
|
|
|
higherWaveData,
|
|
|
|
tableInterpolationFactor);
|
|
|
|
// mPhase runs 0..periodicWaveSize here instead of 0..2*M_PI.
|
|
|
|
mPhase += periodicWaveSize * mFinalFrequency * rate;
|
2014-04-14 19:12:13 -07:00
|
|
|
mPhase = fmod(mPhase, periodicWaveSize);
|
2013-08-28 15:39:26 -07:00
|
|
|
// Bilinear interpolation between adjacent samples in each table.
|
|
|
|
uint32_t j1 = floor(mPhase);
|
|
|
|
uint32_t j2 = j1 + 1;
|
|
|
|
if (j2 >= periodicWaveSize) {
|
|
|
|
j2 -= periodicWaveSize;
|
|
|
|
}
|
|
|
|
float sampleInterpolationFactor = mPhase - j1;
|
|
|
|
float lower = sampleInterpolationFactor * lowerWaveData[j1] +
|
|
|
|
(1 - sampleInterpolationFactor) * lowerWaveData[j2];
|
|
|
|
float higher = sampleInterpolationFactor * higherWaveData[j1] +
|
|
|
|
(1 - sampleInterpolationFactor) * higherWaveData[j2];
|
|
|
|
aOutput[i] = tableInterpolationFactor * lower +
|
|
|
|
(1 - tableInterpolationFactor) * higher;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-19 11:56:00 -07:00
|
|
|
void ComputeSilence(AudioChunk *aOutput)
|
|
|
|
{
|
|
|
|
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
|
|
|
|
}
|
|
|
|
|
2014-03-04 13:09:49 -08:00
|
|
|
virtual void ProcessBlock(AudioNodeStream* aStream,
|
|
|
|
const AudioChunk& aInput,
|
|
|
|
AudioChunk* aOutput,
|
|
|
|
bool* aFinished) MOZ_OVERRIDE
|
2013-08-19 11:53:00 -07:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mSource == aStream, "Invalid source stream");
|
|
|
|
|
2013-08-19 11:56:00 -07:00
|
|
|
TrackTicks ticks = aStream->GetCurrentPosition();
|
2013-08-26 02:46:33 -07:00
|
|
|
if (mStart == -1) {
|
|
|
|
ComputeSilence(aOutput);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-19 11:56:00 -07:00
|
|
|
if (ticks >= mStop) {
|
|
|
|
// We've finished playing.
|
|
|
|
ComputeSilence(aOutput);
|
|
|
|
*aFinished = true;
|
|
|
|
return;
|
|
|
|
}
|
2013-09-23 18:48:35 -07:00
|
|
|
if (ticks + WEBAUDIO_BLOCK_SIZE < mStart) {
|
|
|
|
// We're not playing yet.
|
|
|
|
ComputeSilence(aOutput);
|
|
|
|
return;
|
|
|
|
}
|
2013-09-17 10:36:00 -07:00
|
|
|
|
|
|
|
AllocateAudioBlock(1, aOutput);
|
|
|
|
float* output = static_cast<float*>(
|
|
|
|
const_cast<void*>(aOutput->mChannelData[0]));
|
|
|
|
|
|
|
|
uint32_t start, end;
|
|
|
|
FillBounds(output, ticks, start, end);
|
|
|
|
|
2013-08-19 11:56:00 -07:00
|
|
|
// Synthesize the correct waveform.
|
2013-09-17 10:36:00 -07:00
|
|
|
switch(mType) {
|
2013-08-19 11:56:00 -07:00
|
|
|
case OscillatorType::Sine:
|
2013-09-17 10:36:00 -07:00
|
|
|
ComputeSine(output, ticks, start, end);
|
2013-08-19 11:56:00 -07:00
|
|
|
break;
|
|
|
|
case OscillatorType::Square:
|
2013-09-17 10:36:00 -07:00
|
|
|
ComputeSquare(output, ticks, start, end);
|
2013-09-17 09:14:58 -07:00
|
|
|
break;
|
|
|
|
case OscillatorType::Triangle:
|
2013-09-17 10:36:00 -07:00
|
|
|
ComputeTriangle(output, ticks, start, end);
|
|
|
|
break;
|
|
|
|
case OscillatorType::Sawtooth:
|
|
|
|
ComputeSawtooth(output, ticks, start, end);
|
2013-08-19 11:56:00 -07:00
|
|
|
break;
|
2013-08-28 15:39:26 -07:00
|
|
|
case OscillatorType::Custom:
|
|
|
|
ComputeCustom(output, ticks, start, end);
|
|
|
|
break;
|
2013-08-19 11:56:00 -07:00
|
|
|
default:
|
|
|
|
ComputeSilence(aOutput);
|
2013-09-17 10:36:00 -07:00
|
|
|
};
|
|
|
|
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
|
2014-04-13 11:08:10 -07:00
|
|
|
virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
|
|
|
|
// Not owned:
|
|
|
|
// - mSource
|
|
|
|
// - mDestination
|
|
|
|
// - mFrequency (internal ref owned by node)
|
|
|
|
// - mDetune (internal ref owned by node)
|
|
|
|
|
|
|
|
if (mCustom) {
|
|
|
|
amount += mCustom->SizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mPeriodicWave) {
|
|
|
|
amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
|
|
|
|
{
|
|
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
2013-09-17 10:36:00 -07:00
|
|
|
DCBlocker mDCBlocker;
|
2013-08-19 11:53:00 -07:00
|
|
|
AudioNodeStream* mSource;
|
|
|
|
AudioNodeStream* mDestination;
|
|
|
|
TrackTicks mStart;
|
|
|
|
TrackTicks mStop;
|
|
|
|
AudioParamTimeline mFrequency;
|
|
|
|
AudioParamTimeline mDetune;
|
|
|
|
OscillatorType mType;
|
2013-09-17 10:36:00 -07:00
|
|
|
float mPhase;
|
|
|
|
float mFinalFrequency;
|
|
|
|
uint32_t mNumberOfHarmonics;
|
|
|
|
float mSignalPeriod;
|
|
|
|
float mAmplitudeAtZero;
|
|
|
|
float mPhaseIncrement;
|
|
|
|
float mSquare;
|
|
|
|
float mTriangle;
|
|
|
|
float mSaw;
|
|
|
|
float mPhaseWrap;
|
b=923106 recompute frequency dependent parameters when OscillatorType changes r=padenot
mSquare, mTriangle, and mSaw are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized when switching to
OscillatorType::Sine. These parameters are initialized if and when switching
to the OscillatorTypes that use them.
mFinalFrequency, mNumberOfHarmonics, mSignalPeriod, mAmplitudeAtZero,
mPhaseIncrement, mPhaseWrap are not initialized in the OscillatorNodeEngine
constructor, just like they are not initialized immediately on switching
OscillatorType. These parameters are initialized in
UpdateParametersIfNeeded(), conditional on mRecomputeParameters.
--HG--
extra : transplant_source : a%C1%83%03%03%5CJ%88%99j%A0%93%05%92%06%CA%D6%8B%00%21
2013-10-14 17:10:02 -07:00
|
|
|
bool mRecomputeParameters;
|
2013-08-28 15:39:26 -07:00
|
|
|
nsRefPtr<ThreadSharedFloatArrayBufferList> mCustom;
|
|
|
|
uint32_t mCustomLength;
|
|
|
|
nsAutoPtr<WebCore::PeriodicWave> mPeriodicWave;
|
2013-08-19 11:53:00 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
OscillatorNode::OscillatorNode(AudioContext* aContext)
|
|
|
|
: AudioNode(aContext,
|
|
|
|
2,
|
|
|
|
ChannelCountMode::Max,
|
|
|
|
ChannelInterpretation::Speakers)
|
|
|
|
, mType(OscillatorType::Sine)
|
|
|
|
, mFrequency(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
|
|
|
|
SendFrequencyToStream, 440.0f))
|
|
|
|
, mDetune(new AudioParam(MOZ_THIS_IN_INITIALIZER_LIST(),
|
|
|
|
SendDetuneToStream, 0.0f))
|
|
|
|
, mStartCalled(false)
|
|
|
|
, mStopped(false)
|
|
|
|
{
|
|
|
|
OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
|
|
|
|
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
|
|
|
|
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
|
2013-09-23 18:47:30 -07:00
|
|
|
mStream->AddMainThreadListener(this);
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
|
2013-08-19 11:57:00 -07:00
|
|
|
OscillatorNode::~OscillatorNode()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2014-04-13 11:08:10 -07:00
|
|
|
size_t
|
|
|
|
OscillatorNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
|
|
|
|
{
|
|
|
|
size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
|
|
|
|
// For now only report if we know for sure that it's not shared.
|
|
|
|
amount += mPeriodicWave->SizeOfExcludingThisIfNotShared(aMallocSizeOf);
|
|
|
|
amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
amount += mDetune->SizeOfIncludingThis(aMallocSizeOf);
|
|
|
|
return amount;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
OscillatorNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
|
|
|
|
{
|
|
|
|
return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
|
|
|
|
}
|
|
|
|
|
2013-08-19 11:53:00 -07:00
|
|
|
JSObject*
|
2014-04-08 15:27:18 -07:00
|
|
|
OscillatorNode::WrapObject(JSContext* aCx)
|
2013-08-19 11:53:00 -07:00
|
|
|
{
|
Bug 991742 part 6. Remove the "aScope" argument of binding Wrap() methods. r=bholley
This patch was mostly generated with this command:
find . -name "*.h" -o -name "*.cpp" | xargs sed -e 's/Binding::Wrap(aCx, aScope, this/Binding::Wrap(aCx, this/' -e 's/Binding_workers::Wrap(aCx, aScope, this/Binding_workers::Wrap(aCx, this/' -e 's/Binding::Wrap(cx, scope, this/Binding::Wrap(cx, this/' -i ""
plus a few manual fixes to dom/bindings/Codegen.py, js/xpconnect/src/event_impl_gen.py, and a few C++ files that were not caught in the search-and-replace above.
2014-04-08 15:27:17 -07:00
|
|
|
return OscillatorNodeBinding::Wrap(aCx, this);
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
|
|
|
|
{
|
|
|
|
OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
|
|
|
|
SendTimelineParameterToStream(This, OscillatorNodeEngine::FREQUENCY, *This->mFrequency);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OscillatorNode::SendDetuneToStream(AudioNode* aNode)
|
|
|
|
{
|
|
|
|
OscillatorNode* This = static_cast<OscillatorNode*>(aNode);
|
|
|
|
SendTimelineParameterToStream(This, OscillatorNodeEngine::DETUNE, *This->mDetune);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OscillatorNode::SendTypeToStream()
|
|
|
|
{
|
|
|
|
if (mType == OscillatorType::Custom) {
|
2013-08-28 15:39:26 -07:00
|
|
|
// The engine assumes we'll send the custom data before updating the type.
|
|
|
|
SendPeriodicWaveToStream();
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
2013-08-28 15:39:26 -07:00
|
|
|
SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
|
|
|
|
}
|
|
|
|
|
|
|
|
void OscillatorNode::SendPeriodicWaveToStream()
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mType == OscillatorType::Custom,
|
|
|
|
"Sending custom waveform to engine thread with non-custom type");
|
|
|
|
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
|
|
|
MOZ_ASSERT(ns, "Missing node stream.");
|
|
|
|
MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
|
|
|
|
SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE,
|
|
|
|
mPeriodicWave->DataLength());
|
|
|
|
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
|
|
|
|
mPeriodicWave->GetThreadSharedBuffer();
|
|
|
|
ns->SetBuffer(data.forget());
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OscillatorNode::Start(double aWhen, ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (!WebAudioUtils::IsTimeValid(aWhen)) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mStartCalled) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mStartCalled = true;
|
|
|
|
|
|
|
|
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
|
|
|
if (!ns) {
|
|
|
|
// Nothing to play, or we're already dead for some reason
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Perhaps we need to do more here.
|
|
|
|
ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
|
2014-01-15 03:08:20 -08:00
|
|
|
Context(), aWhen);
|
2013-08-19 11:53:00 -07:00
|
|
|
|
2013-09-23 18:45:41 -07:00
|
|
|
MarkActive();
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (!WebAudioUtils::IsTimeValid(aWhen)) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mStartCalled) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
|
|
|
if (!ns || !Context()) {
|
|
|
|
// We've already stopped and had our stream shut down
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Perhaps we need to do more here.
|
|
|
|
ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
|
2014-01-15 03:08:20 -08:00
|
|
|
Context(), std::max(0.0, aWhen));
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
OscillatorNode::NotifyMainThreadStateChanged()
|
|
|
|
{
|
|
|
|
if (mStream->IsFinished()) {
|
|
|
|
class EndedEventDispatcher : public nsRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit EndedEventDispatcher(OscillatorNode* aNode)
|
|
|
|
: mNode(aNode) {}
|
|
|
|
NS_IMETHODIMP Run()
|
|
|
|
{
|
|
|
|
// If it's not safe to run scripts right now, schedule this to run later
|
|
|
|
if (!nsContentUtils::IsSafeToRunScript()) {
|
|
|
|
nsContentUtils::AddScriptRunner(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
mNode->DispatchTrustedEvent(NS_LITERAL_STRING("ended"));
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
nsRefPtr<OscillatorNode> mNode;
|
|
|
|
};
|
|
|
|
if (!mStopped) {
|
|
|
|
// Only dispatch the ended event once
|
|
|
|
NS_DispatchToMainThread(new EndedEventDispatcher(this));
|
|
|
|
mStopped = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Drop the playing reference
|
|
|
|
// Warning: The below line might delete this.
|
2013-09-23 18:45:41 -07:00
|
|
|
MarkInactive();
|
2013-08-19 11:53:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|