mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 849713 - Part 5: Implement the looping logic in AudioBufferSourceNodeEngine; r=roc
The logic in this function is mostly around the mLoop variable. mLoop being NotLooping means that the playback of this node is never going to loop, in which case the logic doesn't change compared to the ProduceAudioBlock function before this patch. If the loop mode is turned on when start() is called, mLoop will initially be WillLoop. In that case, we play back until mLoopEnd, and then we will wrap around to mLoopStart, set mLoop to be IsLooping, and start playback again. From that point on, mLoop will always be IsLooping, and we will loop between mLoopStart and mLoopEnd. Where possible, we'll just use BorrowFromInputBuffer to avoid copying the buffer, and if we hit the edges right around the time that we loop, we copy some frames from the end of the input buffer and some from the beginning in the memcpy loops at the end of the ProduceAudioBlock function.
This commit is contained in:
parent
a5fea34024
commit
a974ee8086
@ -100,15 +100,26 @@ public:
|
||||
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop until stop() has not been called
|
||||
TrackTicks endTime = std::min(mStart + mDuration, mStop);
|
||||
// Don't set *aFinished just because we passed mStop. Maybe someone
|
||||
// will call stop() again with a different value.
|
||||
if (currentPosition + WEBAUDIO_BLOCK_SIZE >= mStart + mDuration) {
|
||||
*aFinished = true;
|
||||
}
|
||||
if (currentPosition >= endTime || mStart >= endTime) {
|
||||
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
if (mLoop != NotLooping) {
|
||||
if (mStop != TRACK_TICKS_MAX &&
|
||||
currentPosition + WEBAUDIO_BLOCK_SIZE >= mStop) {
|
||||
*aFinished = true;
|
||||
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Don't set *aFinished just because we passed mStop. Maybe someone
|
||||
// will call stop() again with a different value.
|
||||
if (currentPosition + WEBAUDIO_BLOCK_SIZE >= mStart + mDuration) {
|
||||
*aFinished = true;
|
||||
}
|
||||
if (currentPosition >= endTime || mStart >= endTime) {
|
||||
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t channels = mBuffer->GetChannels();
|
||||
@ -117,7 +128,9 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentPosition >= mStart &&
|
||||
// If we're not in loop mode
|
||||
if (mLoop == NotLooping &&
|
||||
currentPosition >= mStart &&
|
||||
currentPosition + WEBAUDIO_BLOCK_SIZE <= endTime) {
|
||||
// Data is entirely within the buffer. Avoid copying it.
|
||||
BorrowFromInputBuffer(aOutput, channels,
|
||||
@ -125,19 +138,103 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're in the loop mode but have not started looping yet
|
||||
TrackTicks startLoop = std::min(mStart + mLoopEnd - mOffset, mStop);
|
||||
if (mLoop == WillLoop &&
|
||||
currentPosition >= mStart &&
|
||||
currentPosition + WEBAUDIO_BLOCK_SIZE <= startLoop) {
|
||||
// Data is entirely within the buffer. Avoid copying it.
|
||||
BorrowFromInputBuffer(aOutput, channels,
|
||||
uintptr_t(currentPosition - mStart + mOffset));
|
||||
|
||||
if (currentPosition + WEBAUDIO_BLOCK_SIZE == startLoop) {
|
||||
// Move to the first repeat of the loop
|
||||
mLoop = IsLooping;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're already looping
|
||||
int32_t loopLength;
|
||||
TrackTicks distanceFromLoopStart;
|
||||
if (mLoop == IsLooping &&
|
||||
currentPosition + WEBAUDIO_BLOCK_SIZE <= mStop) {
|
||||
MOZ_ASSERT(currentPosition >= mStart);
|
||||
|
||||
loopLength = mLoopEnd - mLoopStart;
|
||||
TrackTicks intoLoop = currentPosition - mStart + mOffset - mLoopEnd;
|
||||
distanceFromLoopStart = intoLoop % loopLength;
|
||||
|
||||
if (loopLength >= WEBAUDIO_BLOCK_SIZE &&
|
||||
distanceFromLoopStart + WEBAUDIO_BLOCK_SIZE <= loopLength) {
|
||||
// Data is entirely within the buffer. Avoid copying it.
|
||||
BorrowFromInputBuffer(aOutput, channels, mLoopStart + distanceFromLoopStart);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, handle the case where we're close to the edge of the total output
|
||||
// buffer, and build the output chunk manually.
|
||||
AllocateAudioBlock(channels, aOutput);
|
||||
TrackTicks start = std::max(currentPosition, mStart);
|
||||
TrackTicks end = std::min(currentPosition + WEBAUDIO_BLOCK_SIZE, endTime);
|
||||
WriteZeroesToAudioBlock(aOutput, 0, uint32_t(start - currentPosition));
|
||||
for (uint32_t i = 0; i < channels; ++i) {
|
||||
memcpy(static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])) +
|
||||
uint32_t(start - currentPosition),
|
||||
mBuffer->GetData(i) +
|
||||
uintptr_t(start - mStart + mOffset),
|
||||
uint32_t(end - start) * sizeof(float));
|
||||
if (mLoop == NotLooping) {
|
||||
// Not in loop mode
|
||||
TrackTicks end = std::min(currentPosition + WEBAUDIO_BLOCK_SIZE, endTime);
|
||||
WriteZeroesToAudioBlock(aOutput, 0, uint32_t(start - currentPosition));
|
||||
for (uint32_t i = 0; i < channels; ++i) {
|
||||
memcpy(static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])) +
|
||||
uint32_t(start - currentPosition),
|
||||
mBuffer->GetData(i) +
|
||||
uintptr_t(start - mStart + mOffset),
|
||||
uint32_t(end - start) * sizeof(float));
|
||||
}
|
||||
uint32_t endOffset = uint32_t(end - currentPosition);
|
||||
WriteZeroesToAudioBlock(aOutput, endOffset, WEBAUDIO_BLOCK_SIZE - endOffset);
|
||||
} else if (mLoop == WillLoop) {
|
||||
// In loop mode but not looping yet
|
||||
TrackTicks end = std::min(currentPosition + WEBAUDIO_BLOCK_SIZE, mStop);
|
||||
TrackTicks endPreLoop = std::min(currentPosition + WEBAUDIO_BLOCK_SIZE,
|
||||
std::min(mStart + mLoopEnd, mStop));
|
||||
WriteZeroesToAudioBlock(aOutput, 0, uint32_t(start - currentPosition));
|
||||
for (uint32_t i = 0; i < channels; ++i) {
|
||||
float* baseChannelData = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i]));
|
||||
// Copy the chunk before we hit the loop point
|
||||
memcpy(baseChannelData + uint32_t(start - currentPosition),
|
||||
mBuffer->GetData(i) + uintptr_t(start - mStart + mOffset),
|
||||
uint32_t(endPreLoop - start) * sizeof(float));
|
||||
// Start back from mLoopStart and fill in the rest of the buffer
|
||||
memcpy(baseChannelData + uint32_t(endPreLoop - currentPosition),
|
||||
mBuffer->GetData(i) + mLoopStart,
|
||||
uint32_t(end - endPreLoop) * sizeof(float));
|
||||
}
|
||||
uint32_t endOffset = uint32_t(end - currentPosition);
|
||||
WriteZeroesToAudioBlock(aOutput, endOffset, WEBAUDIO_BLOCK_SIZE - endOffset);
|
||||
|
||||
if (currentPosition + WEBAUDIO_BLOCK_SIZE >= startLoop) {
|
||||
// Move to the first repeat of the loop
|
||||
mLoop = IsLooping;
|
||||
}
|
||||
} else {
|
||||
// Already looping
|
||||
MOZ_ASSERT(start == currentPosition);
|
||||
|
||||
TrackTicks end = std::min(currentPosition + WEBAUDIO_BLOCK_SIZE, mStop);
|
||||
TrackTicks endLoop = std::min(currentPosition + loopLength - distanceFromLoopStart, mStop);
|
||||
MOZ_ASSERT(endLoop < currentPosition + WEBAUDIO_BLOCK_SIZE);
|
||||
for (uint32_t i = 0; i < channels; ++i) {
|
||||
float* baseChannelData = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i]));
|
||||
// Copy the chunk before we hit the loop point
|
||||
memcpy(baseChannelData + uint32_t(start - currentPosition),
|
||||
mBuffer->GetData(i) + uintptr_t(distanceFromLoopStart + mLoopStart),
|
||||
uint32_t(endLoop - start) * sizeof(float));
|
||||
// Start back from mLoopStart and fill in the rest of the buffer
|
||||
memcpy(baseChannelData + uint32_t(endLoop - currentPosition),
|
||||
mBuffer->GetData(i) + mLoopStart,
|
||||
uint32_t(end - endLoop) * sizeof(float));
|
||||
}
|
||||
uint32_t endOffset = uint32_t(end - currentPosition);
|
||||
WriteZeroesToAudioBlock(aOutput, endOffset, WEBAUDIO_BLOCK_SIZE - endOffset);
|
||||
}
|
||||
uint32_t endOffset = uint32_t(end - currentPosition);
|
||||
WriteZeroesToAudioBlock(aOutput, endOffset, WEBAUDIO_BLOCK_SIZE - endOffset);
|
||||
}
|
||||
|
||||
TrackTicks mStart;
|
||||
|
Loading…
Reference in New Issue
Block a user