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 544 of file AudioIO.cpp.

Member Enumeration Documentation

anonymous enum
private
Enumerator
Size 

Definition at line 960 of file AudioIO.cpp.

960 { 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 546 of file AudioIO.cpp.

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

Definition at line 579 of file AudioIO.cpp.

579 {}

Member Function Documentation

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

Definition at line 747 of file AudioIO.cpp.

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

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

Definition at line 581 of file AudioIO.cpp.

References mEntries, mLeadingIdx, mRate, AudioIO::ScrubQueue::Entry::mS1, mUpdating, and Size.

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

Definition at line 591 of file AudioIO.cpp.

References mAvailable, mNudged, and mUpdating.

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

Definition at line 598 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.

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

Definition at line 663 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.

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

Member Data Documentation

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

Definition at line 974 of file AudioIO.cpp.

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

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

Definition at line 969 of file AudioIO.cpp.

Referenced by Transformer().

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

Definition at line 970 of file AudioIO.cpp.

Referenced by Transformer().

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

Definition at line 961 of file AudioIO.cpp.

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

wxLongLong AudioIO::ScrubQueue::mLastScrubTimeMillis
private

Definition at line 966 of file AudioIO.cpp.

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

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

Definition at line 968 of file AudioIO.cpp.

Referenced by Transformer().

unsigned AudioIO::ScrubQueue::mLeadingIdx
private

Definition at line 964 of file AudioIO.cpp.

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

const long AudioIO::ScrubQueue::mMaxDebt
private

Definition at line 971 of file AudioIO.cpp.

Referenced by Transformer().

unsigned AudioIO::ScrubQueue::mMiddleIdx
private

Definition at line 963 of file AudioIO.cpp.

Referenced by Consumer(), and Transformer().

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

Definition at line 975 of file AudioIO.cpp.

Referenced by Nudge(), and Transformer().

const double AudioIO::ScrubQueue::mRate
private

Definition at line 965 of file AudioIO.cpp.

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

unsigned AudioIO::ScrubQueue::mTrailingIdx
private

Definition at line 962 of file AudioIO.cpp.

Referenced by Consumer(), and Producer().

wxMutex AudioIO::ScrubQueue::mUpdating
mutableprivate

Definition at line 973 of file AudioIO.cpp.

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


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