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

Member Enumeration Documentation

anonymous enum
private
Enumerator
Size 

Definition at line 962 of file AudioIO.cpp.

962 { 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 548 of file AudioIO.cpp.

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

Definition at line 581 of file AudioIO.cpp.

581 {}

Member Function Documentation

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

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

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

Definition at line 583 of file AudioIO.cpp.

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

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

Definition at line 593 of file AudioIO.cpp.

References mAvailable, mNudged, and mUpdating.

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

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

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

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

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

Member Data Documentation

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

Definition at line 976 of file AudioIO.cpp.

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

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

Definition at line 971 of file AudioIO.cpp.

Referenced by Transformer().

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

Definition at line 972 of file AudioIO.cpp.

Referenced by Transformer().

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

Definition at line 963 of file AudioIO.cpp.

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

wxLongLong AudioIO::ScrubQueue::mLastScrubTimeMillis
private

Definition at line 968 of file AudioIO.cpp.

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

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

Definition at line 970 of file AudioIO.cpp.

Referenced by Transformer().

unsigned AudioIO::ScrubQueue::mLeadingIdx
private

Definition at line 966 of file AudioIO.cpp.

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

const long AudioIO::ScrubQueue::mMaxDebt
private

Definition at line 973 of file AudioIO.cpp.

Referenced by Transformer().

unsigned AudioIO::ScrubQueue::mMiddleIdx
private

Definition at line 965 of file AudioIO.cpp.

Referenced by Consumer(), and Transformer().

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

Definition at line 977 of file AudioIO.cpp.

Referenced by Nudge(), and Transformer().

const double AudioIO::ScrubQueue::mRate
private

Definition at line 967 of file AudioIO.cpp.

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

unsigned AudioIO::ScrubQueue::mTrailingIdx
private

Definition at line 964 of file AudioIO.cpp.

Referenced by Consumer(), and Producer().

wxMutex AudioIO::ScrubQueue::mUpdating
mutableprivate

Definition at line 975 of file AudioIO.cpp.

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


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