1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-12-18 14:41:20 +01:00

Don't let the consumers discard too much from the scrub queue...

Producer adds silences when needed, so that the accounting in Transformer()
is correct
This commit is contained in:
Paul Licameli
2016-05-24 12:13:59 -04:00
parent b6764d1bf7
commit 9a256d3790
2 changed files with 85 additions and 47 deletions

View File

@@ -376,29 +376,31 @@ struct AudioIO::ScrubQueue
const ScrubbingOptions &options)
: mTrailingIdx(0)
, mMiddleIdx(1)
, mLeadingIdx(2)
, mLeadingIdx(1)
, mRate(rate)
, mLastScrubTimeMillis(startClockMillis)
, mUpdating()
, mMaxDebt { maxDebt }
{
// Ignore options.adjustStart, pass false.
bool success = InitEntry(mEntries[mMiddleIdx], nullptr,
t0, t1, maxSpeed, false, false, options);
if (!success)
{
// StartClock equals now? Really?
--mLastScrubTimeMillis;
success = InitEntry(mEntries[mMiddleIdx], nullptr,
t0, t1, maxSpeed, false, false, options);
const long s0 = std::max(options.minSample, std::min(options.maxSample,
lrint(t0 * mRate)
));
const long s1 = lrint(t1 * mRate);
Duration dd { *this };
long actualDuration = std::max(1L, dd.duration);
auto success = mEntries[mMiddleIdx].Init(nullptr,
s0, s1, actualDuration, maxSpeed, options);
if (success)
++mLeadingIdx;
else {
// If not, we can wait to enqueue again later
dd.Cancel();
}
wxASSERT(success);
// So the play indicator starts out unconfused:
{
Entry &entry = mEntries[mTrailingIdx];
entry.mS0 = entry.mS1 = mEntries[mMiddleIdx].mS0;
entry.mS0 = entry.mS1 = s0;
entry.mPlayed = entry.mDuration = 1;
}
}
@@ -428,23 +430,50 @@ struct AudioIO::ScrubQueue
// MAY ADVANCE mLeadingIdx, BUT IT NEVER CATCHES UP TO mTrailingIdx.
wxMutexLocker locker(mUpdating);
const unsigned next = (mLeadingIdx + 1) % Size;
bool result = true;
unsigned next = (mLeadingIdx + 1) % Size;
if (next != mTrailingIdx)
{
Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size];
auto current = &mEntries[mLeadingIdx];
auto previous = &mEntries[(mLeadingIdx + Size - 1) % Size];
// Use the previous end as NEW start.
const double startTime = previous.mS1 / mRate;
// Might reject the request because of zero duration,
// or a too-short "stutter"
const bool success =
(InitEntry(mEntries[mLeadingIdx], &previous, startTime, end, maxSpeed,
options.enqueueBySpeed, options.adjustStart, options));
if (success) {
const long s0 = previous->mS1;
Duration dd { *this };
const auto &origDuration = dd.duration;
if (origDuration <= 0)
return false;
auto actualDuration = origDuration;
const long s1 = options.enqueueBySpeed
? s0 + lrint(origDuration * end) // end is a speed
: lrint(end * mRate); // end is a time
auto success =
current->Init(previous, s0, s1, actualDuration, maxSpeed, options);
if (success)
mLeadingIdx = next;
mAvailable.Signal();
else {
dd.Cancel();
return false;
}
return success;
// Fill up the queue with some silence if there was trimming
wxASSERT(actualDuration <= origDuration);
if (actualDuration < origDuration) {
next = (mLeadingIdx + 1) % Size;
if (next != mTrailingIdx) {
previous = &mEntries[(mLeadingIdx + Size - 1) % Size];
current = &mEntries[mLeadingIdx];
current->InitSilent(*previous, origDuration - actualDuration);
mLeadingIdx = next;
}
else
// Oops, can't enqueue the silence -- so do what?
;
}
mAvailable.Signal();
return result;
}
else
{
@@ -584,10 +613,12 @@ private:
, mPlayed(0)
{}
bool Init(Entry *previous, long s0, long s1, long duration,
double maxSpeed, bool adjustStart,
const ScrubbingOptions &options)
bool Init(Entry *previous, long s0, long s1,
long &duration /* in/out */,
double maxSpeed, const ScrubbingOptions &options)
{
const bool &adjustStart = options.adjustStart;
wxASSERT(duration > 0);
double speed = static_cast<double>(std::abs(s1 - s0)) / duration;
bool adjustedSpeed = false;
@@ -684,6 +715,14 @@ private:
return true;
}
void InitSilent(const Entry &previous, long duration)
{
mGoal = previous.mGoal;
mS0 = mS1 = previous.mS1;
mPlayed = 0;
mDuration = duration;
}
double GetTime(double rate) const
{
return
@@ -709,23 +748,23 @@ private:
long mPlayed;
};
bool InitEntry(Entry &entry, Entry *previous, double t0, double end, double maxSpeed,
bool bySpeed, bool adjustStart,
const ScrubbingOptions &options)
{
const wxLongLong clockTime(::wxGetLocalTimeMillis());
const long duration =
mRate * (clockTime - mLastScrubTimeMillis).ToDouble() / 1000.0;
const long s0 = t0 * mRate;
const long s1 = bySpeed
? s0 + lrint(duration * end) // end is a speed
: lrint(end * mRate); // end is a time
const bool success =
entry.Init(previous, s0, s1, duration, maxSpeed, adjustStart, options);
if (success)
mLastScrubTimeMillis = clockTime;
return success;
}
struct Duration {
Duration (ScrubQueue &queue_) : queue(queue_) {}
~Duration ()
{
if(!cancelled)
queue.mLastScrubTimeMillis = clockTime;
}
void Cancel() { cancelled = true; }
ScrubQueue &queue;
const wxLongLong clockTime { ::wxGetLocalTimeMillis() };
const long duration { static_cast<long>
(queue.mRate * (clockTime - queue.mLastScrubTimeMillis).ToDouble() / 1000.0)
};
bool cancelled { false };
};
enum { Size = 10 };
Entry mEntries[Size];

View File

@@ -169,9 +169,8 @@ class AUDACITY_DLL_API AudioIO final {
* If options.adjustStart is true, then when mouse movement exceeds maximum scrub speed,
* adjust the beginning of the scrub interval rather than the end, so that
* the scrub skips or "stutters" to stay near the cursor.
* But if the "stutter" is too short for the minimum, then there is no effect
* on the work queue.
* Return true if some work was really enqueued.
* Return true if some sound was really enqueued.
* But if the "stutter" is too short for the minimum, enqueue nothing and return false.
*/
bool EnqueueScrub(double endTimeOrSpeed, double maxSpeed, const ScrubbingOptions &options);