1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-24 06:10:09 +01:00

Support backwards play, a requirement for scrubbing

Uncomment the line at the top of ControlToolBar::PlayPlayRegion to play
everything backwards and test it

It even works correctly with a time track
This commit is contained in:
Paul-Licameli
2015-04-16 17:35:58 -04:00
parent 2b85d0edb4
commit 5abfd25a34
8 changed files with 256 additions and 109 deletions

View File

@@ -79,6 +79,7 @@
the speed control. In a separate algorithm, the audio callback updates
mTime by (frames / samplerate) * factor, where factor reflects the
speed at mTime. This effectively integrates speed to get position.
Negative speeds are allowed too, for instance in scrubbing.
\par Midi Time
MIDI is not warped according to the speed control. This might be
@@ -1213,10 +1214,13 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks,
// with ComputeWarpedLength, it is now possible the calculate the warped length with 100% accuracy
// (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
mWarpedTime = 0.0;
if(mTimeTrack)
if (mTimeTrack)
// Following gives negative when mT0 > mT1
mWarpedLength = mTimeTrack->ComputeWarpedLength(mT0, mT1);
else
mWarpedLength = mT1 - mT0;
// PRL allow backwards play
mWarpedLength = abs(mWarpedLength);
//
// The RingBuffer sizes, and the max amount of the buffer to
@@ -1999,6 +2003,15 @@ bool AudioIO::IsMonitoring()
return ( mPortStreamV19 && mStreamToken==0 );
}
double AudioIO::LimitStreamTime(double absoluteTime) const
{
// Allows for forward or backward play
if (ReversedTime())
return std::max(mT1, std::min(mT0, absoluteTime));
else
return std::max(mT0, std::min(mT1, absoluteTime));
}
double AudioIO::NormalizeStreamTime(double absoluteTime) const
{
// dmazzoni: This function is needed for two reasons:
@@ -2013,13 +2026,7 @@ double AudioIO::NormalizeStreamTime(double absoluteTime) const
// mode. In this case, we should jump over a defined "gap" in the
// audio.
// msmeyer: Just to be sure, the returned stream time should
// never be smaller than the actual start time.
if (absoluteTime < mT0)
absoluteTime = mT0;
if (absoluteTime > mT1)
absoluteTime = mT1;
absoluteTime = LimitStreamTime(absoluteTime);
if (mCutPreviewGapLen > 0)
{
@@ -2863,6 +2870,7 @@ void AudioIO::FillBuffers()
warpedSamples = mPlaybackMixers[i]->GetBuffer();
mPlaybackBuffers[i]->Put(warpedSamples, floatSample, processed);
}
//if looping and processed is less than the full chunk/block/buffer that gets pulled from
//other longer tracks, then we still need to advance the ring buffers or
//we'll trip up on ourselves when we start them back up again.
@@ -3561,17 +3569,20 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
// Calculate the new time position
gAudioIO->mTime += gAudioIO->mSeek;
if (gAudioIO->mTime < gAudioIO->mT0)
gAudioIO->mTime = gAudioIO->mT0;
else if (gAudioIO->mTime > gAudioIO->mT1)
gAudioIO->mTime = gAudioIO->mT1;
gAudioIO->mTime = gAudioIO->LimitStreamTime(gAudioIO->mTime);
gAudioIO->mSeek = 0.0;
// Reset mixer positions and flush buffers for all tracks
if(gAudioIO->mTimeTrack)
gAudioIO->mWarpedTime = gAudioIO->mTimeTrack->ComputeWarpedLength(gAudioIO->mT0, gAudioIO->mTime);
// Following gives negative when mT0 > mTime
gAudioIO->mWarpedTime =
gAudioIO->mTimeTrack->ComputeWarpedLength
(gAudioIO->mT0, gAudioIO->mTime);
else
gAudioIO->mWarpedTime = gAudioIO->mTime - gAudioIO->mT0;
gAudioIO->mWarpedTime = abs(gAudioIO->mWarpedTime);
// Reset mixer positions and flush buffers for all tracks
for (i = 0; i < (unsigned int)numPlaybackTracks; i++)
{
gAudioIO->mPlaybackMixers[i]->Reposition(gAudioIO->mTime);
@@ -3703,10 +3714,12 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
// the end, then we've actually finished playing the entire
// selection.
// msmeyer: We never finish if we are playing looped
if (len == 0 && gAudioIO->mTime >= gAudioIO->mT1 &&
!gAudioIO->mPlayLooped)
{
callbackReturn = paComplete;
if (len == 0 &&
!gAudioIO->mPlayLooped) {
if ((gAudioIO->ReversedTime()
? gAudioIO->mTime <= gAudioIO->mT1
: gAudioIO->mTime >= gAudioIO->mT1))
callbackReturn = paComplete;
}
if (cut) // no samples to process, they've been discarded
@@ -3852,20 +3865,30 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
}
// Update the current time position
if (gAudioIO->mTimeTrack) {
// MB: this is why SolveWarpedLength is needed :)
gAudioIO->mTime = gAudioIO->mTimeTrack->SolveWarpedLength(gAudioIO->mTime, framesPerBuffer / gAudioIO->mRate);
} else {
gAudioIO->mTime += framesPerBuffer / gAudioIO->mRate;
{
double delta = framesPerBuffer / gAudioIO->mRate;
if (gAudioIO->ReversedTime())
delta *= -1.0;
if (gAudioIO->mTimeTrack)
// MB: this is why SolveWarpedLength is needed :)
gAudioIO->mTime =
gAudioIO->mTimeTrack->SolveWarpedLength(gAudioIO->mTime, delta);
else
gAudioIO->mTime += delta;
}
// Wrap to start if looping
while (gAudioIO->mPlayLooped && gAudioIO->mTime >= gAudioIO->mT1)
if (gAudioIO->mPlayLooped)
{
// LL: This is not exactly right, but I'm at my wits end trying to
// figure it out. Feel free to fix it. :-)
// MB: it's much easier than you think, mTime isn't warped at all!
gAudioIO->mTime -= gAudioIO->mT1 - gAudioIO->mT0;
while (gAudioIO->ReversedTime()
? gAudioIO->mTime <= gAudioIO->mT1
: gAudioIO->mTime >= gAudioIO->mT1)
{
// LL: This is not exactly right, but I'm at my wits end trying to
// figure it out. Feel free to fix it. :-)
// MB: it's much easier than you think, mTime isn't warped at all!
gAudioIO->mTime -= gAudioIO->mT1 - gAudioIO->mT0;
}
}
// Record the reported latency from PortAudio.