mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-06 15:19:29 +02:00
Bug1967: scrub shouldn't be clicky...
... And this further simplifies the use of the clock. Rely on the regularity of spacing between calls to FillBuffers at commit a62cf53
This commit is contained in:
parent
e724ef0793
commit
03def8dbaf
@ -541,49 +541,40 @@ struct AudioIO::ScrubState
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Get(sampleCount &startSample, sampleCount &endSample,
|
void Get(sampleCount &startSample, sampleCount &endSample,
|
||||||
sampleCount &duration)
|
sampleCount inDuration, sampleCount &duration)
|
||||||
{
|
{
|
||||||
// Called by the thread that calls AudioIO::FillBuffers
|
// Called by the thread that calls AudioIO::FillBuffers
|
||||||
startSample = endSample = duration = -1LL;
|
startSample = endSample = duration = -1LL;
|
||||||
Duration dd { *this };
|
|
||||||
if (dd.duration <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Message message( mMessage.Read() );
|
Message message( mMessage.Read() );
|
||||||
if ( !mStarted ) {
|
if ( !mStarted ) {
|
||||||
|
// Make some initial silence
|
||||||
const sampleCount s0 { llrint( mRate *
|
const sampleCount s0 { llrint( mRate *
|
||||||
std::max( message.options.minTime,
|
std::max( message.options.minTime,
|
||||||
std::min( message.options.maxTime, mStartTime ) ) ) };
|
std::min( message.options.maxTime, mStartTime ) ) ) };
|
||||||
const sampleCount s1 ( message.options.bySpeed
|
mData.mS0 = mData.mS1 = s0;
|
||||||
? s0.as_double() +
|
mData.mGoal = -1;
|
||||||
llrint(dd.duration.as_double() * message.end) // end is a speed
|
mData.mDuration = duration = inDuration;
|
||||||
: llrint(message.end * mRate) // end is a time
|
mData.mSilence = 0;
|
||||||
);
|
|
||||||
auto actualDuration = std::max(sampleCount{1}, dd.duration);
|
|
||||||
auto success = mData.Init(nullptr,
|
|
||||||
s0, s1, actualDuration, message.options, mRate);
|
|
||||||
if ( !success ) {
|
|
||||||
// If not, we can wait to enqueue again later
|
|
||||||
dd.Cancel();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mStarted = true;
|
mStarted = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Data newData;
|
Data newData;
|
||||||
auto previous = &mData;
|
inDuration += mAccumulatedSeekDuration;
|
||||||
|
|
||||||
// Use the previous end as NEW start.
|
// Use the previous end as NEW start.
|
||||||
const auto s0 = previous->mS1;
|
const auto s0 = mData.mS1;
|
||||||
const sampleCount s1 ( message.options.bySpeed
|
const sampleCount s1 ( message.options.bySpeed
|
||||||
? s0.as_double() +
|
? s0.as_double() +
|
||||||
lrint(dd.duration.as_double() * message.end) // end is a speed
|
lrint(inDuration.as_double() * message.end) // end is a speed
|
||||||
: lrint(message.end * mRate) // end is a time
|
: lrint(message.end * mRate) // end is a time
|
||||||
);
|
);
|
||||||
auto success =
|
auto success =
|
||||||
newData.Init(previous, s0, s1, dd.duration, message.options, mRate);
|
newData.Init(mData, s0, s1, inDuration, message.options, mRate);
|
||||||
if ( !success ) {
|
if (success)
|
||||||
dd.Cancel();
|
mAccumulatedSeekDuration = 0;
|
||||||
|
else {
|
||||||
|
mAccumulatedSeekDuration += inDuration;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mData = newData;
|
mData = newData;
|
||||||
@ -639,10 +630,11 @@ private:
|
|||||||
, mSilence(0)
|
, mSilence(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool Init(Data *previous, sampleCount s0, sampleCount s1,
|
bool Init(Data &rPrevious, sampleCount s0, sampleCount s1,
|
||||||
sampleCount duration,
|
sampleCount duration,
|
||||||
const ScrubbingOptions &options, double rate)
|
const ScrubbingOptions &options, double rate)
|
||||||
{
|
{
|
||||||
|
auto previous = &rPrevious;
|
||||||
auto origDuration = duration;
|
auto origDuration = duration;
|
||||||
mSilence = 0;
|
mSilence = 0;
|
||||||
|
|
||||||
@ -665,7 +657,6 @@ private:
|
|||||||
adjustedSpeed = true;
|
adjustedSpeed = true;
|
||||||
}
|
}
|
||||||
else if (!adjustStart &&
|
else if (!adjustStart &&
|
||||||
previous &&
|
|
||||||
previous->mGoal >= 0 &&
|
previous->mGoal >= 0 &&
|
||||||
previous->mGoal == s1)
|
previous->mGoal == s1)
|
||||||
{
|
{
|
||||||
@ -776,39 +767,11 @@ private:
|
|||||||
sampleCount mSilence;
|
sampleCount mSilence;
|
||||||
};
|
};
|
||||||
|
|
||||||
using Clock = std::chrono::steady_clock;
|
|
||||||
|
|
||||||
struct Duration {
|
|
||||||
Duration (ScrubState &queue_) : queue(queue_)
|
|
||||||
{
|
|
||||||
do {
|
|
||||||
clockTime = Clock::now();
|
|
||||||
using Seconds = std::chrono::duration<double>;
|
|
||||||
const auto elapsed = std::chrono::duration_cast<Seconds>(
|
|
||||||
clockTime - queue.mLastScrubTime);
|
|
||||||
duration = static_cast<long long>( queue.mRate * elapsed.count() );
|
|
||||||
} while( duration <= 0 && (::wxMilliSleep(1), true) );
|
|
||||||
}
|
|
||||||
~Duration ()
|
|
||||||
{
|
|
||||||
if(!cancelled)
|
|
||||||
queue.mLastScrubTime = clockTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cancel() { cancelled = true; }
|
|
||||||
|
|
||||||
ScrubState &queue;
|
|
||||||
Clock::time_point clockTime;
|
|
||||||
sampleCount duration;
|
|
||||||
bool cancelled { false };
|
|
||||||
};
|
|
||||||
|
|
||||||
double mStartTime;
|
double mStartTime;
|
||||||
bool mStarted{ false };
|
bool mStarted{ false };
|
||||||
std::atomic<bool> mStopped { false };
|
std::atomic<bool> mStopped { false };
|
||||||
Data mData;
|
Data mData;
|
||||||
const double mRate;
|
const double mRate;
|
||||||
Clock::time_point mLastScrubTime { Clock::now() };
|
|
||||||
struct Message {
|
struct Message {
|
||||||
Message() = default;
|
Message() = default;
|
||||||
Message(const Message&) = default;
|
Message(const Message&) = default;
|
||||||
@ -816,6 +779,7 @@ private:
|
|||||||
ScrubbingOptions options;
|
ScrubbingOptions options;
|
||||||
};
|
};
|
||||||
MessageBuffer<Message> mMessage;
|
MessageBuffer<Message> mMessage;
|
||||||
|
sampleCount mAccumulatedSeekDuration{};
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -3927,7 +3891,7 @@ void AudioIO::FillBuffers()
|
|||||||
{
|
{
|
||||||
sampleCount startSample, endSample;
|
sampleCount startSample, endSample;
|
||||||
mScrubState->Get(
|
mScrubState->Get(
|
||||||
startSample, endSample, mScrubDuration);
|
startSample, endSample, available, mScrubDuration);
|
||||||
if (mScrubDuration < 0)
|
if (mScrubDuration < 0)
|
||||||
{
|
{
|
||||||
// Can't play anything
|
// Can't play anything
|
||||||
|
@ -364,7 +364,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
|
|||||||
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
|
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
|
||||||
options.pScrubbingOptions = &mOptions;
|
options.pScrubbingOptions = &mOptions;
|
||||||
options.timeTrack = NULL;
|
options.timeTrack = NULL;
|
||||||
mOptions.delay = (ScrubPollInterval_ms * 0.9 / 1000.0);
|
mOptions.delay = (ScrubPollInterval_ms / 1000.0);
|
||||||
mOptions.isPlayingAtSpeed = false;
|
mOptions.isPlayingAtSpeed = false;
|
||||||
mOptions.minSpeed = 0.0;
|
mOptions.minSpeed = 0.0;
|
||||||
#ifdef USE_TRANSCRIPTION_TOOLBAR
|
#ifdef USE_TRANSCRIPTION_TOOLBAR
|
||||||
@ -470,7 +470,7 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1)
|
|||||||
AudioIOStartStreamOptions options(mProject->GetSpeedPlayOptions());
|
AudioIOStartStreamOptions options(mProject->GetSpeedPlayOptions());
|
||||||
options.pScrubbingOptions = &mOptions;
|
options.pScrubbingOptions = &mOptions;
|
||||||
options.timeTrack = NULL;
|
options.timeTrack = NULL;
|
||||||
mOptions.delay = (ScrubPollInterval_ms * 0.9 / 1000.0);
|
mOptions.delay = (ScrubPollInterval_ms / 1000.0);
|
||||||
mOptions.minSpeed = speed -0.01;
|
mOptions.minSpeed = speed -0.01;
|
||||||
mOptions.maxSpeed = speed +0.01;
|
mOptions.maxSpeed = speed +0.01;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user