Audacity  2.2.2
Classes | Public Member Functions | Private Types | Private Attributes | List of all members
AudioIO::ScrubQueue Struct Reference

Classes

struct  Duration
 
struct  Entry
 

Public Member Functions

 ScrubQueue (double t0, double t1, wxLongLong startClockMillis, double rate, long maxDebt, const ScrubbingOptions &options)
 
 ~ScrubQueue ()
 
double LastTimeInQueue () const
 
void Nudge ()
 
bool Producer (double end, const ScrubbingOptions &options)
 
void Transformer (sampleCount &startSample, sampleCount &endSample, sampleCount &duration, Maybe< wxMutexLocker > &cleanup)
 
double Consumer (unsigned long frames)
 

Private Types

enum  { Size = 10 }
 

Private Attributes

Entry mEntries [Size]
 
unsigned mTrailingIdx
 
unsigned mMiddleIdx
 
unsigned mLeadingIdx
 
const double mRate
 
wxLongLong mLastScrubTimeMillis
 
wxLongLong mLastTransformerTimeMillis { -1LL }
 
sampleCount mCredit { 0 }
 
sampleCount mDebt { 0 }
 
const long mMaxDebt
 
wxMutex mUpdating
 
wxCondition mAvailable { mUpdating }
 
bool mNudged { false }
 

Detailed Description

Definition at line 545 of file AudioIO.cpp.

Member Enumeration Documentation

anonymous enum
private
Enumerator
Size 

Definition at line 961 of file AudioIO.cpp.

961 { Size = 10 };

Constructor & Destructor Documentation

AudioIO::ScrubQueue::ScrubQueue ( double  t0,
double  t1,
wxLongLong  startClockMillis,
double  rate,
long  maxDebt,
const ScrubbingOptions options 
)
inline

Definition at line 547 of file AudioIO.cpp.

550  : mTrailingIdx(0)
551  , mMiddleIdx(1)
552  , mLeadingIdx(1)
553  , mRate(rate)
554  , mLastScrubTimeMillis(startClockMillis)
555  , mMaxDebt { maxDebt }
556  , mUpdating()
557  {
558  const auto s0 = std::max(options.minSample, std::min(options.maxSample,
559  sampleCount(lrint(t0 * mRate))
560  ));
561  const auto s1 = sampleCount(lrint(t1 * mRate));
562  Duration dd { *this };
563  auto actualDuration = std::max(sampleCount{1}, dd.duration);
564  auto success = mEntries[mMiddleIdx].Init(nullptr,
565  s0, s1, actualDuration, options);
566  if (success)
567  ++mLeadingIdx;
568  else {
569  // If not, we can wait to enqueue again later
570  dd.Cancel();
571  }
572 
573  // So the play indicator starts out unconfused:
574  {
575  Entry &entry = mEntries[mTrailingIdx];
576  entry.mS0 = entry.mS1 = s0;
577  entry.mPlayed = entry.mDuration = 1;
578  }
579  }
sampleCount maxSample
Definition: Scrubbing.h:44
wxLongLong mLastScrubTimeMillis
Definition: AudioIO.cpp:967
unsigned mTrailingIdx
Definition: AudioIO.cpp:963
Entry mEntries[Size]
Definition: AudioIO.cpp:962
const double mRate
Definition: AudioIO.cpp:966
bool Init(Entry *previous, sampleCount s0, sampleCount s1, sampleCount &duration, const ScrubbingOptions &options)
Definition: AudioIO.cpp:795
unsigned mMiddleIdx
Definition: AudioIO.cpp:964
const long mMaxDebt
Definition: AudioIO.cpp:972
#define lrint(dbl)
Definition: float_cast.h:136
int min(int a, int b)
unsigned mLeadingIdx
Definition: AudioIO.cpp:965
sampleCount minSample
Definition: Scrubbing.h:45
AudioIO::ScrubQueue::~ScrubQueue ( )
inline

Definition at line 580 of file AudioIO.cpp.

580 {}

Member Function Documentation

double AudioIO::ScrubQueue::Consumer ( unsigned long  frames)
inline

Definition at line 748 of file AudioIO.cpp.

References sampleCount::as_size_t(), AudioIO::ScrubQueue::Entry::GetTime(), AudioIO::ScrubQueue::Entry::mDuration, mEntries, mMiddleIdx, AudioIO::ScrubQueue::Entry::mPlayed, mRate, mTrailingIdx, mUpdating, and Size.

749  {
750  // Portaudio thread consumes samples and must update
751  // the time for the indicator. This finds the time value.
752 
753  // MAY ADVANCE mTrailingIdx, BUT IT NEVER CATCHES UP TO mMiddleIdx.
754 
755  wxMutexLocker locker(mUpdating);
756 
757  // Mark entries as partly or fully "consumed" for
758  // purposes of mTime update. It should not happen that
759  // frames exceed the total of samples to be consumed,
760  // but in that case we just use the t1 of the latest entry.
761  while (1)
762  {
763  Entry *pEntry = &mEntries[mTrailingIdx];
764  auto remaining = pEntry->mDuration - pEntry->mPlayed;
765  if (frames >= remaining)
766  {
767  // remaining is not more than frames
768  frames -= remaining.as_size_t();
769  pEntry->mPlayed = pEntry->mDuration;
770  }
771  else
772  {
773  pEntry->mPlayed += frames;
774  break;
775  }
776  const unsigned next = (mTrailingIdx + 1) % Size;
777  if (next == mMiddleIdx)
778  break;
779  mTrailingIdx = next;
780  }
782  }
double GetTime(double rate) const
Definition: AudioIO.cpp:918
unsigned mTrailingIdx
Definition: AudioIO.cpp:963
Entry mEntries[Size]
Definition: AudioIO.cpp:962
const double mRate
Definition: AudioIO.cpp:966
unsigned mMiddleIdx
Definition: AudioIO.cpp:964
double AudioIO::ScrubQueue::LastTimeInQueue ( ) const
inline

Definition at line 582 of file AudioIO.cpp.

References sampleCount::as_double(), mEntries, mLeadingIdx, mRate, AudioIO::ScrubQueue::Entry::mS1, mUpdating, and Size.

583  {
584  // Needed by the main thread sometimes
585  wxMutexLocker locker(mUpdating);
586  const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size];
587  return previous.mS1.as_double() / mRate;
588  }
Entry mEntries[Size]
Definition: AudioIO.cpp:962
const double mRate
Definition: AudioIO.cpp:966
unsigned mLeadingIdx
Definition: AudioIO.cpp:965
void AudioIO::ScrubQueue::Nudge ( )
inline

Definition at line 592 of file AudioIO.cpp.

References mAvailable, mNudged, and mUpdating.

593  {
594  wxMutexLocker locker(mUpdating);
595  mNudged = true;
596  mAvailable.Signal();
597  }
wxCondition mAvailable
Definition: AudioIO.cpp:975
bool AudioIO::ScrubQueue::Producer ( double  end,
const ScrubbingOptions options 
)
inline

Definition at line 599 of file AudioIO.cpp.

References AudioIO::ScrubQueue::Duration::duration, ScrubbingOptions::enqueueBySpeed, AudioIO::ScrubQueue::Entry::InitSilent(), lrint, mAvailable, mEntries, mLeadingIdx, mRate, AudioIO::ScrubQueue::Entry::mS1, mTrailingIdx, mUpdating, and Size.

600  {
601  // Main thread indicates a scrubbing interval
602 
603  // MAY ADVANCE mLeadingIdx, BUT IT NEVER CATCHES UP TO mTrailingIdx.
604 
605  wxMutexLocker locker(mUpdating);
606  bool result = true;
607  unsigned next = (mLeadingIdx + 1) % Size;
608  if (next != mTrailingIdx)
609  {
610  auto current = &mEntries[mLeadingIdx];
611  auto previous = &mEntries[(mLeadingIdx + Size - 1) % Size];
612 
613  // Use the previous end as NEW start.
614  const auto s0 = previous->mS1;
615  Duration dd { *this };
616  const auto &origDuration = dd.duration;
617  if (origDuration <= 0)
618  return false;
619 
620  auto actualDuration = origDuration;
621  const sampleCount s1 ( options.enqueueBySpeed
622  ? s0.as_double() +
623  lrint(origDuration.as_double() * end) // end is a speed
624  : lrint(end * mRate) // end is a time
625  );
626  auto success =
627  current->Init(previous, s0, s1, actualDuration, options);
628  if (success)
629  mLeadingIdx = next;
630  else {
631  dd.Cancel();
632  return false;
633  }
634 
635  // Fill up the queue with some silence if there was trimming
636  wxASSERT(actualDuration <= origDuration);
637  if (actualDuration < origDuration) {
638  next = (mLeadingIdx + 1) % Size;
639  if (next != mTrailingIdx) {
640  previous = &mEntries[(mLeadingIdx + Size - 1) % Size];
641  current = &mEntries[mLeadingIdx];
642  current->InitSilent(*previous, origDuration - actualDuration);
643  mLeadingIdx = next;
644  }
645  else
646  // Oops, can't enqueue the silence -- so do what?
647  ;
648  }
649 
650  mAvailable.Signal();
651  return result;
652  }
653  else
654  {
655  // ??
656  // Queue wasn't long enough. Write side (UI thread)
657  // has overtaken the trailing read side (PortAudio thread), despite
658  // my comments above! We lose some work requests then.
659  // wxASSERT(false);
660  return false;
661  }
662  }
unsigned mTrailingIdx
Definition: AudioIO.cpp:963
Entry mEntries[Size]
Definition: AudioIO.cpp:962
const double mRate
Definition: AudioIO.cpp:966
#define lrint(dbl)
Definition: float_cast.h:136
wxCondition mAvailable
Definition: AudioIO.cpp:975
unsigned mLeadingIdx
Definition: AudioIO.cpp:965
sampleCount mS1
Definition: AudioIO.cpp:928
bool enqueueBySpeed
Definition: Scrubbing.h:47
void InitSilent(const Entry &previous, sampleCount duration)
Definition: AudioIO.cpp:910
void AudioIO::ScrubQueue::Transformer ( sampleCount startSample,
sampleCount endSample,
sampleCount duration,
Maybe< wxMutexLocker > &  cleanup 
)
inline

Definition at line 664 of file AudioIO.cpp.

References Maybe< X >::create(), mAvailable, mCredit, mDebt, AudioIO::ScrubQueue::Entry::mDuration, mEntries, mLastTransformerTimeMillis, mLeadingIdx, mMaxDebt, mMiddleIdx, mNudged, mRate, AudioIO::ScrubQueue::Entry::mS0, AudioIO::ScrubQueue::Entry::mS1, mUpdating, and Size.

667  {
668  // Audio thread is ready for the next interval.
669 
670  // MAY ADVANCE mMiddleIdx, WHICH MAY EQUAL mLeadingIdx, BUT DOES NOT PASS IT.
671 
672  bool checkDebt = false;
673  if (!cleanup) {
674  cleanup.create(mUpdating);
675 
676  // Check for cancellation of work only when re-enetering the cricial section
677  checkDebt = true;
678  }
679  while(!mNudged && mMiddleIdx == mLeadingIdx)
680  mAvailable.Wait();
681 
682  mNudged = false;
683 
684  auto now = ::wxGetLocalTimeMillis();
685 
686  if (checkDebt &&
687  mLastTransformerTimeMillis >= 0 && // Not the first time for this scrub
688  mMiddleIdx != mLeadingIdx) {
689  // There is work in the queue, but if Producer is outrunning us, discard some,
690  // which may make a skip yet keep playback better synchronized with user gestures.
691  const auto interval = (now - mLastTransformerTimeMillis).ToDouble() / 1000.0;
692  //const Entry &previous = mEntries[(mMiddleIdx + Size - 1) % Size];
693  const auto deficit =
694  static_cast<long>(interval * mRate) - // Samples needed in the last time interval
695  mCredit; // Samples done in the last time interval
696  mCredit = 0;
697  mDebt += deficit;
698  auto toDiscard = mDebt - mMaxDebt;
699  while (toDiscard > 0 && mMiddleIdx != mLeadingIdx) {
700  // Cancel some debt (discard some NEW work)
701  auto &entry = mEntries[mMiddleIdx];
702  auto &dur = entry.mDuration;
703  if (toDiscard >= dur) {
704  // Discard entire queue entry
705  mDebt -= dur;
706  toDiscard -= dur;
707  dur = 0; // So Consumer() will handle abandoned entry correctly
708  mMiddleIdx = (mMiddleIdx + 1) % Size;
709  }
710  else {
711  // Adjust the start time
712  auto &start = entry.mS0;
713  const auto end = entry.mS1;
714  const auto ratio = toDiscard.as_double() / dur.as_double();
715  const sampleCount adjustment(
716  std::abs((end - start).as_long_long()) * ratio
717  );
718  if (start <= end)
719  start += adjustment;
720  else
721  start -= adjustment;
722 
723  mDebt -= toDiscard;
724  dur -= toDiscard;
725  toDiscard = 0;
726  }
727  }
728  }
729 
730  if (mMiddleIdx != mLeadingIdx) {
731  // There is still work in the queue, after cancelling debt
732  Entry &entry = mEntries[mMiddleIdx];
733  startSample = entry.mS0;
734  endSample = entry.mS1;
735  duration = entry.mDuration;
736  mMiddleIdx = (mMiddleIdx + 1) % Size;
737  mCredit += duration;
738  }
739  else {
740  // We got the shut-down signal, or we got nudged, or we discarded all the work.
741  startSample = endSample = duration = -1L;
742  }
743 
744  if (checkDebt)
746  }
sampleCount mDuration
Definition: AudioIO.cpp:933
Entry mEntries[Size]
Definition: AudioIO.cpp:962
const double mRate
Definition: AudioIO.cpp:966
unsigned mMiddleIdx
Definition: AudioIO.cpp:964
const long mMaxDebt
Definition: AudioIO.cpp:972
wxCondition mAvailable
Definition: AudioIO.cpp:975
wxLongLong mLastTransformerTimeMillis
Definition: AudioIO.cpp:969
unsigned mLeadingIdx
Definition: AudioIO.cpp:965
sampleCount mCredit
Definition: AudioIO.cpp:970
sampleCount mDebt
Definition: AudioIO.cpp:971
void create(Args &&...args)
Definition: MemoryX.h:653

Member Data Documentation

wxCondition AudioIO::ScrubQueue::mAvailable { mUpdating }
mutableprivate

Definition at line 975 of file AudioIO.cpp.

Referenced by Nudge(), Producer(), and Transformer().

sampleCount AudioIO::ScrubQueue::mCredit { 0 }
private

Definition at line 970 of file AudioIO.cpp.

Referenced by Transformer().

sampleCount AudioIO::ScrubQueue::mDebt { 0 }
private

Definition at line 971 of file AudioIO.cpp.

Referenced by Transformer().

Entry AudioIO::ScrubQueue::mEntries[Size]
private

Definition at line 962 of file AudioIO.cpp.

Referenced by Consumer(), LastTimeInQueue(), Producer(), and Transformer().

wxLongLong AudioIO::ScrubQueue::mLastScrubTimeMillis
private

Definition at line 967 of file AudioIO.cpp.

Referenced by AudioIO::ScrubQueue::Duration::~Duration().

wxLongLong AudioIO::ScrubQueue::mLastTransformerTimeMillis { -1LL }
private

Definition at line 969 of file AudioIO.cpp.

Referenced by Transformer().

unsigned AudioIO::ScrubQueue::mLeadingIdx
private

Definition at line 965 of file AudioIO.cpp.

Referenced by LastTimeInQueue(), Producer(), and Transformer().

const long AudioIO::ScrubQueue::mMaxDebt
private

Definition at line 972 of file AudioIO.cpp.

Referenced by Transformer().

unsigned AudioIO::ScrubQueue::mMiddleIdx
private

Definition at line 964 of file AudioIO.cpp.

Referenced by Consumer(), and Transformer().

bool AudioIO::ScrubQueue::mNudged { false }
private

Definition at line 976 of file AudioIO.cpp.

Referenced by Nudge(), and Transformer().

const double AudioIO::ScrubQueue::mRate
private

Definition at line 966 of file AudioIO.cpp.

Referenced by Consumer(), LastTimeInQueue(), Producer(), and Transformer().

unsigned AudioIO::ScrubQueue::mTrailingIdx
private

Definition at line 963 of file AudioIO.cpp.

Referenced by Consumer(), and Producer().

wxMutex AudioIO::ScrubQueue::mUpdating
mutableprivate

Definition at line 974 of file AudioIO.cpp.

Referenced by Consumer(), LastTimeInQueue(), Nudge(), Producer(), and Transformer().


The documentation for this struct was generated from the following file: