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 
20 #include <wx/event.h>
21 
22 class AudacityProject;
23 struct AudioIOStartStreamOptions;
24 class BoundedEnvelope;
25 using PRCrossfadeData = std::vector< std::vector < float > >;
26 class PlayRegionEvent;
27 
28 constexpr size_t TimeQueueGrainSize = 2000;
29 
30 //! Communicate data atomically from one writer thread to one reader.
31 /*!
32  This is not a queue: it is not necessary for each write to be read.
33  Rather loss of a message is allowed:  writer may overwrite.
34  Data must be default-constructible and either copyable or movable.
35  */
36 template<typename Data>
37 class MessageBuffer {
38    struct UpdateSlot {
39       std::atomic<bool> mBusy{ false };
40       Data mData;
41    };
42    NonInterfering<UpdateSlot> mSlots[2];
43 
44    std::atomic<unsigned char> mLastWrittenSlot{ 0 };
45 
46 public:
47    void Initialize();
48 
49    //! Move data out (if available), or else copy it out
50    Data Read();
51 
52    //! Copy data in
53    void Write( const Data &data );
54    //! Move data in
55    void Write( Data &&data );
56 };
57 
58 template<typename Data>
Initialize()59 void MessageBuffer<Data>::Initialize()
60 {
61    for (auto &slot : mSlots)
62       // Lock both slots first, maybe spinning a little
63       while ( slot.mBusy.exchange( true, std::memory_order_acquire ) )
64          {}
65 
66    mSlots[0].mData = {};
67    mSlots[1].mData = {};
68    mLastWrittenSlot.store( 0, std::memory_order_relaxed );
69 
70    for (auto &slot : mSlots)
71       slot.mBusy.exchange( false, std::memory_order_release );
72 }
73 
74 template<typename Data>
Read()75 Data MessageBuffer<Data>::Read()
76 {
77    // Whichever slot was last written, prefer to read that.
78    auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
79    idx = 1 - idx;
80    bool wasBusy = false;
81    do {
82       // This loop is unlikely to execute twice, but it might because the
83       // producer thread is writing a slot.
84       idx = 1 - idx;
85       wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
86    } while ( wasBusy );
87 
88    // Copy the slot
89    auto result = std::move( mSlots[idx].mData );
90 
91    mSlots[idx].mBusy.store( false, std::memory_order_release );
92 
93    return result;
94 }
95 
96 template<typename Data>
Write(const Data & data)97 void MessageBuffer<Data>::Write( const Data &data )
98 {
99    // Whichever slot was last written, prefer to write the other.
100    auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
101    bool wasBusy = false;
102    do {
103       // This loop is unlikely to execute twice, but it might because the
104       // consumer thread is reading a slot.
105       idx = 1 - idx;
106       wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
107    } while ( wasBusy );
108 
109    mSlots[idx].mData = data;
110    mLastWrittenSlot.store( idx, std::memory_order_relaxed );
111 
112    mSlots[idx].mBusy.store( false, std::memory_order_release );
113 }
114 
115 template<typename Data>
Write(Data && data)116 void MessageBuffer<Data>::Write( Data &&data )
117 {
118    // Whichever slot was last written, prefer to write the other.
119    auto idx = mLastWrittenSlot.load( std::memory_order_relaxed );
120    bool wasBusy = false;
121    do {
122       // This loop is unlikely to execute twice, but it might because the
123       // consumer thread is reading a slot.
124       idx = 1 - idx;
125       wasBusy = mSlots[idx].mBusy.exchange( true, std::memory_order_acquire );
126    } while ( wasBusy );
127 
128    mSlots[idx].mData = std::move( data );
129    mLastWrittenSlot.store( idx, std::memory_order_relaxed );
130 
131    mSlots[idx].mBusy.store( false, std::memory_order_release );
132 }
133 
134 struct RecordingSchedule {
135    double mPreRoll{};
136    double mLatencyCorrection{}; // negative value usually
137    double mDuration{};
138    PRCrossfadeData mCrossfadeData;
139 
140    // These are initialized by the main thread, then updated
141    // only by the thread calling TrackBufferExchange:
142    double mPosition{};
143    bool mLatencyCorrected{};
144 
TotalCorrectionRecordingSchedule145    double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
146    double ToConsume() const;
147    double Consumed() const;
148    double ToDiscard() const;
149 };
150 
151 class Mixer;
152 struct PlaybackSchedule;
153 
154 //! Describes an amount of contiguous (but maybe time-warped) data to be extracted from tracks to play
155 struct PlaybackSlice {
156    const size_t frames; //!< Total number of frames to be buffered
157    const size_t toProduce; //!< Not more than `frames`; the difference will be trailing silence
158 
159    //! Constructor enforces some invariants
160    /*! @invariant `result.toProduce <= result.frames && result.frames <= available`
161     */
PlaybackSlicePlaybackSlice162    PlaybackSlice(
163       size_t available, size_t frames_, size_t toProduce_)
164       : frames{ std::min(available, frames_) }
165       , toProduce{ std::min(toProduce_, frames) }
166    {}
167 };
168 
169 //! Directs which parts of tracks to fetch for playback
170 /*!
171  A non-default policy object may be created each time playback begins, and if so it is destroyed when
172  playback stops, not reused in the next playback.
173 
174  Methods of the object are passed a PlaybackSchedule as context.
175  */
176 class PlaybackPolicy {
177 public:
178    //! @section Called by the main thread
179 
180    virtual ~PlaybackPolicy() = 0;
181 
182    //! Called before starting an audio stream
183    virtual void Initialize( PlaybackSchedule &schedule, double rate );
184 
185    //! Called after stopping of an audio stream or an unsuccessful start
186    virtual void Finalize( PlaybackSchedule &schedule );
187 
188    //! Options to use when constructing mixers for each playback track
189    virtual Mixer::WarpOptions MixerWarpOptions(PlaybackSchedule &schedule);
190 
191    //! Times are in seconds
192    struct BufferTimes {
193       double batchSize; //!< Try to put at least this much into the ring buffer in each pass
194       double latency; //!< Try not to let ring buffer contents fall below this
195       double ringBufferDelay; //!< Length of ring buffer in seconds
196    };
197    //! Provide hints for construction of playback RingBuffer objects
198    virtual BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule);
199 
200    //! @section Called by the PortAudio callback thread
201 
202    //! Whether repositioning commands are allowed during playback
203    virtual bool AllowSeek( PlaybackSchedule &schedule );
204 
205    //! Returns true if schedule.GetTrackTime() has reached the end of playback
206    virtual bool Done( PlaybackSchedule &schedule,
207       unsigned long outputFrames //!< how many playback frames were taken from RingBuffers
208    );
209 
210    //! Called when the play head needs to jump a certain distance
211    /*! @param offset signed amount requested to be added to schedule::GetTrackTime()
212       @return the new value that will be set as the schedule's track time
213     */
214    virtual double OffsetTrackTime( PlaybackSchedule &schedule, double offset );
215 
216    //! @section Called by the AudioIO::TrackBufferExchange thread
217 
218    //! How long to wait between calls to AudioIO::TrackBufferExchange
219    virtual std::chrono::milliseconds
220       SleepInterval( PlaybackSchedule &schedule );
221 
222    //! Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers
223    virtual PlaybackSlice GetPlaybackSlice( PlaybackSchedule &schedule,
224       size_t available //!< upper bound for the length of the fetch
225    );
226 
227    //! Compute a new point in a track's timeline from an old point and a real duration
228    /*!
229     Needed because playback might be at non-unit speed.
230 
231     Called one or more times between GetPlaybackSlice and RepositionPlayback,
232     until the sum of the nSamples values equals the most recent playback slice
233     (including any trailing silence).
234 
235     @return a pair, which indicates a discontinuous jump when its members are not equal, or
236        specially the end of playback when the second member is infinite
237     */
238    virtual std::pair<double, double>
239       AdvancedTrackTime( PlaybackSchedule &schedule,
240          double trackTime, size_t nSamples );
241 
242    using Mixers = std::vector<std::unique_ptr<Mixer>>;
243 
244    //! AudioIO::FillPlayBuffers calls this to update its cursors into tracks for changes of position or speed
245    /*!
246     @return if true, AudioIO::FillPlayBuffers stops producing samples even if space remains
247     */
248    virtual bool RepositionPlayback(
249       PlaybackSchedule &schedule, const Mixers &playbackMixers,
250       size_t frames, //!< how many samples were just now buffered for play
251       size_t available //!< how many more samples may be buffered
252    );
253 
254    //! @section To be removed
255 
256    virtual bool Looping( const PlaybackSchedule &schedule ) const;
257 
258 protected:
259    double mRate = 0;
260 };
261 
262 struct AUDACITY_DLL_API PlaybackSchedule {
263 
264    /// Playback starts at offset of mT0, which is measured in seconds.
265    double              mT0;
266    /// Playback ends at offset of mT1, which is measured in seconds.  Note that mT1 may be less than mT0 during scrubbing.
267    double              mT1;
268    /// Current track time position during playback, in seconds.
269    /// Initialized by the main thread but updated by worker threads during
270    /// playback or recording, and periodically reread by the main thread for
271    /// purposes such as display update.
272    std::atomic<double> mTime;
273 
274    /// Accumulated real time (not track position), starting at zero (unlike
275    /// mTime), and wrapping back to zero each time around looping play.
276    /// Thus, it is the length in real seconds between mT0 and mTime.
277    double              mWarpedTime;
278 
279    /// Real length to be played (if looping, for each pass) after warping via a
280    /// time track, computed just once when starting the stream.
281    /// Length in real seconds between mT0 and mT1.  Always positive.
282    double              mWarpedLength;
283 
284    // mWarpedTime and mWarpedLength are irrelevant when scrubbing,
285    // else they are used in updating mTime,
286    // and when not scrubbing or playing looped, mTime is also used
287    // in the test for termination of playback.
288 
289    // with ComputeWarpedLength, it is now possible the calculate the warped length with 100% accuracy
290    // (ignoring accumulated rounding errors during playback) which fixes the 'missing sound at the end' bug
291 
292    const BoundedEnvelope *mEnvelope;
293 
294    //! A circular buffer
295    /*
296     Holds track time values corresponding to every nth sample in the
297     playback buffers, for the large n == TimeQueueGrainSize.
298 
299     The "producer" is the Audio thread that fetches samples from tracks and
300     fills the playback RingBuffers.  The "consumer" is the high-latency
301     PortAudio thread that drains the RingBuffers.  The atomics in the
302     RingBuffer implement lock-free synchronization.
303 
304     This other structure relies on the RingBuffer's synchronization, and adds
305     other information to the stream of samples:  which track times they
306     correspond to.
307 
308     The consumer thread uses that information, and also makes known to the main
309     thread, what the last consumed track time is.  The main thread can use that
310     for other purposes such as refreshing the display of the play head position.
311     */
312    class TimeQueue {
313    public:
314 
315       //! @section called by main thread
316 
317       void Clear();
318       void Resize(size_t size);
319 
320       //! @section Called by the AudioIO::TrackBufferExchange thread
321 
322       //! Enqueue track time value advanced by `nSamples` according to `schedule`'s PlaybackPolicy
323       void Producer( PlaybackSchedule &schedule, size_t nSamples );
324 
325       //! Return the last time saved by Producer
326       double GetLastTime() const;
327 
328       void SetLastTime(double time);
329 
330       //! @section called by PortAudio callback thread
331 
332       //! Find the track time value `nSamples` after the last consumed sample
333       double Consumer( size_t nSamples, double rate );
334 
335       //! @section called by any thread while producer and consumer are suspended
336 
337       //! Empty the queue and reassign the last produced time
338       /*! Assumes producer and consumer are suspended */
339       void Prime( double time );
340 
341    private:
342       struct Record {
343          double timeValue;
344          // More fields to come
345       };
346       using Records = std::vector<Record>;
347       Records mData;
348       double mLastTime {};
349       struct Cursor {
350          size_t mIndex {};
351          size_t mRemainder {};
352       };
353       //! Aligned to avoid false sharing
354       NonInterfering<Cursor> mHead, mTail;
355    } mTimeQueue;
356 
357    PlaybackPolicy &GetPolicy();
358    const PlaybackPolicy &GetPolicy() const;
359 
360    void Init(
361       double t0, double t1,
362       const AudioIOStartStreamOptions &options,
363       const RecordingSchedule *pRecordingSchedule );
364 
365    /** @brief Compute signed duration (in seconds at playback) of the specified region of the track.
366     *
367     * Takes a region of the time track (specified by the unwarped time points in the project), and
368     * calculates how long it will actually take to play this region back, taking the time track's
369     * warping effects into account.
370     * @param t0 unwarped time to start calculation from
371     * @param t1 unwarped time to stop calculation at
372     * @return the warped duration in seconds, negated if `t0 > t1`
373     */
374    double ComputeWarpedLength(double t0, double t1) const;
375 
376    /** @brief Compute how much unwarped time must have elapsed if length seconds of warped time has
377     * elapsed, and add to t0
378     *
379     * @param t0 The unwarped time (seconds from project start) at which to start
380     * @param length How many seconds of real time went past; signed
381     * @return The end point (in seconds from project start) as unwarped time
382     */
383    double SolveWarpedLength(double t0, double length) const;
384 
385    /** \brief True if the end time is before the start time */
ReversedTimePlaybackSchedule386    bool ReversedTime() const
387    {
388       return mT1 < mT0;
389    }
390 
391    /** \brief Get current track time value, unadjusted
392     *
393     * Returns a time in seconds.
394     */
GetTrackTimePlaybackSchedule395    double GetTrackTime() const
396    { return mTime.load(std::memory_order_relaxed); }
397 
398    /** \brief Set current track time value, unadjusted
399     */
SetTrackTimePlaybackSchedule400    void SetTrackTime( double time )
401    { mTime.store(time, std::memory_order_relaxed); }
402 
ResetModePlaybackSchedule403    void ResetMode() {
404       mPolicyValid.store(false, std::memory_order_release);
405    }
406 
407    // Convert time between mT0 and argument to real duration, according to
408    // time track if one is given; result is always nonnegative
409    double RealDuration(double trackTime1) const;
410 
411    // Convert time between mT0 and argument to real duration, according to
412    // time track if one is given; may be negative
413    double RealDurationSigned(double trackTime1) const;
414 
415    // How much real time left?
416    double RealTimeRemaining() const;
417 
418    // Advance the real time position
419    void RealTimeAdvance( double increment );
420 
421    // Determine starting duration within the first pass -- sometimes not
422    // zero
423    void RealTimeInit( double trackTime );
424 
425    void RealTimeRestart();
426 
427 private:
428    std::unique_ptr<PlaybackPolicy> mpPlaybackPolicy;
429    std::atomic<bool> mPolicyValid{ false };
430 };
431 
432 class NewDefaultPlaybackPolicy final
433    : public PlaybackPolicy
434    , public NonInterferingBase
435    , public wxEvtHandler
436 {
437 public:
438    NewDefaultPlaybackPolicy( AudacityProject &project,
439       double trackEndTime, double loopEndTime,
440       bool loopEnabled, bool variableSpeed);
441    ~NewDefaultPlaybackPolicy() override;
442 
443    void Initialize( PlaybackSchedule &schedule, double rate ) override;
444 
445    Mixer::WarpOptions MixerWarpOptions(PlaybackSchedule &schedule) override;
446 
447    BufferTimes SuggestedBufferTimes(PlaybackSchedule &schedule) override;
448 
449    bool Done( PlaybackSchedule &schedule, unsigned long ) override;
450 
451    PlaybackSlice GetPlaybackSlice(
452       PlaybackSchedule &schedule, size_t available ) override;
453 
454    std::pair<double, double>
455       AdvancedTrackTime( PlaybackSchedule &schedule,
456          double trackTime, size_t nSamples ) override;
457 
458    bool RepositionPlayback(
459       PlaybackSchedule &schedule, const Mixers &playbackMixers,
460       size_t frames, size_t available ) override;
461 
462    bool Looping( const PlaybackSchedule & ) const override;
463 
464 private:
465    bool RevertToOldDefault( const PlaybackSchedule &schedule ) const;
466    void OnPlayRegionChange(PlayRegionEvent &evt);
467    void OnPlaySpeedChange(wxCommandEvent &evt);
468    void WriteMessage();
469    double GetPlaySpeed();
470 
471    AudacityProject &mProject;
472 
473    // The main thread writes changes in response to user events, and
474    // the audio thread later reads, and changes the playback.
475    struct SlotData {
476       double mPlaySpeed;
477       double mT0;
478       double mT1;
479       bool mLoopEnabled;
480    };
481    MessageBuffer<SlotData> mMessageChannel;
482 
483    double mLastPlaySpeed{ 1.0 };
484    const double mTrackEndTime;
485    double mLoopEndTime;
486    size_t mRemaining{ 0 };
487    bool mProgress{ true };
488    bool mLoopEnabled{ true };
489    bool mVariableSpeed{ false };
490 };
491 #endif
492