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
This commit is contained in:
Karl Tomlinson 2013-10-15 13:10:02 +13:00
parent 1f3a3a581f
commit fa7c6ec5b6
3 changed files with 77 additions and 20 deletions

View File

@ -70,16 +70,13 @@ public:
, mDetune(0.f)
, mType(OscillatorType::Sine)
, mPhase(0.)
, mFinalFrequency(0.0)
, mNumberOfHarmonics(0)
, mSignalPeriod(0.0)
, mAmplitudeAtZero(0.0)
, mPhaseIncrement(0.0)
, mSquare(0.0)
, mTriangle(0.0)
, mSaw(0.0)
, mPhaseWrap(0.0)
, mRecomputeFrequency(true)
// 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)
, mCustomLength(0)
{
}
@ -101,7 +98,7 @@ public:
const AudioParamTimeline& aValue,
TrackRate aSampleRate) MOZ_OVERRIDE
{
mRecomputeFrequency = true;
mRecomputeParameters = true;
switch (aIndex) {
case FREQUENCY:
MOZ_ASSERT(mSource && mDestination);
@ -139,6 +136,7 @@ public:
mCustomLength = 0;
mCustom = nullptr;
mPeriodicWave = nullptr;
mRecomputeParameters = true;
}
// Update BLIT integrators with the new initial conditions.
switch (mType) {
@ -208,7 +206,7 @@ public:
return mType == OscillatorType::Square || mType == OscillatorType::Triangle;
}
void UpdateFrequencyIfNeeded(TrackTicks ticks, size_t count)
void UpdateParametersIfNeeded(TrackTicks ticks, size_t count)
{
double frequency, detune;
@ -217,7 +215,7 @@ public:
// Shortcut if frequency-related AudioParam are not automated, and we
// already have computed the frequency information and related parameters.
if (simpleFrequency && simpleDetune && !mRecomputeFrequency) {
if (simpleFrequency && simpleDetune && !mRecomputeParameters) {
return;
}
@ -233,7 +231,7 @@ public:
}
mFinalFrequency = frequency * pow(2., detune / 1200.);
mRecomputeFrequency = false;
mRecomputeParameters = false;
// When using bipolar BLIT, we divide the signal period by two, because we
// are using two BLIT out of phase.
@ -308,7 +306,7 @@ public:
void ComputeSine(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
{
for (uint32_t i = aStart; i < aEnd; ++i) {
UpdateFrequencyIfNeeded(ticks, i);
UpdateParametersIfNeeded(ticks, i);
aOutput[i] = sin(mPhase);
@ -319,7 +317,7 @@ public:
void ComputeSquare(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
{
for (uint32_t i = aStart; i < aEnd; ++i) {
UpdateFrequencyIfNeeded(ticks, i);
UpdateParametersIfNeeded(ticks, i);
// Integration to get us a square. It turns out we can have a
// pure integrator here.
mSquare += BipolarBLIT();
@ -334,7 +332,7 @@ public:
{
float dcoffset;
for (uint32_t i = aStart; i < aEnd; ++i) {
UpdateFrequencyIfNeeded(ticks, i);
UpdateParametersIfNeeded(ticks, i);
// 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
@ -350,7 +348,7 @@ public:
void ComputeTriangle(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
{
for (uint32_t i = aStart; i < aEnd; ++i) {
UpdateFrequencyIfNeeded(ticks, i);
UpdateParametersIfNeeded(ticks, i);
// Integrate to get a square
mSquare += BipolarBLIT();
// Leaky integrate to get a triangle. We get too much dc offset if we don't
@ -380,7 +378,7 @@ public:
float rate = 1.0 / mSource->SampleRate();
for (uint32_t i = aStart; i < aEnd; ++i) {
UpdateFrequencyIfNeeded(ticks, i);
UpdateParametersIfNeeded(ticks, i);
mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency,
lowerWaveData,
higherWaveData,
@ -484,7 +482,7 @@ public:
float mTriangle;
float mSaw;
float mPhaseWrap;
bool mRecomputeFrequency;
bool mRecomputeParameters;
nsRefPtr<ThreadSharedFloatArrayBufferList> mCustom;
uint32_t mCustomLength;
nsAutoPtr<WebCore::PeriodicWave> mPeriodicWave;

View File

@ -97,6 +97,7 @@ support-files =
[test_oscillatorNode.html]
[test_oscillatorNode2.html]
[test_oscillatorNodeStart.html]
[test_oscillatorTypeChange.html]
[test_pannerNode.html]
[test_pannerNodeAbove.html]
[test_pannerNodeChannelCount.html]

View File

@ -0,0 +1,58 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test OscillatorNode type change after it has started and triangle phase</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
const bufferSize = 1024;
function startTest() {
var ctx = new AudioContext();
var oscillator1 = ctx.createOscillator();
oscillator1.connect(ctx.destination);
oscillator1.start(0);
// Assuming the above Web Audio operations have already scheduled an event
// to run in stable state and start the graph thread, schedule a subsequent
// event to change the type of oscillator1.
SimpleTest.executeSoon(function() {
oscillator1.type = "triangle";
// Another triangle wave with -1 gain should cancel the first. This is
// starting at the same time as the type change, assuming that the phase
// is reset on type change. A negative frequency should achieve the same
// as the -1 gain but for bug 916285.
var oscillator2 = ctx.createOscillator();
oscillator2.type = "triangle";
oscillator2.start(0);
var processor = ctx.createScriptProcessor(bufferSize, 1, 0);
oscillator1.connect(processor);
var gain = ctx.createGain();
gain.gain.value = -1;
gain.connect(processor);
oscillator2.connect(gain);
processor.onaudioprocess = function(e) {
compareBuffers(e.inputBuffer.getChannelData(0),
new Float32Array(bufferSize));
e.target.onaudioprocess = null;
SimpleTest.finish();
}
});
};
startTest();
</script>
</pre>
</body>
</html>