Audacity 3.2.0
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 "MessageBuffer.h"
16#include "Mix.h"
17#include "Observer.h"
18#include <atomic>
19#include <chrono>
20#include <vector>
21
22#include <wx/event.h>
23
24class AudacityProject;
26class BoundedEnvelope;
27using PRCrossfadeData = std::vector< std::vector < float > >;
28class PlayRegionEvent;
29
30constexpr size_t TimeQueueGrainSize = 2000;
31
33 double mPreRoll{};
34 double mLatencyCorrection{}; // negative value usually
35 double mDuration{};
37
38 // These are initialized by the main thread, then updated
39 // only by the thread calling TrackBufferExchange:
40 double mPosition{};
42
43 double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
44 double ToConsume() const;
45 double Consumed() const;
46 double ToDiscard() const;
47};
48
49class Mixer;
50struct PlaybackSchedule;
51
54 const size_t frames;
55 const size_t toProduce;
56
58
61 size_t available, size_t frames_, size_t toProduce_)
62 : frames{ std::min(available, frames_) }
63 , toProduce{ std::min(toProduce_, frames) }
64 {}
65};
66
68
75public:
76 using Duration = std::chrono::duration<double>;
77
79
80 virtual ~PlaybackPolicy() = 0;
81
83 virtual void Initialize( PlaybackSchedule &schedule, double rate );
84
86 virtual void Finalize( PlaybackSchedule &schedule );
87
90
92 struct BufferTimes {
96 };
99
101
103 virtual bool AllowSeek( PlaybackSchedule &schedule );
104
106 virtual bool Done( PlaybackSchedule &schedule,
107 unsigned long outputFrames
108 );
109
111
114 virtual double OffsetTrackTime( PlaybackSchedule &schedule, double offset );
115
117
119 virtual std::chrono::milliseconds
120 SleepInterval( PlaybackSchedule &schedule );
121
124 size_t available
125 );
126
128
138 virtual std::pair<double, double>
140 double trackTime, size_t nSamples );
141
142 using Mixers = std::vector<std::unique_ptr<Mixer>>;
143
145
148 virtual bool RepositionPlayback(
149 PlaybackSchedule &schedule, const Mixers &playbackMixers,
150 size_t frames,
151 size_t available
152 );
153
155
156 virtual bool Looping( const PlaybackSchedule &schedule ) const;
157
158protected:
159 double mRate = 0;
160};
161
162struct AUDACITY_DLL_API PlaybackSchedule {
163
165 double mT0;
167 double mT1;
172 std::atomic<double> mTime;
173
178
183
184 // mWarpedTime and mWarpedLength are irrelevant when scrubbing,
185 // else they are used in updating mTime,
186 // and when not scrubbing or playing looped, mTime is also used
187 // in the test for termination of playback.
188
189 // with ComputeWarpedLength, it is now possible the calculate the warped length with 100% accuracy
190 // (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
191
193
195 /*
196 Holds track time values corresponding to every nth sample in the
197 playback buffers, for the large n == TimeQueueGrainSize.
198
199 The "producer" is the Audio thread that fetches samples from tracks and
200 fills the playback RingBuffers. The "consumer" is the high-latency
201 PortAudio thread that drains the RingBuffers. The atomics in the
202 RingBuffer implement lock-free synchronization.
203
204 This other structure relies on the RingBuffer's synchronization, and adds
205 other information to the stream of samples: which track times they
206 correspond to.
207
208 The consumer thread uses that information, and also makes known to the main
209 thread, what the last consumed track time is. The main thread can use that
210 for other purposes such as refreshing the display of the play head position.
211 */
212 class TimeQueue {
213 public:
214
216
217 void Clear();
218 void Resize(size_t size);
219
221
223 void Producer( PlaybackSchedule &schedule, PlaybackSlice slice );
224
226 double GetLastTime() const;
227
228 void SetLastTime(double time);
229
231
233 double Consumer( size_t nSamples, double rate );
234
236
238
239 void Prime( double time );
240
241 private:
242 struct Record {
243 double timeValue;
244 // More fields to come
245 };
246 using Records = std::vector<Record>;
248 double mLastTime {};
249 struct Cursor {
250 size_t mIndex {};
251 size_t mRemainder {};
252 };
255 } mTimeQueue;
256
257 PlaybackPolicy &GetPolicy();
258 const PlaybackPolicy &GetPolicy() const;
259
260 void Init(
261 double t0, double t1,
262 const AudioIOStartStreamOptions &options,
263 const RecordingSchedule *pRecordingSchedule );
264
274 double ComputeWarpedLength(double t0, double t1) const;
275
283 double SolveWarpedLength(double t0, double length) const;
284
286 bool ReversedTime() const
287 {
288 return mT1 < mT0;
289 }
290
295 double GetTrackTime() const
296 { return mTime.load(std::memory_order_relaxed); }
297
300 void SetTrackTime( double time )
301 { mTime.store(time, std::memory_order_relaxed); }
302
303 void ResetMode() {
304 mPolicyValid.store(false, std::memory_order_release);
305 }
306
307 // Convert time between mT0 and argument to real duration, according to
308 // time track if one is given; result is always nonnegative
309 double RealDuration(double trackTime1) const;
310
311 // Convert time between mT0 and argument to real duration, according to
312 // time track if one is given; may be negative
313 double RealDurationSigned(double trackTime1) const;
314
315 // How much real time left?
316 double RealTimeRemaining() const;
317
318 // Advance the real time position
319 void RealTimeAdvance( double increment );
320
321 // Determine starting duration within the first pass -- sometimes not
322 // zero
323 void RealTimeInit( double trackTime );
324
325 void RealTimeRestart();
326
327private:
328 std::unique_ptr<PlaybackPolicy> mpPlaybackPolicy;
329 std::atomic<bool> mPolicyValid{ false };
330};
331
333 : public PlaybackPolicy
334 , public NonInterferingBase
335 , public wxEvtHandler
336{
337public:
339 double trackEndTime, double loopEndTime,
340 bool loopEnabled, bool variableSpeed);
342
343 void Initialize( PlaybackSchedule &schedule, double rate ) override;
344
346
348
349 bool Done( PlaybackSchedule &schedule, unsigned long ) override;
350
352 PlaybackSchedule &schedule, size_t available ) override;
353
354 std::pair<double, double>
356 double trackTime, size_t nSamples ) override;
357
359 PlaybackSchedule &schedule, const Mixers &playbackMixers,
360 size_t frames, size_t available ) override;
361
362 bool Looping( const PlaybackSchedule & ) const override;
363
364private:
365 bool RevertToOldDefault( const PlaybackSchedule &schedule ) const;
367 void OnPlaySpeedChange(wxCommandEvent &evt);
368 void WriteMessage();
369 double GetPlaySpeed();
370
372
373 // The main thread writes changes in response to user events, and
374 // the audio thread later reads, and changes the playback.
375 struct SlotData {
377 double mT0;
378 double mT1;
380 };
382
384
385 double mLastPlaySpeed{ 1.0 };
386 const double mTrackEndTime;
388 size_t mRemaining{ 0 };
389 bool mProgress{ true };
390 bool mLoopEnabled{ true };
391 bool mVariableSpeed{ false };
392};
393#endif
int min(int a, int b)
constexpr size_t TimeQueueGrainSize
std::vector< std::vector< float > > PRCrossfadeData
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
Communicate data atomically from one writer thread to one reader.
Definition: MessageBuffer.h:23
Functions for doing the mixdown of the tracks.
Definition: Mix.h:26
bool Done(PlaybackSchedule &schedule, unsigned long) override
Returns true if schedule.GetTrackTime() has reached the end of playback.
void Initialize(PlaybackSchedule &schedule, double rate) override
Called before starting an audio stream.
MessageBuffer< SlotData > mMessageChannel
~NewDefaultPlaybackPolicy() override
Mixer::WarpOptions MixerWarpOptions(PlaybackSchedule &schedule) override
Options to use when constructing mixers for each playback track.
AudacityProject & mProject
bool Looping(const PlaybackSchedule &) const override
PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available) override
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
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.
Observer::Subscription mSubscription
bool RevertToOldDefault(const PlaybackSchedule &schedule) const
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...
NewDefaultPlaybackPolicy(AudacityProject &project, double trackEndTime, double loopEndTime, bool loopEnabled, bool variableSpeed)
void OnPlayRegionChange(Observer::Message)
BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule) override
Provide hints for construction of playback RingBuffer objects.
void OnPlaySpeedChange(wxCommandEvent &evt)
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
Directs which parts of tracks to fetch for playback.
virtual BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule)
Provide hints for construction of playback RingBuffer objects.
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.
virtual double OffsetTrackTime(PlaybackSchedule &schedule, double offset)
Called when the play head needs to jump a certain distance.
virtual bool AllowSeek(PlaybackSchedule &schedule)
Whether repositioning commands are allowed during playback.
std::vector< std::unique_ptr< Mixer > > Mixers
virtual void Finalize(PlaybackSchedule &schedule)
Called after stopping of an audio stream or an unsuccessful start.
virtual bool Looping(const PlaybackSchedule &schedule) const
virtual Mixer::WarpOptions MixerWarpOptions(PlaybackSchedule &schedule)
Options to use when constructing mixers for each playback track.
virtual void Initialize(PlaybackSchedule &schedule, double rate)
Called before starting an audio stream.
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...
virtual PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available)
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
virtual bool Done(PlaybackSchedule &schedule, unsigned long outputFrames)
Returns true if schedule.GetTrackTime() has reached the end of playback.
virtual ~PlaybackPolicy()=0
std::chrono::duration< double > Duration
virtual std::chrono::milliseconds SleepInterval(PlaybackSchedule &schedule)
How long to wait between calls to AudioIO::TrackBufferExchange.
NonInterfering< Cursor > mHead
Aligned to avoid false sharing.
std::vector< Record > Records
double ComputeWarpedLength(const Envelope &env, double t0, double t1)
Definition: Ruler.cpp:989
double SolveWarpedLength(const Envelope &env, double t0, double length)
Definition: Ruler.cpp:994
STL namespace.
struct holding stream options, including a pointer to the time warp info and AudioIOListener and whet...
Definition: AudioIOBase.h:44
Immutable structure is an argument to Mixer's constructor.
Definition: MixerOptions.h:54
Default message type for Publisher.
Definition: Observer.h:26
Times are in seconds.
Duration latency
Try not to let ring buffer contents fall below this.
Duration ringBufferDelay
Length of ring buffer.
Duration batchSize
Try to put at least this much into the ring buffer in each pass.
std::unique_ptr< PlaybackPolicy > mpPlaybackPolicy
double GetTrackTime() const
Get current track time value, unadjusted.
double mT0
Playback starts at offset of mT0, which is measured in seconds.
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...
bool ReversedTime() const
True if the end time is before the start time.
const BoundedEnvelope * mEnvelope
void SetTrackTime(double time)
Set current track time value, unadjusted.
std::atomic< double > mTime
Describes an amount of contiguous (but maybe time-warped) data to be extracted from tracks to play.
const size_t toProduce
Not more than frames; the difference will be trailing silence.
PlaybackSlice(size_t available, size_t frames_, size_t toProduce_)
Constructor enforces some invariants.
const size_t frames
Total number of frames to be buffered.
double TotalCorrection() const
double ToDiscard() const
double ToConsume() const
double Consumed() const
PRCrossfadeData mCrossfadeData