Audacity  3.0.3
PlaybackSchedule.h
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  PlaybackSchedule.h
6 
7  Paul Licameli split from AudioIOBase.h
8 
9  **********************************************************************/
10 
11 #ifndef __AUDACITY_PLAYBACK_SCHEDULE__
12 #define __AUDACITY_PLAYBACK_SCHEDULE__
13 
14 #include "MemoryX.h"
15 #include "Mix.h"
16 #include <atomic>
17 #include <chrono>
18 #include <vector>
19 
21 class BoundedEnvelope;
22 using PRCrossfadeData = std::vector< std::vector < float > >;
23 class PlayRegionEvent;
24 
25 constexpr size_t TimeQueueGrainSize = 2000;
26 
28 
33 template<typename Data>
35  struct UpdateSlot {
36  std::atomic<bool> mBusy{ false };
37  Data mData;
38  };
40 
41  std::atomic<unsigned char> mLastWrittenSlot{ 0 };
42 
43 public:
44  void Initialize();
45 
47  Data Read();
48 
50  void Write( const Data &data );
52  void Write( Data &&data );
53 };
54 
55 template<typename Data>
57 {
58  for (auto &slot : mSlots)
59  // Lock both slots first, maybe spinning a little
60  while ( slot.mBusy.exchange( true, std::memory_order_acquire ) )
61  {}
62 
63  mSlots[0].mData = {};
64  mSlots[1].mData = {};
65  mLastWrittenSlot.store( 0, std::memory_order_relaxed );
66 
67  for (auto &slot : mSlots)
68  slot.mBusy.exchange( false, std::memory_order_release );
69 }
70 
71 template<typename Data>
73 {
74  // Whichever slot was last written, prefer to read that.
75  auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
76  idx = 1 - idx;
77  bool wasBusy = false;
78  do {
79  // This loop is unlikely to execute twice, but it might because the
80  // producer thread is writing a slot.
81  idx = 1 - idx;
82  wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
83  } while ( wasBusy );
84 
85  // Copy the slot
86  auto result = std::move( mSlots[idx].mData );
87 
88  mSlots[idx].mBusy.store( false, std::memory_order_release );
89 
90  return result;
91 }
92 
93 template<typename Data>
94 void MessageBuffer<Data>::Write( const Data &data )
95 {
96  // Whichever slot was last written, prefer to write the other.
97  auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
98  bool wasBusy = false;
99  do {
100  // This loop is unlikely to execute twice, but it might because the
101  // consumer thread is reading a slot.
102  idx = 1 - idx;
103  wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
104  } while ( wasBusy );
105 
106  mSlots[idx].mData = data;
107  mLastWrittenSlot.store( idx, std::memory_order_relaxed );
108 
109  mSlots[idx].mBusy.store( false, std::memory_order_release );
110 }
111 
112 template<typename Data>
113 void MessageBuffer<Data>::Write( Data &&data )
114 {
115  // Whichever slot was last written, prefer to write the other.
116  auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
117  bool wasBusy = false;
118  do {
119  // This loop is unlikely to execute twice, but it might because the
120  // consumer thread is reading a slot.
121  idx = 1 - idx;
122  wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
123  } while ( wasBusy );
124 
125  mSlots[idx].mData = std::move( data );
126  mLastWrittenSlot.store( idx, std::memory_order_relaxed );
127 
128  mSlots[idx].mBusy.store( false, std::memory_order_release );
129 }
130 
132  double mPreRoll{};
133  double mLatencyCorrection{}; // negative value usually
134  double mDuration{};
136 
137  // These are initialized by the main thread, then updated
138  // only by the thread calling TrackBufferExchange:
139  double mPosition{};
141 
142  double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
143  double ToConsume() const;
144  double Consumed() const;
145  double ToDiscard() const;
146 };
147 
148 class Mixer;
149 struct PlaybackSchedule;
150 
153  const size_t frames;
154  const size_t toProduce;
155 
157 
160  size_t available, size_t frames_, size_t toProduce_)
161  : frames{ std::min(available, frames_) }
162  , toProduce{ std::min(toProduce_, frames) }
163  {}
164 };
165 
167 
174 public:
176 
177  virtual ~PlaybackPolicy() = 0;
178 
180  virtual void Initialize( PlaybackSchedule &schedule, double rate );
181 
183  virtual void Finalize( PlaybackSchedule &schedule );
184 
187 
189  struct BufferTimes {
190  double batchSize;
191  double latency;
193  };
196 
198 
200  virtual bool AllowSeek( PlaybackSchedule &schedule );
201 
203  virtual bool Done( PlaybackSchedule &schedule,
204  unsigned long outputFrames
205  );
206 
208 
211  virtual double OffsetTrackTime( PlaybackSchedule &schedule, double offset );
212 
214 
216  virtual std::chrono::milliseconds
217  SleepInterval( PlaybackSchedule &schedule );
218 
221  size_t available
222  );
223 
225 
235  virtual std::pair<double, double>
237  double trackTime, size_t nSamples );
238 
240 
246  virtual void MessageConsumer( PlaybackSchedule &schedule );
247 
248  using Mixers = std::vector<std::unique_ptr<Mixer>>;
249 
251 
254  virtual bool RepositionPlayback(
255  PlaybackSchedule &schedule, const Mixers &playbackMixers,
256  size_t frames,
257  size_t available
258  );
259 
261 
262  virtual bool Looping( const PlaybackSchedule &schedule ) const;
263 
264 protected:
265  double mRate = 0;
266 };
267 
268 struct AUDACITY_DLL_API PlaybackSchedule {
269 
271  double mT0;
273  double mT1;
278  std::atomic<double> mTime;
279 
283  double mWarpedTime;
284 
289 
290  // mWarpedTime and mWarpedLength are irrelevant when scrubbing,
291  // else they are used in updating mTime,
292  // and when not scrubbing or playing looped, mTime is also used
293  // in the test for termination of playback.
294 
295  // with ComputeWarpedLength, it is now possible the calculate the warped length with 100% accuracy
296  // (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
297 
299 
301  /*
302  Holds track time values corresponding to every nth sample in the
303  playback buffers, for the large n == TimeQueueGrainSize.
304 
305  The "producer" is the Audio thread that fetches samples from tracks and
306  fills the playback RingBuffers. The "consumer" is the high-latency
307  PortAudio thread that drains the RingBuffers. The atomics in the
308  RingBuffer implement lock-free synchronization.
309 
310  This other structure relies on the RingBuffer's synchronization, and adds
311  other information to the stream of samples: which track times they
312  correspond to.
313 
314  The consumer thread uses that information, and also makes known to the main
315  thread, what the last consumed track time is. The main thread can use that
316  for other purposes such as refreshing the display of the play head position.
317  */
318  class TimeQueue {
319  public:
320 
322 
323  void Clear();
324  void Resize(size_t size);
325 
327 
329  void Producer( PlaybackSchedule &schedule, size_t nSamples );
330 
332  double GetLastTime() const;
333 
334  void SetLastTime(double time);
335 
337 
339  double Consumer( size_t nSamples, double rate );
340 
342 
344 
345  void Prime( double time );
346 
347  private:
348  struct Record {
349  double timeValue;
350  // More fields to come
351  };
352  using Records = std::vector<Record>;
354  double mLastTime {};
355  struct Cursor {
356  size_t mIndex {};
357  size_t mRemainder {};
358  };
361  } mTimeQueue;
362 
363  PlaybackPolicy &GetPolicy();
364  const PlaybackPolicy &GetPolicy() const;
365 
366  // The main thread writes changes in response to user events, and
367  // the audio thread later reads, and changes the playback.
368  struct SlotData {
369  double mT0;
370  double mT1;
371  };
373 
374  void Init(
375  double t0, double t1,
376  const AudioIOStartStreamOptions &options,
377  const RecordingSchedule *pRecordingSchedule );
378 
388  double ComputeWarpedLength(double t0, double t1) const;
389 
397  double SolveWarpedLength(double t0, double length) const;
398 
399  void MessageProducer( PlayRegionEvent &evt );
400 
402  bool ReversedTime() const
403  {
404  return mT1 < mT0;
405  }
406 
411  double GetTrackTime() const
412  { return mTime.load(std::memory_order_relaxed); }
413 
416  void SetTrackTime( double time )
417  { mTime.store(time, std::memory_order_relaxed); }
418 
424  double ClampTrackTime( double trackTime ) const;
425 
426  void ResetMode() {
427  mPolicyValid.store(false, std::memory_order_release);
428  }
429 
430  // Convert time between mT0 and argument to real duration, according to
431  // time track if one is given; result is always nonnegative
432  double RealDuration(double trackTime1) const;
433 
434  // How much real time left?
435  double RealTimeRemaining() const;
436 
437  // Advance the real time position
438  void RealTimeAdvance( double increment );
439 
440  // Determine starting duration within the first pass -- sometimes not
441  // zero
442  void RealTimeInit( double trackTime );
443 
444  void RealTimeRestart();
445 
446 private:
447  std::unique_ptr<PlaybackPolicy> mpPlaybackPolicy;
448  std::atomic<bool> mPolicyValid{ false };
449 };
450 
451 class LoopingPlaybackPolicy final : public PlaybackPolicy {
452 public:
454 
456 
457  bool Done( PlaybackSchedule &schedule, unsigned long ) override;
459  PlaybackSchedule &schedule, size_t available ) override;
460 
461  std::pair<double, double>
463  double trackTime, size_t nSamples ) override;
464 
465  void MessageConsumer( PlaybackSchedule &schedule ) override;
466 
467  bool RepositionPlayback(
468  PlaybackSchedule &schedule, const Mixers &playbackMixers,
469  size_t frames, size_t available ) override;
470 
471  bool Looping( const PlaybackSchedule & ) const override;
472 
473 private:
474  size_t mRemaining{ 0 };
475  bool mProgress{ true };
476  bool mKicked{ false };
477 };
478 #endif
size
size_t size
Definition: ffmpeg-2.3.6-single-header.h:412
Init
Definition: ModuleManager.h:152
RecordingSchedule::mLatencyCorrection
double mLatencyCorrection
Definition: PlaybackSchedule.h:133
MessageBuffer::Write
void Write(Data &&data)
Move data in.
Definition: PlaybackSchedule.h:113
RecordingSchedule::mPosition
double mPosition
Definition: PlaybackSchedule.h:139
LoopingPlaybackPolicy::MessageConsumer
void MessageConsumer(PlaybackSchedule &schedule) override
May be called between AdvancedTrackTime() and RepositionPlayback()
Definition: PlaybackSchedule.cpp:220
PlaybackSchedule::SetTrackTime
void SetTrackTime(double time)
Set current track time value, unadjusted.
Definition: PlaybackSchedule.h:416
LoopingPlaybackPolicy
Definition: PlaybackSchedule.h:451
PlaybackSchedule::SlotData::mT0
double mT0
Definition: PlaybackSchedule.h:369
PlaybackSchedule::mT1
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...
Definition: PlaybackSchedule.h:273
MessageBuffer::Initialize
void Initialize()
Definition: PlaybackSchedule.h:56
MessageBuffer::Write
void Write(const Data &data)
Copy data in.
Definition: PlaybackSchedule.h:94
MessageBuffer::mLastWrittenSlot
std::atomic< unsigned char > mLastWrittenSlot
Definition: PlaybackSchedule.h:41
PlaybackSchedule::mT0
double mT0
Playback starts at offset of mT0, which is measured in seconds.
Definition: PlaybackSchedule.h:271
PlaybackSchedule::mWarpedTime
double mWarpedTime
Definition: PlaybackSchedule.h:283
Mixer::WarpOptions
Definition: Mix.h:81
Mix.h
PlaybackSchedule::TimeQueue::mTail
NonInterfering< Cursor > mTail
Definition: PlaybackSchedule.h:360
PlaybackSlice::toProduce
const size_t toProduce
Not more than frames; the difference will be trailing silence.
Definition: PlaybackSchedule.h:154
PlaybackPolicy::MessageConsumer
virtual void MessageConsumer(PlaybackSchedule &schedule)
May be called between AdvancedTrackTime() and RepositionPlayback()
Definition: PlaybackSchedule.cpp:63
PlaybackSchedule::mMessageChannel
MessageBuffer< SlotData > mMessageChannel
Definition: PlaybackSchedule.h:372
PlaybackPolicy::Looping
virtual bool Looping(const PlaybackSchedule &schedule) const
Definition: PlaybackSchedule.cpp:125
PlaybackPolicy::~PlaybackPolicy
virtual ~PlaybackPolicy()=0
PlaybackPolicy::Done
virtual bool Done(PlaybackSchedule &schedule, unsigned long outputFrames)
Returns true if schedule.GetTrackTime() has reached the end of playback.
Definition: PlaybackSchedule.cpp:43
PlaybackSchedule::TimeQueue::Record::timeValue
double timeValue
Definition: PlaybackSchedule.h:349
PlaybackSchedule::TimeQueue::Cursor
Definition: PlaybackSchedule.h:355
PlaybackPolicy::BufferTimes::latency
double latency
Try not to let ring buffer contents fall below this.
Definition: PlaybackSchedule.h:191
anonymous_namespace{Ruler.cpp}::SolveWarpedLength
double SolveWarpedLength(const Envelope &env, double t0, double length)
Definition: Ruler.cpp:994
LoopingPlaybackPolicy::Looping
bool Looping(const PlaybackSchedule &) const override
Definition: PlaybackSchedule.cpp:273
PlaybackPolicy::BufferTimes::ringBufferDelay
double ringBufferDelay
Length of ring buffer in seconds.
Definition: PlaybackSchedule.h:192
PRCrossfadeData
std::vector< std::vector< float > > PRCrossfadeData
Definition: PlaybackSchedule.h:22
PlaybackPolicy::BufferTimes
Times are in seconds.
Definition: PlaybackSchedule.h:189
LoopingPlaybackPolicy::Done
bool Done(PlaybackSchedule &schedule, unsigned long) override
Returns true if schedule.GetTrackTime() has reached the end of playback.
Definition: PlaybackSchedule.cpp:160
LoopingPlaybackPolicy::GetPlaybackSlice
PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available) override
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
Definition: PlaybackSchedule.cpp:166
RecordingSchedule::TotalCorrection
double TotalCorrection() const
Definition: PlaybackSchedule.h:142
LoopingPlaybackPolicy::RepositionPlayback
bool RepositionPlayback(PlaybackSchedule &schedule, const Mixers &playbackMixers, size_t frames, size_t available) override
AudioIO::FillPlayBuffers calls this to update its cursors into tracks for changes of position or spee...
Definition: PlaybackSchedule.cpp:249
PlaybackPolicy::Mixers
std::vector< std::unique_ptr< Mixer > > Mixers
Definition: PlaybackSchedule.h:248
PlaybackPolicy::AdvancedTrackTime
virtual std::pair< double, double > AdvancedTrackTime(PlaybackSchedule &schedule, double trackTime, size_t nSamples)
Compute a new point in a track's timeline from an old point and a real duration.
Definition: PlaybackSchedule.cpp:100
PlayRegionEvent
Definition: ViewInfo.h:118
PlaybackPolicy::AllowSeek
virtual bool AllowSeek(PlaybackSchedule &schedule)
Whether repositioning commands are allowed during playback.
Definition: PlaybackSchedule.cpp:38
BoundedEnvelope
Definition: Envelope.h:284
RecordingSchedule::ToConsume
double ToConsume() const
Definition: PlaybackSchedule.cpp:369
PlaybackSlice::PlaybackSlice
PlaybackSlice(size_t available, size_t frames_, size_t toProduce_)
Constructor enforces some invariants.
Definition: PlaybackSchedule.h:159
PlaybackSchedule::TimeQueue
A circular buffer.
Definition: PlaybackSchedule.h:318
LoopingPlaybackPolicy::mProgress
bool mProgress
Definition: PlaybackSchedule.h:475
PlaybackSchedule::ResetMode
void ResetMode()
Definition: PlaybackSchedule.h:426
PlaybackSchedule::SlotData::mT1
double mT1
Definition: PlaybackSchedule.h:370
PlaybackSchedule::ReversedTime
bool ReversedTime() const
True if the end time is before the start time.
Definition: PlaybackSchedule.h:402
PlaybackSchedule::TimeQueue::Record
Definition: PlaybackSchedule.h:348
MessageBuffer::mSlots
NonInterfering< UpdateSlot > mSlots[2]
Definition: PlaybackSchedule.h:39
PlaybackPolicy::OffsetTrackTime
virtual double OffsetTrackTime(PlaybackSchedule &schedule, double offset)
Called when the play head needs to jump a certain distance.
Definition: PlaybackSchedule.cpp:54
PlaybackSlice::frames
const size_t frames
Total number of frames to be buffered.
Definition: PlaybackSchedule.h:153
PlaybackPolicy::SleepInterval
virtual std::chrono::milliseconds SleepInterval(PlaybackSchedule &schedule)
How long to wait between calls to AudioIO::TrackBufferExchange.
Definition: PlaybackSchedule.cpp:67
MessageBuffer::UpdateSlot::mBusy
std::atomic< bool > mBusy
Definition: PlaybackSchedule.h:36
PlaybackPolicy::MixerWarpOptions
virtual Mixer::WarpOptions MixerWarpOptions(PlaybackSchedule &schedule)
Options to use when constructing mixers for each playback track.
Definition: PlaybackSchedule.cpp:27
PlaybackSchedule
Definition: PlaybackSchedule.h:268
MessageBuffer
Communicate data atomically from one writer thread to one reader.
Definition: PlaybackSchedule.h:34
RecordingSchedule::mCrossfadeData
PRCrossfadeData mCrossfadeData
Definition: PlaybackSchedule.h:135
PlaybackPolicy::RepositionPlayback
virtual bool RepositionPlayback(PlaybackSchedule &schedule, const Mixers &playbackMixers, size_t frames, size_t available)
AudioIO::FillPlayBuffers calls this to update its cursors into tracks for changes of position or spee...
Definition: PlaybackSchedule.cpp:119
RefreshCode::Resize
@ Resize
Definition: RefreshCode.h:28
PlaybackPolicy::Initialize
virtual void Initialize(PlaybackSchedule &schedule, double rate)
Called before starting an audio stream.
Definition: PlaybackSchedule.cpp:21
PlaybackSchedule::TimeQueue::mData
Records mData
Definition: PlaybackSchedule.h:353
AudioIOStartStreamOptions
struct holding stream options, including a pointer to the time warp info and AudioIOListener and whet...
Definition: AudioIOBase.h:44
PlaybackPolicy::SuggestedBufferTimes
virtual BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule)
Provide hints for construction of playback RingBuffer objects.
Definition: PlaybackSchedule.cpp:33
LoopingPlaybackPolicy::SuggestedBufferTimes
BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule) override
Provide hints for construction of playback RingBuffer objects.
Definition: PlaybackSchedule.cpp:153
NonInterfering
Definition: MemoryX.h:623
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
PlaybackSchedule::TimeQueue::Records
std::vector< Record > Records
Definition: PlaybackSchedule.h:352
PlaybackSchedule::mTime
std::atomic< double > mTime
Definition: PlaybackSchedule.h:278
TimeQueueGrainSize
constexpr size_t TimeQueueGrainSize
Definition: PlaybackSchedule.h:25
MemoryX.h
anonymous_namespace{Ruler.cpp}::ComputeWarpedLength
double ComputeWarpedLength(const Envelope &env, double t0, double t1)
Definition: Ruler.cpp:989
LoopingPlaybackPolicy::~LoopingPlaybackPolicy
~LoopingPlaybackPolicy() override
PlaybackPolicy::GetPlaybackSlice
virtual PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available)
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
Definition: PlaybackSchedule.cpp:74
PlaybackSlice
Describes an amount of contiguous (but maybe time-warped) data to be extracted from tracks to play.
Definition: PlaybackSchedule.h:152
PlaybackSchedule::GetTrackTime
double GetTrackTime() const
Get current track time value, unadjusted.
Definition: PlaybackSchedule.h:411
RecordingSchedule::ToDiscard
double ToDiscard() const
Definition: PlaybackSchedule.cpp:379
PlaybackSchedule::mWarpedLength
double mWarpedLength
Definition: PlaybackSchedule.h:288
MessageBuffer::UpdateSlot
Definition: PlaybackSchedule.h:35
LoopingPlaybackPolicy::mRemaining
size_t mRemaining
Definition: PlaybackSchedule.h:474
MessageBuffer::UpdateSlot::mData
Data mData
Definition: PlaybackSchedule.h:37
RecordingSchedule
Definition: PlaybackSchedule.h:131
PlaybackSchedule::mpPlaybackPolicy
std::unique_ptr< PlaybackPolicy > mpPlaybackPolicy
Definition: PlaybackSchedule.h:447
PlaybackSchedule::SlotData
Definition: PlaybackSchedule.h:368
PlaybackPolicy::mRate
double mRate
Definition: PlaybackSchedule.h:265
MessageBuffer::Read
Data Read()
Move data out (if available), or else copy it out.
Definition: PlaybackSchedule.h:72
RecordingSchedule::Consumed
double Consumed() const
Definition: PlaybackSchedule.cpp:374
LoopingPlaybackPolicy::mKicked
bool mKicked
Definition: PlaybackSchedule.h:476
PlaybackPolicy::Finalize
virtual void Finalize(PlaybackSchedule &schedule)
Called after stopping of an audio stream or an unsuccessful start.
Definition: PlaybackSchedule.cpp:25
LoopingPlaybackPolicy::AdvancedTrackTime
std::pair< double, double > AdvancedTrackTime(PlaybackSchedule &schedule, double trackTime, size_t nSamples) override
Compute a new point in a track's timeline from an old point and a real duration.
Definition: PlaybackSchedule.cpp:195
RecordingSchedule::mLatencyCorrected
bool mLatencyCorrected
Definition: PlaybackSchedule.h:140
PlaybackSchedule::mEnvelope
const BoundedEnvelope * mEnvelope
Definition: PlaybackSchedule.h:298
Mixer
Functions for doing the mixdown of the tracks.
Definition: Mix.h:76
RecordingSchedule::mDuration
double mDuration
Definition: PlaybackSchedule.h:134
PlaybackPolicy
Directs which parts of tracks to fetch for playback.
Definition: PlaybackSchedule.h:173
PlaybackPolicy::BufferTimes::batchSize
double batchSize
Try to put at least this much into the ring buffer in each pass.
Definition: PlaybackSchedule.h:190
RecordingSchedule::mPreRoll
double mPreRoll
Definition: PlaybackSchedule.h:132