Bug 1113634 - Update mLastComputedValue in AudioEventTimeline when skipping events of same time; r?karlt
We need to update mLastComputedValue while processing events that occur at the
same time rather than just skipping over them.
MozReview-Commit-ID: LuxSK6PHFHv
--- a/dom/media/webaudio/AudioEventTimeline.cpp
+++ b/dom/media/webaudio/AudioEventTimeline.cpp
@@ -179,68 +179,74 @@ AudioEventTimeline::GetValuesAtTimeHelpe
next->mType == AudioTimelineEvent::SetValueCurve);
#endif
if (TimesEqual(aTime, TimeOf(*next))) {
mLastComputedValue = mComputedValue;
// Find the last event with the same time
while (eventIndex < mEvents.Length() - 1 &&
TimesEqual(aTime, TimeOf(mEvents[eventIndex + 1]))) {
+ mLastComputedValue = GetValueAtTimeOfEvent<TimeType>(&mEvents[eventIndex]);
++eventIndex;
}
+
timeMatchesEventIndex = true;
break;
}
previous = next;
}
if (timeMatchesEventIndex) {
// The time matches one of the events exactly.
MOZ_ASSERT(TimesEqual(aTime, TimeOf(mEvents[eventIndex])));
-
- switch (mEvents[eventIndex].mType) {
- case AudioTimelineEvent::SetTarget:
- // SetTarget nodes can be handled no matter what their next node is
- // (if they have one).
- // Follow the curve, without regard to the next event, starting at
- // the last value of the last event.
- mComputedValue =
- ExponentialApproach(TimeOf(mEvents[eventIndex]),
- mLastComputedValue, mEvents[eventIndex].mValue,
- mEvents[eventIndex].mTimeConstant, aTime);
- break;
- case AudioTimelineEvent::SetValueCurve:
- // SetValueCurve events can be handled no matter what their event
- // node is (if they have one)
- mComputedValue =
- ExtractValueFromCurve(TimeOf(mEvents[eventIndex]),
- mEvents[eventIndex].mCurve,
- mEvents[eventIndex].mCurveLength,
- mEvents[eventIndex].mDuration, aTime);
- break;
- default:
- // For other event types
- mComputedValue = mEvents[eventIndex].mValue;
- }
+ mComputedValue = GetValueAtTimeOfEvent<TimeType>(&mEvents[eventIndex]);
} else {
mComputedValue = GetValuesAtTimeHelperInternal(aTime, previous, next);
}
aBuffer[bufferIndex] = mComputedValue;
}
}
template void
AudioEventTimeline::GetValuesAtTimeHelper(double aTime, float* aBuffer,
const size_t aSize);
template void
AudioEventTimeline::GetValuesAtTimeHelper(int64_t aTime, float* aBuffer,
const size_t aSize);
template<class TimeType> float
+AudioEventTimeline::GetValueAtTimeOfEvent(const AudioTimelineEvent* aNext)
+{
+ TimeType time = aNext->template Time<TimeType>();
+ switch (aNext->mType) {
+ case AudioTimelineEvent::SetTarget:
+ // SetTarget nodes can be handled no matter what their next node is
+ // (if they have one).
+ // Follow the curve, without regard to the next event, starting at
+ // the last value of the last event.
+ return ExponentialApproach(time,
+ mLastComputedValue, aNext->mValue,
+ aNext->mTimeConstant, time);
+ break;
+ case AudioTimelineEvent::SetValueCurve:
+ // SetValueCurve events can be handled no matter what their event
+ // node is (if they have one)
+ return ExtractValueFromCurve(time,
+ aNext->mCurve,
+ aNext->mCurveLength,
+ aNext->mDuration, time);
+ break;
+ default:
+ // For other event types
+ return aNext->mValue;
+ }
+}
+
+template<class TimeType> float
AudioEventTimeline::GetValuesAtTimeHelperInternal(TimeType aTime,
const AudioTimelineEvent* aPrevious,
const AudioTimelineEvent* aNext)
{
// If the requested time is before all of the existing events
if (!aPrevious) {
return mValue;
}
--- a/dom/media/webaudio/AudioEventTimeline.h
+++ b/dom/media/webaudio/AudioEventTimeline.h
@@ -345,16 +345,19 @@ public:
}
}
private:
template<class TimeType>
void GetValuesAtTimeHelper(TimeType aTime, float* aBuffer, const size_t aSize);
template<class TimeType>
+ float GetValueAtTimeOfEvent(const AudioTimelineEvent* aNext);
+
+ template<class TimeType>
float GetValuesAtTimeHelperInternal(TimeType aTime,
const AudioTimelineEvent* aPrevious,
const AudioTimelineEvent* aNext);
const AudioTimelineEvent* GetPreviousEvent(double aTime) const;
static bool IsValid(double value)
{
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -84,16 +84,17 @@ tags=capturestream
[test_bug875221.html]
[test_bug875402.html]
[test_bug894150.html]
[test_bug956489.html]
[test_bug964376.html]
[test_bug966247.html]
tags=capturestream
[test_bug972678.html]
+[test_bug1113634.html]
[test_bug1118372.html]
[test_bug1027864.html]
[test_bug1056032.html]
skip-if = toolkit == 'android' # bug 1056706
[test_bug1255618.html]
[test_bug1267579.html]
[test_channelMergerNode.html]
[test_channelMergerNodeWithVolume.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_bug1113634.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test AudioParam.setTargetAtTime where the target time is the same as the time of a previous event</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">
+
+var V0 = 0.9;
+var V1 = 0.1;
+var T0 = 0;
+var TimeConstant = 0.1;
+
+var gTest = {
+ length: 2048,
+ numberOfChannels: 1,
+ createGraph: function(context) {
+ var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+ for (var i = 0; i < 2048; ++i) {
+ sourceBuffer.getChannelData(0)[i] = 1;
+ }
+
+ var source = context.createBufferSource();
+ source.buffer = sourceBuffer;
+
+ var gain = context.createGain();
+ gain.gain.setValueAtTime(V0, T0);
+ gain.gain.setTargetAtTime(V1, T0, TimeConstant);
+
+ source.connect(gain);
+
+ source.start(0);
+ return gain;
+ },
+ createExpectedBuffers: function(context) {
+ var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+ for (var i = 0; i < 2048; ++i) {
+ var t = i / context.sampleRate;
+ expectedBuffer.getChannelData(0)[i] = V1 + (V0 - V1) * Math.exp(-(t - T0) / TimeConstant);
+ }
+ return expectedBuffer;
+ },
+};
+
+runTest();
+
+</script>
+</pre>
+</body>
+</html>