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:
parent
2b85d0edb4
commit
5abfd25a34
|
@ -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.
|
||||
|
|
|
@ -435,6 +435,12 @@ private:
|
|||
/** \brief How many sample rates to try */
|
||||
static const int NumRatesToTry;
|
||||
|
||||
bool ReversedTime() const
|
||||
{
|
||||
return mT1 < mT0;
|
||||
}
|
||||
double LimitStreamTime(double absoluteTime) const;
|
||||
|
||||
double NormalizeStreamTime(double absoluteTime) const;
|
||||
|
||||
/** \brief Clean up after StartStream if it fails.
|
||||
|
|
|
@ -1430,31 +1430,42 @@ double Envelope::SolveIntegralOfInverse( double t0, double area )
|
|||
{
|
||||
if(area == 0.0)
|
||||
return t0;
|
||||
if(area < 0.0)
|
||||
{
|
||||
fprintf( stderr, "SolveIntegralOfInverse called with negative area, this is not supported!\n" );
|
||||
return t0;
|
||||
}
|
||||
|
||||
unsigned int count = mEnv.Count();
|
||||
if(count == 0) // 'empty' envelope
|
||||
return t0 + area * mDefaultValue;
|
||||
|
||||
double lastT, lastVal;
|
||||
unsigned int i; // this is the next point to check
|
||||
int i; // this is the next point to check
|
||||
if(t0 < mEnv[0]->GetT()) // t0 preceding the first point
|
||||
{
|
||||
i = 1;
|
||||
lastT = mEnv[0]->GetT();
|
||||
lastVal = mEnv[0]->GetVal();
|
||||
double added = (lastT - t0) / lastVal;
|
||||
if(added >= area)
|
||||
if (area < 0) {
|
||||
return t0 + area * mEnv[0]->GetVal();
|
||||
area -= added;
|
||||
}
|
||||
else {
|
||||
i = 1;
|
||||
lastT = mEnv[0]->GetT();
|
||||
lastVal = mEnv[0]->GetVal();
|
||||
double added = (lastT - t0) / lastVal;
|
||||
if(added >= area)
|
||||
return t0 + area * mEnv[0]->GetVal();
|
||||
area -= added;
|
||||
}
|
||||
}
|
||||
else if(t0 >= mEnv[count - 1]->GetT()) // t0 following the last point
|
||||
{
|
||||
return t0 + area * mEnv[count - 1]->GetVal();
|
||||
if (area < 0) {
|
||||
i = count - 2;
|
||||
lastT = mEnv[count - 1]->GetT();
|
||||
lastVal = mEnv[count - 1]->GetVal();
|
||||
double added = (lastT - t0) / lastVal; // negative
|
||||
if(added <= area)
|
||||
return t0 + area * mEnv[count - 1]->GetVal();
|
||||
area -= added;
|
||||
}
|
||||
else {
|
||||
return t0 + area * mEnv[count - 1]->GetVal();
|
||||
}
|
||||
}
|
||||
else // t0 enclosed by points
|
||||
{
|
||||
|
@ -1463,25 +1474,52 @@ double Envelope::SolveIntegralOfInverse( double t0, double area )
|
|||
BinarySearchForTime(lo, hi, t0);
|
||||
lastVal = InterpolatePoints(mEnv[lo]->GetVal(), mEnv[hi]->GetVal(), (t0 - mEnv[lo]->GetT()) / (mEnv[hi]->GetT() - mEnv[lo]->GetT()), mDB);
|
||||
lastT = t0;
|
||||
i = hi; // the point immediately after t0.
|
||||
if (area < 0)
|
||||
i = lo;
|
||||
else
|
||||
i = hi; // the point immediately after t0.
|
||||
}
|
||||
|
||||
// loop through the rest of the envelope points until we get to t1
|
||||
while (1)
|
||||
{
|
||||
if(i >= count) // the requested range extends beyond the last point
|
||||
if (area < 0) {
|
||||
// loop BACKWARDS through the rest of the envelope points until we get to t1
|
||||
// (which is less than t0)
|
||||
while (1)
|
||||
{
|
||||
return lastT + area * lastVal;
|
||||
if(i < 0) // the requested range extends beyond the leftmost point
|
||||
{
|
||||
return lastT + area * lastVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
double added =
|
||||
-IntegrateInverseInterpolated(mEnv[i]->GetVal(), lastVal, lastT - mEnv[i]->GetT(), mDB);
|
||||
if(added <= area)
|
||||
return lastT - SolveIntegrateInverseInterpolated(lastVal, mEnv[i]->GetVal(), lastT - mEnv[i]->GetT(), -area, mDB);
|
||||
area -= added;
|
||||
lastT = mEnv[i]->GetT();
|
||||
lastVal = mEnv[i]->GetVal();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else {
|
||||
// loop through the rest of the envelope points until we get to t1
|
||||
while (1)
|
||||
{
|
||||
double added = IntegrateInverseInterpolated(lastVal, mEnv[i]->GetVal(), mEnv[i]->GetT() - lastT, mDB);
|
||||
if(added >= area)
|
||||
return lastT + SolveIntegrateInverseInterpolated(lastVal, mEnv[i]->GetVal(), mEnv[i]->GetT() - lastT, area, mDB);
|
||||
area -= added;
|
||||
lastT = mEnv[i]->GetT();
|
||||
lastVal = mEnv[i]->GetVal();
|
||||
i++;
|
||||
if(i >= count) // the requested range extends beyond the last point
|
||||
{
|
||||
return lastT + area * lastVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
double added = IntegrateInverseInterpolated(lastVal, mEnv[i]->GetVal(), mEnv[i]->GetT() - lastT, mDB);
|
||||
if(added >= area)
|
||||
return lastT + SolveIntegrateInverseInterpolated(lastVal, mEnv[i]->GetVal(), mEnv[i]->GetT() - lastT, area, mDB);
|
||||
area -= added;
|
||||
lastT = mEnv[i]->GetT();
|
||||
lastVal = mEnv[i]->GetVal();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,8 +99,8 @@ class Envelope : public XMLTagHandler {
|
|||
void Flatten(double value);
|
||||
int GetDragPoint(void) {return mDragPoint;}
|
||||
|
||||
double GetMinValue() { return mMinValue; }
|
||||
double GetMaxValue() { return mMaxValue; }
|
||||
double GetMinValue() const { return mMinValue; }
|
||||
double GetMaxValue() const { return mMaxValue; }
|
||||
void SetRange(double minValue, double maxValue);
|
||||
|
||||
double ClampValue(double value) { return std::max(mMinValue, std::min(mMaxValue, value)); }
|
||||
|
|
139
src/Mix.cpp
139
src/Mix.cpp
|
@ -423,10 +423,9 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||
int *queueStart, int *queueLen,
|
||||
Resample * pResample)
|
||||
{
|
||||
double trackRate = track->GetRate();
|
||||
double initialWarp = mRate / trackRate;
|
||||
double tstep = 1.0 / trackRate;
|
||||
double t = (*pos - *queueLen) / trackRate;
|
||||
const double trackRate = track->GetRate();
|
||||
const double initialWarp = mRate / trackRate;
|
||||
const double tstep = 1.0 / trackRate;
|
||||
int sampleSize = SAMPLE_SIZE(floatSample);
|
||||
|
||||
sampleCount out = 0;
|
||||
|
@ -443,14 +442,15 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||
*/
|
||||
|
||||
// Find the last sample
|
||||
sampleCount endPos;
|
||||
double endTime = track->GetEndTime();
|
||||
if (endTime > mT1) {
|
||||
endPos = track->TimeToLongSamples(mT1);
|
||||
}
|
||||
else {
|
||||
endPos = track->TimeToLongSamples(endTime);
|
||||
}
|
||||
double startTime = track->GetStartTime();
|
||||
const sampleCount endPos =
|
||||
track->TimeToLongSamples(std::max(startTime, std::min(endTime, mT1)));
|
||||
const sampleCount startPos =
|
||||
track->TimeToLongSamples(std::max(startTime, std::min(endTime, mT0)));
|
||||
const bool backwards = (endPos < startPos);
|
||||
// Find the time corresponding to the start of the queue, for use with time track
|
||||
double t = (*pos + (backwards ? *queueLen : - *queueLen)) / trackRate;
|
||||
|
||||
while (out < mMaxOut) {
|
||||
if (*queueLen < mProcessLen) {
|
||||
|
@ -458,31 +458,48 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||
memmove(queue, &queue[*queueStart], (*queueLen) * sampleSize);
|
||||
*queueStart = 0;
|
||||
|
||||
int getLen = mQueueMaxLen - *queueLen;
|
||||
int getLen =
|
||||
std::min((backwards ? *pos - endPos : endPos - *pos),
|
||||
sampleCount(mQueueMaxLen - *queueLen));
|
||||
|
||||
// Constrain
|
||||
if (*pos + getLen > endPos) {
|
||||
getLen = endPos - *pos;
|
||||
}
|
||||
|
||||
// Nothing to do if past end of track
|
||||
// Nothing to do if past end of play interval
|
||||
if (getLen > 0) {
|
||||
track->Get((samplePtr)&queue[*queueLen],
|
||||
floatSample,
|
||||
*pos,
|
||||
getLen);
|
||||
if (backwards) {
|
||||
track->Get((samplePtr)&queue[*queueLen],
|
||||
floatSample,
|
||||
*pos - (getLen - 1),
|
||||
getLen);
|
||||
|
||||
track->GetEnvelopeValues(mEnvValues,
|
||||
getLen,
|
||||
(*pos) / trackRate,
|
||||
tstep);
|
||||
track->GetEnvelopeValues(mEnvValues,
|
||||
getLen,
|
||||
(*pos - (getLen- 1)) / trackRate,
|
||||
tstep);
|
||||
|
||||
*pos -= getLen;
|
||||
}
|
||||
else {
|
||||
track->Get((samplePtr)&queue[*queueLen],
|
||||
floatSample,
|
||||
*pos,
|
||||
getLen);
|
||||
|
||||
track->GetEnvelopeValues(mEnvValues,
|
||||
getLen,
|
||||
(*pos) / trackRate,
|
||||
tstep);
|
||||
|
||||
*pos += getLen;
|
||||
}
|
||||
|
||||
for (int i = 0; i < getLen; i++) {
|
||||
queue[(*queueLen) + i] *= mEnvValues[i];
|
||||
}
|
||||
|
||||
if (backwards)
|
||||
ReverseSamples((samplePtr)&queue[0], floatSample,
|
||||
*queueStart, getLen);
|
||||
|
||||
*queueLen += getLen;
|
||||
*pos += getLen;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,8 +516,13 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||
// as a result of this the warp factor may be slightly wrong, so AudioIO will stop too soon
|
||||
// or too late (resulting in missing sound or inserted silence). This can't be fixed
|
||||
// without changing the way the resampler works, because the number of input samples that will be used
|
||||
// is unpredictable. Maybe it can be compensated lated though.
|
||||
factor *= mTimeTrack->ComputeWarpFactor(t, t + (double)thisProcessLen / trackRate);
|
||||
// is unpredictable. Maybe it can be compensated later though.
|
||||
if (backwards)
|
||||
factor *= mTimeTrack->ComputeWarpFactor
|
||||
(t - (double)thisProcessLen / trackRate + tstep, t + tstep);
|
||||
else
|
||||
factor *= mTimeTrack->ComputeWarpFactor
|
||||
(t, t + (double)thisProcessLen / trackRate);
|
||||
}
|
||||
|
||||
int input_used;
|
||||
|
@ -519,7 +541,7 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||
*queueStart += input_used;
|
||||
*queueLen -= input_used;
|
||||
out += outgen;
|
||||
t += (input_used / trackRate);
|
||||
t += ((backwards ? -input_used : input_used) / trackRate);
|
||||
|
||||
if (last) {
|
||||
break;
|
||||
|
@ -551,24 +573,46 @@ sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrack *track,
|
|||
{
|
||||
int slen = mMaxOut;
|
||||
int c;
|
||||
double t = *pos / track->GetRate();
|
||||
double trackEndTime = track->GetEndTime();
|
||||
double tEnd = trackEndTime > mT1 ? mT1 : trackEndTime;
|
||||
const double t = *pos / track->GetRate();
|
||||
const double trackEndTime = track->GetEndTime();
|
||||
const double trackStartTime = track->GetStartTime();
|
||||
const double tEnd = std::max(trackStartTime, std::min(trackEndTime, mT1));
|
||||
const double tStart = std::max(trackStartTime, std::min(trackEndTime, mT0));
|
||||
const bool backwards = (tEnd < tStart);
|
||||
|
||||
//don't process if we're at the end of the selection or track.
|
||||
if (t>=tEnd)
|
||||
if ((backwards ? t <= tEnd : t >= tEnd))
|
||||
return 0;
|
||||
//if we're about to approach the end of the track or selection, figure out how much we need to grab
|
||||
if (t + slen/track->GetRate() > tEnd)
|
||||
slen = (int)((tEnd - t) * track->GetRate() + 0.5);
|
||||
if (backwards) {
|
||||
if (t - slen/track->GetRate() < tEnd)
|
||||
slen = (int)((t - tEnd) * track->GetRate() + 0.5);
|
||||
}
|
||||
else {
|
||||
if (t + slen/track->GetRate() > tEnd)
|
||||
slen = (int)((tEnd - t) * track->GetRate() + 0.5);
|
||||
}
|
||||
|
||||
if (slen > mMaxOut)
|
||||
slen = mMaxOut;
|
||||
|
||||
track->Get((samplePtr)mFloatBuffer, floatSample, *pos, slen);
|
||||
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
|
||||
for(int i=0; i<slen; i++)
|
||||
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
||||
if (backwards) {
|
||||
track->Get((samplePtr)mFloatBuffer, floatSample, *pos - (slen - 1), slen);
|
||||
track->GetEnvelopeValues(mEnvValues, slen, t - (slen - 1) / mRate, 1.0 / mRate);
|
||||
for(int i=0; i<slen; i++)
|
||||
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
||||
ReverseSamples((samplePtr)mFloatBuffer, floatSample, 0, slen);
|
||||
|
||||
*pos -= slen;
|
||||
}
|
||||
else {
|
||||
track->Get((samplePtr)mFloatBuffer, floatSample, *pos, slen);
|
||||
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
|
||||
for(int i=0; i<slen; i++)
|
||||
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
||||
|
||||
*pos += slen;
|
||||
}
|
||||
|
||||
for(c=0; c<mNumChannels; c++)
|
||||
if (mApplyTrackGains)
|
||||
|
@ -579,8 +623,6 @@ sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrack *track,
|
|||
MixBuffers(mNumChannels, channelFlags, mGains,
|
||||
(samplePtr)mFloatBuffer, mTemp, slen, mInterleaved);
|
||||
|
||||
*pos += slen;
|
||||
|
||||
return slen;
|
||||
}
|
||||
|
||||
|
@ -639,7 +681,9 @@ sampleCount Mixer::Process(sampleCount maxToProcess)
|
|||
maxOut = out;
|
||||
|
||||
double t = (double)mSamplePos[i] / (double)track->GetRate();
|
||||
if(t > mTime)
|
||||
if (mT0 > mT1)
|
||||
mTime = std::max(t, mT1);
|
||||
else
|
||||
mTime = std::min(t, mT1);
|
||||
}
|
||||
if(mInterleaved) {
|
||||
|
@ -707,10 +751,11 @@ void Mixer::Reposition(double t)
|
|||
int i;
|
||||
|
||||
mTime = t;
|
||||
if( mTime < mT0 )
|
||||
mTime = mT0;
|
||||
if( mTime > mT1 )
|
||||
mTime = mT1;
|
||||
const bool backwards = (mT1 < mT0);
|
||||
if (backwards)
|
||||
mTime = std::max(mT1, (std::min(mT0, mTime)));
|
||||
else
|
||||
mTime = std::max(mT0, (std::min(mT1, mTime)));
|
||||
|
||||
for(i=0; i<mNumInputTracks; i++) {
|
||||
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mTime);
|
||||
|
|
|
@ -92,6 +92,24 @@ void ClearSamples(samplePtr src, sampleFormat format,
|
|||
memset(src + start*size, 0, len*size);
|
||||
}
|
||||
|
||||
void ReverseSamples(samplePtr src, sampleFormat format,
|
||||
int start, int len)
|
||||
{
|
||||
int size = SAMPLE_SIZE(format);
|
||||
samplePtr first = src + start * size;
|
||||
samplePtr last = src + (start + len - 1) * size;
|
||||
enum { fixedSize = SAMPLE_SIZE(floatSample) };
|
||||
wxASSERT(size <= fixedSize);
|
||||
char temp[fixedSize];
|
||||
while (first < last) {
|
||||
memcpy(temp, first, size);
|
||||
memcpy(first, last, size);
|
||||
memcpy(last, temp, size);
|
||||
first += size;
|
||||
last -= size;
|
||||
}
|
||||
}
|
||||
|
||||
void CopySamples(samplePtr src, sampleFormat srcFormat,
|
||||
samplePtr dst, sampleFormat dstFormat,
|
||||
unsigned int len,
|
||||
|
|
|
@ -70,6 +70,9 @@ void CopySamplesNoDither(samplePtr src, sampleFormat srcFormat,
|
|||
void ClearSamples(samplePtr buffer, sampleFormat format,
|
||||
int start, int len);
|
||||
|
||||
void ReverseSamples(samplePtr buffer, sampleFormat format,
|
||||
int start, int len);
|
||||
|
||||
//
|
||||
// This must be called on startup and everytime new ditherers
|
||||
// are set in preferences.
|
||||
|
|
|
@ -473,13 +473,17 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||
bool cutpreview, /* = false */
|
||||
bool backwards /* = false */)
|
||||
{
|
||||
// Uncomment this for laughs!
|
||||
// backwards = true;
|
||||
|
||||
double t0 = selectedRegion.t0();
|
||||
double t1 = selectedRegion.t1();
|
||||
// SelectedRegion guarantees t0 <= t1, so we need another boolean argument
|
||||
// to indicate backwards play.
|
||||
const bool looped = options.playLooped;
|
||||
|
||||
wxASSERT(! backwards);
|
||||
if (backwards)
|
||||
std::swap(t0, t1);
|
||||
|
||||
SetPlay(true, looped, cutpreview);
|
||||
|
||||
|
@ -555,7 +559,9 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||
t1 = t->GetEndTime();
|
||||
}
|
||||
else {
|
||||
// always t0 < t1 right?
|
||||
// maybe t1 < t0, with backwards scrubbing for instance
|
||||
if (backwards)
|
||||
std::swap(t0, t1);
|
||||
|
||||
// the set intersection between the play region and the
|
||||
// valid range maximum of lower bounds
|
||||
|
@ -579,6 +585,9 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||
t0 = maxofmins;
|
||||
t1 = minofmaxs;
|
||||
}
|
||||
|
||||
if (backwards)
|
||||
std::swap(t0, t1);
|
||||
}
|
||||
|
||||
// Can't play before 0...either shifted or latency corrected tracks
|
||||
|
@ -589,14 +598,19 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||
|
||||
int token = -1;
|
||||
bool success = false;
|
||||
if (t1 > t0) {
|
||||
if (t1 != t0) {
|
||||
if (cutpreview) {
|
||||
const double tless = std::min(t0, t1);
|
||||
const double tgreater = std::max(t0, t1);
|
||||
double beforeLen, afterLen;
|
||||
gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
|
||||
gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
|
||||
double tcp0 = t0-beforeLen;
|
||||
double tcp1 = (t1+afterLen) - (t1-t0);
|
||||
SetupCutPreviewTracks(tcp0, t0, t1, tcp1);
|
||||
double tcp0 = tless-beforeLen;
|
||||
double diff = tgreater - tless;
|
||||
double tcp1 = (tgreater+afterLen) - diff;
|
||||
SetupCutPreviewTracks(tcp0, tless, tgreater, tcp1);
|
||||
if (backwards)
|
||||
std::swap(tcp0, tcp1);
|
||||
if (mCutPreviewTracks)
|
||||
{
|
||||
AudioIOStartStreamOptions myOptions = options;
|
||||
|
|
Loading…
Reference in New Issue
Block a user