Bug 1308438 - Part 4: Add parameter aBufferMin to CopyFromInputBufferWithResampling and modify the function to work with negative playback rate. r?dminor, r?padenot
MozReview-Commit-ID: 3tAifLbFdPO
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -261,26 +261,34 @@ public:
// The number of frames consumed/produced depends on the amount of space
// remaining in both the input and output buffer, and the playback rate (that
// is, the ratio between the output samplerate and the input samplerate).
void CopyFromInputBufferWithResampling(AudioBlock* aOutput,
uint32_t aChannels,
uint32_t* aOffsetWithinBlock,
uint32_t aAvailableInOutput,
StreamTime* aCurrentPosition,
+ uint32_t aBufferMin,
uint32_t aBufferMax)
{
if (*aOffsetWithinBlock == 0) {
aOutput->AllocateChannels(aChannels);
}
SpeexResamplerState* resampler = mResampler;
MOZ_ASSERT(aChannels > 0);
- if (mBufferPosition < aBufferMax) {
- uint32_t availableInInputBuffer = aBufferMax - mBufferPosition;
+ float playbackRate = mPlaybackRateTimeline.HasSimpleValue() ?
+ mPlaybackRateTimeline.GetValue() :
+ mPlaybackRateTimeline.GetValueAtTime(*aCurrentPosition);
+
+ if ((playbackRate >= 0 && mBufferPosition < aBufferMax) ||
+ (playbackRate < 0 && mBufferPosition)) {
+ uint32_t availableInInputBuffer = playbackRate >= 0 ?
+ aBufferMax - mBufferPosition :
+ mBufferPosition - aBufferMin;
uint32_t ratioNum, ratioDen;
speex_resampler_get_ratio(resampler, &ratioNum, &ratioDen);
// Limit the number of input samples copied and possibly
// format-converted for resampling by estimating how many will be used.
// This may be a little small if still filling the resampler with
// initial data, but we'll get called again and it will work out.
uint32_t inputLimit = aAvailableInOutput * ratioNum / ratioDen + 10;
if (!BegunResampling()) {
@@ -302,30 +310,47 @@ public:
skipFracNum -= leadSubsamples;
}
speex_resampler_set_skip_frac_num(resampler,
std::min<int64_t>(skipFracNum, UINT32_MAX));
mBeginProcessing = -STREAM_TIME_MAX;
}
inputLimit = std::min(inputLimit, availableInInputBuffer);
+ inputLimit = std::min(inputLimit, WEBAUDIO_BLOCK_SIZE);
+ float bufferDataReversed[WEBAUDIO_BLOCK_SIZE];
for (uint32_t i = 0; true; ) {
uint32_t inSamples = inputLimit;
- const float* inputData = mBuffer->GetData(i) + mBufferPosition;
+ const float* inputData;
+ if (playbackRate >= 0) {
+ inputData = mBuffer->GetData(i) + mBufferPosition;
+ } else {
+ const float* bufferData = mBuffer->GetData(i);
+ for (uint32_t i = 0; i < inSamples; ++i) {
+ bufferDataReversed[i] = bufferData[mBufferPosition - i - 1];
+ }
+ inputData = bufferDataReversed;
+ }
+
uint32_t outSamples = aAvailableInOutput;
float* outputData =
aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
WebAudioUtils::SpeexResamplerProcess(resampler, i,
inputData, &inSamples,
outputData, &outSamples);
if (++i == aChannels) {
- mBufferPosition += inSamples;
+ if (playbackRate >= 0) {
+ mBufferPosition += inSamples;
+ } else {
+ MOZ_ASSERT(inSamples <= mBufferPosition);
+ mBufferPosition -= inSamples;
+ }
MOZ_ASSERT(mBufferPosition <= mBufferEnd || mLoop);
*aOffsetWithinBlock += outSamples;
*aCurrentPosition += outSamples;
if (inSamples == availableInInputBuffer && !mLoop) {
// We'll feed in enough zeros to empty out the resampler's memory.
// This handles the output latency as well as capturing the low
// pass effects of the resample filter.
mRemainingResamplerTail =
@@ -405,17 +430,17 @@ public:
{
MOZ_ASSERT(*aCurrentPosition < mStop);
uint32_t availableInOutput =
std::min<StreamTime>(WEBAUDIO_BLOCK_SIZE - *aOffsetWithinBlock,
mStop - *aCurrentPosition);
if (mResampler) {
CopyFromInputBufferWithResampling(aOutput, aChannels,
aOffsetWithinBlock, availableInOutput,
- aCurrentPosition, aBufferMax);
+ aCurrentPosition, aBufferMin, aBufferMax);
return;
}
if (aChannels == 0) {
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
// There is no attempt here to limit advance so that mBufferPosition is
// limited to aBufferMax. The only observable affect of skipping the
// check would be in the precise timing of the ended event if the loop