mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-16 16:10:06 +02: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:
parent
2b85d0edb4
commit
5abfd25a34
@ -79,6 +79,7 @@
|
|||||||
the speed control. In a separate algorithm, the audio callback updates
|
the speed control. In a separate algorithm, the audio callback updates
|
||||||
mTime by (frames / samplerate) * factor, where factor reflects the
|
mTime by (frames / samplerate) * factor, where factor reflects the
|
||||||
speed at mTime. This effectively integrates speed to get position.
|
speed at mTime. This effectively integrates speed to get position.
|
||||||
|
Negative speeds are allowed too, for instance in scrubbing.
|
||||||
|
|
||||||
\par Midi Time
|
\par Midi Time
|
||||||
MIDI is not warped according to the speed control. This might be
|
MIDI is not warped according to the speed control. This might be
|
||||||
@ -1214,9 +1215,12 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks,
|
|||||||
// (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
|
// (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
|
||||||
mWarpedTime = 0.0;
|
mWarpedTime = 0.0;
|
||||||
if (mTimeTrack)
|
if (mTimeTrack)
|
||||||
|
// Following gives negative when mT0 > mT1
|
||||||
mWarpedLength = mTimeTrack->ComputeWarpedLength(mT0, mT1);
|
mWarpedLength = mTimeTrack->ComputeWarpedLength(mT0, mT1);
|
||||||
else
|
else
|
||||||
mWarpedLength = mT1 - mT0;
|
mWarpedLength = mT1 - mT0;
|
||||||
|
// PRL allow backwards play
|
||||||
|
mWarpedLength = abs(mWarpedLength);
|
||||||
|
|
||||||
//
|
//
|
||||||
// The RingBuffer sizes, and the max amount of the buffer to
|
// The RingBuffer sizes, and the max amount of the buffer to
|
||||||
@ -1999,6 +2003,15 @@ bool AudioIO::IsMonitoring()
|
|||||||
return ( mPortStreamV19 && mStreamToken==0 );
|
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
|
double AudioIO::NormalizeStreamTime(double absoluteTime) const
|
||||||
{
|
{
|
||||||
// dmazzoni: This function is needed for two reasons:
|
// 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
|
// mode. In this case, we should jump over a defined "gap" in the
|
||||||
// audio.
|
// audio.
|
||||||
|
|
||||||
// msmeyer: Just to be sure, the returned stream time should
|
absoluteTime = LimitStreamTime(absoluteTime);
|
||||||
// never be smaller than the actual start time.
|
|
||||||
if (absoluteTime < mT0)
|
|
||||||
absoluteTime = mT0;
|
|
||||||
|
|
||||||
if (absoluteTime > mT1)
|
|
||||||
absoluteTime = mT1;
|
|
||||||
|
|
||||||
if (mCutPreviewGapLen > 0)
|
if (mCutPreviewGapLen > 0)
|
||||||
{
|
{
|
||||||
@ -2863,6 +2870,7 @@ void AudioIO::FillBuffers()
|
|||||||
warpedSamples = mPlaybackMixers[i]->GetBuffer();
|
warpedSamples = mPlaybackMixers[i]->GetBuffer();
|
||||||
mPlaybackBuffers[i]->Put(warpedSamples, floatSample, processed);
|
mPlaybackBuffers[i]->Put(warpedSamples, floatSample, processed);
|
||||||
}
|
}
|
||||||
|
|
||||||
//if looping and processed is less than the full chunk/block/buffer that gets pulled from
|
//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
|
//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.
|
//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
|
// Calculate the new time position
|
||||||
gAudioIO->mTime += gAudioIO->mSeek;
|
gAudioIO->mTime += gAudioIO->mSeek;
|
||||||
if (gAudioIO->mTime < gAudioIO->mT0)
|
gAudioIO->mTime = gAudioIO->LimitStreamTime(gAudioIO->mTime);
|
||||||
gAudioIO->mTime = gAudioIO->mT0;
|
|
||||||
else if (gAudioIO->mTime > gAudioIO->mT1)
|
|
||||||
gAudioIO->mTime = gAudioIO->mT1;
|
|
||||||
gAudioIO->mSeek = 0.0;
|
gAudioIO->mSeek = 0.0;
|
||||||
|
|
||||||
// Reset mixer positions and flush buffers for all tracks
|
// Reset mixer positions and flush buffers for all tracks
|
||||||
if(gAudioIO->mTimeTrack)
|
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
|
else
|
||||||
gAudioIO->mWarpedTime = gAudioIO->mTime - gAudioIO->mT0;
|
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++)
|
for (i = 0; i < (unsigned int)numPlaybackTracks; i++)
|
||||||
{
|
{
|
||||||
gAudioIO->mPlaybackMixers[i]->Reposition(gAudioIO->mTime);
|
gAudioIO->mPlaybackMixers[i]->Reposition(gAudioIO->mTime);
|
||||||
@ -3703,9 +3714,11 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
// the end, then we've actually finished playing the entire
|
// the end, then we've actually finished playing the entire
|
||||||
// selection.
|
// selection.
|
||||||
// msmeyer: We never finish if we are playing looped
|
// msmeyer: We never finish if we are playing looped
|
||||||
if (len == 0 && gAudioIO->mTime >= gAudioIO->mT1 &&
|
if (len == 0 &&
|
||||||
!gAudioIO->mPlayLooped)
|
!gAudioIO->mPlayLooped) {
|
||||||
{
|
if ((gAudioIO->ReversedTime()
|
||||||
|
? gAudioIO->mTime <= gAudioIO->mT1
|
||||||
|
: gAudioIO->mTime >= gAudioIO->mT1))
|
||||||
callbackReturn = paComplete;
|
callbackReturn = paComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3852,21 +3865,31 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the current time position
|
// Update the current time position
|
||||||
if (gAudioIO->mTimeTrack) {
|
{
|
||||||
|
double delta = framesPerBuffer / gAudioIO->mRate;
|
||||||
|
if (gAudioIO->ReversedTime())
|
||||||
|
delta *= -1.0;
|
||||||
|
if (gAudioIO->mTimeTrack)
|
||||||
// MB: this is why SolveWarpedLength is needed :)
|
// MB: this is why SolveWarpedLength is needed :)
|
||||||
gAudioIO->mTime = gAudioIO->mTimeTrack->SolveWarpedLength(gAudioIO->mTime, framesPerBuffer / gAudioIO->mRate);
|
gAudioIO->mTime =
|
||||||
} else {
|
gAudioIO->mTimeTrack->SolveWarpedLength(gAudioIO->mTime, delta);
|
||||||
gAudioIO->mTime += framesPerBuffer / gAudioIO->mRate;
|
else
|
||||||
|
gAudioIO->mTime += delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap to start if looping
|
// Wrap to start if looping
|
||||||
while (gAudioIO->mPlayLooped && gAudioIO->mTime >= gAudioIO->mT1)
|
if (gAudioIO->mPlayLooped)
|
||||||
|
{
|
||||||
|
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
|
// LL: This is not exactly right, but I'm at my wits end trying to
|
||||||
// figure it out. Feel free to fix it. :-)
|
// figure it out. Feel free to fix it. :-)
|
||||||
// MB: it's much easier than you think, mTime isn't warped at all!
|
// MB: it's much easier than you think, mTime isn't warped at all!
|
||||||
gAudioIO->mTime -= gAudioIO->mT1 - gAudioIO->mT0;
|
gAudioIO->mTime -= gAudioIO->mT1 - gAudioIO->mT0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Record the reported latency from PortAudio.
|
// Record the reported latency from PortAudio.
|
||||||
// TODO: Don't recalculate this with every callback?
|
// TODO: Don't recalculate this with every callback?
|
||||||
|
@ -435,6 +435,12 @@ private:
|
|||||||
/** \brief How many sample rates to try */
|
/** \brief How many sample rates to try */
|
||||||
static const int NumRatesToTry;
|
static const int NumRatesToTry;
|
||||||
|
|
||||||
|
bool ReversedTime() const
|
||||||
|
{
|
||||||
|
return mT1 < mT0;
|
||||||
|
}
|
||||||
|
double LimitStreamTime(double absoluteTime) const;
|
||||||
|
|
||||||
double NormalizeStreamTime(double absoluteTime) const;
|
double NormalizeStreamTime(double absoluteTime) const;
|
||||||
|
|
||||||
/** \brief Clean up after StartStream if it fails.
|
/** \brief Clean up after StartStream if it fails.
|
||||||
|
@ -1430,20 +1430,19 @@ double Envelope::SolveIntegralOfInverse( double t0, double area )
|
|||||||
{
|
{
|
||||||
if(area == 0.0)
|
if(area == 0.0)
|
||||||
return t0;
|
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();
|
unsigned int count = mEnv.Count();
|
||||||
if(count == 0) // 'empty' envelope
|
if(count == 0) // 'empty' envelope
|
||||||
return t0 + area * mDefaultValue;
|
return t0 + area * mDefaultValue;
|
||||||
|
|
||||||
double lastT, lastVal;
|
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
|
if(t0 < mEnv[0]->GetT()) // t0 preceding the first point
|
||||||
{
|
{
|
||||||
|
if (area < 0) {
|
||||||
|
return t0 + area * mEnv[0]->GetVal();
|
||||||
|
}
|
||||||
|
else {
|
||||||
i = 1;
|
i = 1;
|
||||||
lastT = mEnv[0]->GetT();
|
lastT = mEnv[0]->GetT();
|
||||||
lastVal = mEnv[0]->GetVal();
|
lastVal = mEnv[0]->GetVal();
|
||||||
@ -1452,9 +1451,21 @@ double Envelope::SolveIntegralOfInverse( double t0, double area )
|
|||||||
return t0 + area * mEnv[0]->GetVal();
|
return t0 + area * mEnv[0]->GetVal();
|
||||||
area -= added;
|
area -= added;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if(t0 >= mEnv[count - 1]->GetT()) // t0 following the last point
|
else if(t0 >= mEnv[count - 1]->GetT()) // t0 following the last point
|
||||||
{
|
{
|
||||||
|
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();
|
return t0 + area * mEnv[count - 1]->GetVal();
|
||||||
|
area -= added;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return t0 + area * mEnv[count - 1]->GetVal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else // t0 enclosed by points
|
else // t0 enclosed by points
|
||||||
{
|
{
|
||||||
@ -1463,9 +1474,35 @@ double Envelope::SolveIntegralOfInverse( double t0, double area )
|
|||||||
BinarySearchForTime(lo, hi, t0);
|
BinarySearchForTime(lo, hi, t0);
|
||||||
lastVal = InterpolatePoints(mEnv[lo]->GetVal(), mEnv[hi]->GetVal(), (t0 - mEnv[lo]->GetT()) / (mEnv[hi]->GetT() - mEnv[lo]->GetT()), mDB);
|
lastVal = InterpolatePoints(mEnv[lo]->GetVal(), mEnv[hi]->GetVal(), (t0 - mEnv[lo]->GetT()) / (mEnv[hi]->GetT() - mEnv[lo]->GetT()), mDB);
|
||||||
lastT = t0;
|
lastT = t0;
|
||||||
|
if (area < 0)
|
||||||
|
i = lo;
|
||||||
|
else
|
||||||
i = hi; // the point immediately after t0.
|
i = hi; // the point immediately after t0.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (area < 0) {
|
||||||
|
// loop BACKWARDS through the rest of the envelope points until we get to t1
|
||||||
|
// (which is less than t0)
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
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 {
|
||||||
// loop through the rest of the envelope points until we get to t1
|
// loop through the rest of the envelope points until we get to t1
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@ -1485,6 +1522,7 @@ double Envelope::SolveIntegralOfInverse( double t0, double area )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Envelope::print()
|
void Envelope::print()
|
||||||
{
|
{
|
||||||
|
@ -99,8 +99,8 @@ class Envelope : public XMLTagHandler {
|
|||||||
void Flatten(double value);
|
void Flatten(double value);
|
||||||
int GetDragPoint(void) {return mDragPoint;}
|
int GetDragPoint(void) {return mDragPoint;}
|
||||||
|
|
||||||
double GetMinValue() { return mMinValue; }
|
double GetMinValue() const { return mMinValue; }
|
||||||
double GetMaxValue() { return mMaxValue; }
|
double GetMaxValue() const { return mMaxValue; }
|
||||||
void SetRange(double minValue, double maxValue);
|
void SetRange(double minValue, double maxValue);
|
||||||
|
|
||||||
double ClampValue(double value) { return std::max(mMinValue, std::min(mMaxValue, value)); }
|
double ClampValue(double value) { return std::max(mMinValue, std::min(mMaxValue, value)); }
|
||||||
|
111
src/Mix.cpp
111
src/Mix.cpp
@ -423,10 +423,9 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||||||
int *queueStart, int *queueLen,
|
int *queueStart, int *queueLen,
|
||||||
Resample * pResample)
|
Resample * pResample)
|
||||||
{
|
{
|
||||||
double trackRate = track->GetRate();
|
const double trackRate = track->GetRate();
|
||||||
double initialWarp = mRate / trackRate;
|
const double initialWarp = mRate / trackRate;
|
||||||
double tstep = 1.0 / trackRate;
|
const double tstep = 1.0 / trackRate;
|
||||||
double t = (*pos - *queueLen) / trackRate;
|
|
||||||
int sampleSize = SAMPLE_SIZE(floatSample);
|
int sampleSize = SAMPLE_SIZE(floatSample);
|
||||||
|
|
||||||
sampleCount out = 0;
|
sampleCount out = 0;
|
||||||
@ -443,14 +442,15 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Find the last sample
|
// Find the last sample
|
||||||
sampleCount endPos;
|
|
||||||
double endTime = track->GetEndTime();
|
double endTime = track->GetEndTime();
|
||||||
if (endTime > mT1) {
|
double startTime = track->GetStartTime();
|
||||||
endPos = track->TimeToLongSamples(mT1);
|
const sampleCount endPos =
|
||||||
}
|
track->TimeToLongSamples(std::max(startTime, std::min(endTime, mT1)));
|
||||||
else {
|
const sampleCount startPos =
|
||||||
endPos = track->TimeToLongSamples(endTime);
|
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) {
|
while (out < mMaxOut) {
|
||||||
if (*queueLen < mProcessLen) {
|
if (*queueLen < mProcessLen) {
|
||||||
@ -458,15 +458,26 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||||||
memmove(queue, &queue[*queueStart], (*queueLen) * sampleSize);
|
memmove(queue, &queue[*queueStart], (*queueLen) * sampleSize);
|
||||||
*queueStart = 0;
|
*queueStart = 0;
|
||||||
|
|
||||||
int getLen = mQueueMaxLen - *queueLen;
|
int getLen =
|
||||||
|
std::min((backwards ? *pos - endPos : endPos - *pos),
|
||||||
|
sampleCount(mQueueMaxLen - *queueLen));
|
||||||
|
|
||||||
// Constrain
|
// Nothing to do if past end of play interval
|
||||||
if (*pos + getLen > endPos) {
|
|
||||||
getLen = endPos - *pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing to do if past end of track
|
|
||||||
if (getLen > 0) {
|
if (getLen > 0) {
|
||||||
|
if (backwards) {
|
||||||
|
track->Get((samplePtr)&queue[*queueLen],
|
||||||
|
floatSample,
|
||||||
|
*pos - (getLen - 1),
|
||||||
|
getLen);
|
||||||
|
|
||||||
|
track->GetEnvelopeValues(mEnvValues,
|
||||||
|
getLen,
|
||||||
|
(*pos - (getLen- 1)) / trackRate,
|
||||||
|
tstep);
|
||||||
|
|
||||||
|
*pos -= getLen;
|
||||||
|
}
|
||||||
|
else {
|
||||||
track->Get((samplePtr)&queue[*queueLen],
|
track->Get((samplePtr)&queue[*queueLen],
|
||||||
floatSample,
|
floatSample,
|
||||||
*pos,
|
*pos,
|
||||||
@ -477,12 +488,18 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||||||
(*pos) / trackRate,
|
(*pos) / trackRate,
|
||||||
tstep);
|
tstep);
|
||||||
|
|
||||||
|
*pos += getLen;
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < getLen; i++) {
|
for (int i = 0; i < getLen; i++) {
|
||||||
queue[(*queueLen) + i] *= mEnvValues[i];
|
queue[(*queueLen) + i] *= mEnvValues[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (backwards)
|
||||||
|
ReverseSamples((samplePtr)&queue[0], floatSample,
|
||||||
|
*queueStart, getLen);
|
||||||
|
|
||||||
*queueLen += 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
|
// 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
|
// 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
|
// 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.
|
// is unpredictable. Maybe it can be compensated later though.
|
||||||
factor *= mTimeTrack->ComputeWarpFactor(t, t + (double)thisProcessLen / trackRate);
|
if (backwards)
|
||||||
|
factor *= mTimeTrack->ComputeWarpFactor
|
||||||
|
(t - (double)thisProcessLen / trackRate + tstep, t + tstep);
|
||||||
|
else
|
||||||
|
factor *= mTimeTrack->ComputeWarpFactor
|
||||||
|
(t, t + (double)thisProcessLen / trackRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
int input_used;
|
int input_used;
|
||||||
@ -519,7 +541,7 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrack *track,
|
|||||||
*queueStart += input_used;
|
*queueStart += input_used;
|
||||||
*queueLen -= input_used;
|
*queueLen -= input_used;
|
||||||
out += outgen;
|
out += outgen;
|
||||||
t += (input_used / trackRate);
|
t += ((backwards ? -input_used : input_used) / trackRate);
|
||||||
|
|
||||||
if (last) {
|
if (last) {
|
||||||
break;
|
break;
|
||||||
@ -551,25 +573,47 @@ sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrack *track,
|
|||||||
{
|
{
|
||||||
int slen = mMaxOut;
|
int slen = mMaxOut;
|
||||||
int c;
|
int c;
|
||||||
double t = *pos / track->GetRate();
|
const double t = *pos / track->GetRate();
|
||||||
double trackEndTime = track->GetEndTime();
|
const double trackEndTime = track->GetEndTime();
|
||||||
double tEnd = trackEndTime > mT1 ? mT1 : trackEndTime;
|
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.
|
//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;
|
return 0;
|
||||||
//if we're about to approach the end of the track or selection, figure out how much we need to grab
|
//if we're about to approach the end of the track or selection, figure out how much we need to grab
|
||||||
|
if (backwards) {
|
||||||
|
if (t - slen/track->GetRate() < tEnd)
|
||||||
|
slen = (int)((t - tEnd) * track->GetRate() + 0.5);
|
||||||
|
}
|
||||||
|
else {
|
||||||
if (t + slen/track->GetRate() > tEnd)
|
if (t + slen/track->GetRate() > tEnd)
|
||||||
slen = (int)((tEnd - t) * track->GetRate() + 0.5);
|
slen = (int)((tEnd - t) * track->GetRate() + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
if (slen > mMaxOut)
|
if (slen > mMaxOut)
|
||||||
slen = mMaxOut;
|
slen = mMaxOut;
|
||||||
|
|
||||||
|
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->Get((samplePtr)mFloatBuffer, floatSample, *pos, slen);
|
||||||
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
|
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
|
||||||
for(int i=0; i<slen; i++)
|
for(int i=0; i<slen; i++)
|
||||||
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
|
||||||
|
|
||||||
|
*pos += slen;
|
||||||
|
}
|
||||||
|
|
||||||
for(c=0; c<mNumChannels; c++)
|
for(c=0; c<mNumChannels; c++)
|
||||||
if (mApplyTrackGains)
|
if (mApplyTrackGains)
|
||||||
mGains[c] = track->GetChannelGain(c);
|
mGains[c] = track->GetChannelGain(c);
|
||||||
@ -579,8 +623,6 @@ sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrack *track,
|
|||||||
MixBuffers(mNumChannels, channelFlags, mGains,
|
MixBuffers(mNumChannels, channelFlags, mGains,
|
||||||
(samplePtr)mFloatBuffer, mTemp, slen, mInterleaved);
|
(samplePtr)mFloatBuffer, mTemp, slen, mInterleaved);
|
||||||
|
|
||||||
*pos += slen;
|
|
||||||
|
|
||||||
return slen;
|
return slen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,7 +681,9 @@ sampleCount Mixer::Process(sampleCount maxToProcess)
|
|||||||
maxOut = out;
|
maxOut = out;
|
||||||
|
|
||||||
double t = (double)mSamplePos[i] / (double)track->GetRate();
|
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);
|
mTime = std::min(t, mT1);
|
||||||
}
|
}
|
||||||
if(mInterleaved) {
|
if(mInterleaved) {
|
||||||
@ -707,10 +751,11 @@ void Mixer::Reposition(double t)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
mTime = t;
|
mTime = t;
|
||||||
if( mTime < mT0 )
|
const bool backwards = (mT1 < mT0);
|
||||||
mTime = mT0;
|
if (backwards)
|
||||||
if( mTime > mT1 )
|
mTime = std::max(mT1, (std::min(mT0, mTime)));
|
||||||
mTime = mT1;
|
else
|
||||||
|
mTime = std::max(mT0, (std::min(mT1, mTime)));
|
||||||
|
|
||||||
for(i=0; i<mNumInputTracks; i++) {
|
for(i=0; i<mNumInputTracks; i++) {
|
||||||
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mTime);
|
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mTime);
|
||||||
|
@ -92,6 +92,24 @@ void ClearSamples(samplePtr src, sampleFormat format,
|
|||||||
memset(src + start*size, 0, len*size);
|
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,
|
void CopySamples(samplePtr src, sampleFormat srcFormat,
|
||||||
samplePtr dst, sampleFormat dstFormat,
|
samplePtr dst, sampleFormat dstFormat,
|
||||||
unsigned int len,
|
unsigned int len,
|
||||||
|
@ -70,6 +70,9 @@ void CopySamplesNoDither(samplePtr src, sampleFormat srcFormat,
|
|||||||
void ClearSamples(samplePtr buffer, sampleFormat format,
|
void ClearSamples(samplePtr buffer, sampleFormat format,
|
||||||
int start, int len);
|
int start, int len);
|
||||||
|
|
||||||
|
void ReverseSamples(samplePtr buffer, sampleFormat format,
|
||||||
|
int start, int len);
|
||||||
|
|
||||||
//
|
//
|
||||||
// This must be called on startup and everytime new ditherers
|
// This must be called on startup and everytime new ditherers
|
||||||
// are set in preferences.
|
// are set in preferences.
|
||||||
|
@ -473,13 +473,17 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
bool cutpreview, /* = false */
|
bool cutpreview, /* = false */
|
||||||
bool backwards /* = false */)
|
bool backwards /* = false */)
|
||||||
{
|
{
|
||||||
|
// Uncomment this for laughs!
|
||||||
|
// backwards = true;
|
||||||
|
|
||||||
double t0 = selectedRegion.t0();
|
double t0 = selectedRegion.t0();
|
||||||
double t1 = selectedRegion.t1();
|
double t1 = selectedRegion.t1();
|
||||||
// SelectedRegion guarantees t0 <= t1, so we need another boolean argument
|
// SelectedRegion guarantees t0 <= t1, so we need another boolean argument
|
||||||
// to indicate backwards play.
|
// to indicate backwards play.
|
||||||
const bool looped = options.playLooped;
|
const bool looped = options.playLooped;
|
||||||
|
|
||||||
wxASSERT(! backwards);
|
if (backwards)
|
||||||
|
std::swap(t0, t1);
|
||||||
|
|
||||||
SetPlay(true, looped, cutpreview);
|
SetPlay(true, looped, cutpreview);
|
||||||
|
|
||||||
@ -555,7 +559,9 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
t1 = t->GetEndTime();
|
t1 = t->GetEndTime();
|
||||||
}
|
}
|
||||||
else {
|
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
|
// the set intersection between the play region and the
|
||||||
// valid range maximum of lower bounds
|
// valid range maximum of lower bounds
|
||||||
@ -579,6 +585,9 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
t0 = maxofmins;
|
t0 = maxofmins;
|
||||||
t1 = minofmaxs;
|
t1 = minofmaxs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (backwards)
|
||||||
|
std::swap(t0, t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't play before 0...either shifted or latency corrected tracks
|
// Can't play before 0...either shifted or latency corrected tracks
|
||||||
@ -589,14 +598,19 @@ int ControlToolBar::PlayPlayRegion(const SelectedRegion &selectedRegion,
|
|||||||
|
|
||||||
int token = -1;
|
int token = -1;
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (t1 > t0) {
|
if (t1 != t0) {
|
||||||
if (cutpreview) {
|
if (cutpreview) {
|
||||||
|
const double tless = std::min(t0, t1);
|
||||||
|
const double tgreater = std::max(t0, t1);
|
||||||
double beforeLen, afterLen;
|
double beforeLen, afterLen;
|
||||||
gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
|
gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
|
||||||
gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
|
gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
|
||||||
double tcp0 = t0-beforeLen;
|
double tcp0 = tless-beforeLen;
|
||||||
double tcp1 = (t1+afterLen) - (t1-t0);
|
double diff = tgreater - tless;
|
||||||
SetupCutPreviewTracks(tcp0, t0, t1, tcp1);
|
double tcp1 = (tgreater+afterLen) - diff;
|
||||||
|
SetupCutPreviewTracks(tcp0, tless, tgreater, tcp1);
|
||||||
|
if (backwards)
|
||||||
|
std::swap(tcp0, tcp1);
|
||||||
if (mCutPreviewTracks)
|
if (mCutPreviewTracks)
|
||||||
{
|
{
|
||||||
AudioIOStartStreamOptions myOptions = options;
|
AudioIOStartStreamOptions myOptions = options;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user