--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -151,16 +151,19 @@ public:
bool BegunResampling()
{
return mBeginProcessing == -STREAM_TIME_MAX;
}
void UpdateResampler(int32_t aOutRate, uint32_t aChannels)
{
+ // Only work with non negative aOutRate
+ aOutRate = std::abs(aOutRate);
+
if (mResampler &&
(aChannels != mChannels ||
// If the resampler has begun, then it will have moved
// mBufferPosition to after the samples it has read, but it hasn't
// output its buffered samples. Keep using the resampler, even if
// the rates now match, so that this latent segment is output.
(aOutRate == mBufferSampleRate && !BegunResampling()))) {
speex_resampler_destroy(mResampler);
@@ -392,16 +395,17 @@ public:
* the buffer at aBufferOffset, and never takes more data than aBufferMax.
* This function knows when it needs to allocate the output buffer, and also
* optimizes the case where it can avoid memory allocations.
*/
void CopyFromBuffer(AudioBlock* aOutput,
uint32_t aChannels,
uint32_t* aOffsetWithinBlock,
StreamTime* aCurrentPosition,
+ uint32_t aBufferMin,
uint32_t aBufferMax)
{
MOZ_ASSERT(*aCurrentPosition < mStop);
uint32_t availableInOutput =
std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
mStop - *aCurrentPosition);
if (mResampler) {
CopyFromInputBufferWithResampling(aOutput, aChannels,
@@ -476,17 +480,17 @@ public:
} else {
playbackRate = mPlaybackRateTimeline.GetValueAtTime(aStreamPosition);
}
if (mDetuneTimeline.HasSimpleValue()) {
detune = mDetuneTimeline.GetValue();
} else {
detune = mDetuneTimeline.GetValueAtTime(aStreamPosition);
}
- if (playbackRate <= 0 || mozilla::IsNaN(playbackRate)) {
+ if (mozilla::IsNaN(playbackRate)) {
playbackRate = 1.0f;
}
detune = std::min(std::max(-1200.f, detune), 1200.f);
int32_t outRate = ComputeFinalOutSampleRate(playbackRate, detune);
UpdateResampler(outRate, aChannels);
}
@@ -503,49 +507,57 @@ public:
return;
}
StreamTime streamPosition = mDestination->GraphTimeToStreamTime(aFrom);
uint32_t channels = mBuffer ? mBuffer->GetChannels() : 0;
UpdateSampleRateIfNeeded(channels, streamPosition);
+ float playbackRate = mPlaybackRateTimeline.HasSimpleValue() ?
+ mPlaybackRateTimeline.GetValue() :
+ mPlaybackRateTimeline.GetValueAtTime(streamPosition);
uint32_t written = 0;
while (written < WEBAUDIO_BLOCK_SIZE) {
if (mStop != STREAM_TIME_MAX &&
streamPosition >= mStop) {
FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX);
continue;
}
if (streamPosition < mBeginProcessing) {
FillWithZeroes(aOutput, channels, &written, &streamPosition,
mBeginProcessing);
continue;
}
if (mLoop) {
// mLoopEnd can become less than mBufferPosition when a LOOPEND engine
// parameter is received after "loopend" is changed on the node or a
// new buffer with lower samplerate is set.
- if (mBufferPosition >= mLoopEnd) {
+ if (playbackRate >= 0 && mBufferPosition >= mLoopEnd) {
mBufferPosition = mLoopStart;
+ } else if (playbackRate < 0 && mBufferPosition <= mLoopStart) {
+ mBufferPosition = mLoopEnd;
}
- CopyFromBuffer(aOutput, channels, &written, &streamPosition, mLoopEnd);
+ CopyFromBuffer(aOutput, channels, &written, &streamPosition, mLoopStart, mLoopEnd);
} else {
- if (mBufferPosition < mBufferEnd || mRemainingResamplerTail) {
- CopyFromBuffer(aOutput, channels, &written, &streamPosition, mBufferEnd);
+ if ((playbackRate < 0 && mBufferPosition) ||
+ (playbackRate >= 0 && mBufferPosition < mBufferEnd) ||
+ mRemainingResamplerTail) {
+ CopyFromBuffer(aOutput, channels, &written, &streamPosition, 0, mBufferEnd);
} else {
FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX);
}
}
}
// We've finished if we've gone past mStop, or if we're past mDuration when
// looping is disabled.
if (streamPosition >= mStop ||
- (!mLoop && mBufferPosition >= mBufferEnd && !mRemainingResamplerTail)) {
+ (playbackRate >= 0 && !mLoop && mBufferPosition >= mBufferEnd && !mRemainingResamplerTail) ||
+ (playbackRate < 0 && !mLoop && !mBufferPosition && !mRemainingResamplerTail)) {
*aFinished = true;
}
}
bool IsActive() const override
{
// Whether buffer has been set and start() has been called.
return mBufferSampleRate != 0;