1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   WaveTrack.h
6 
7   Dominic Mazzoni
8 
9 **********************************************************************/
10 
11 #ifndef __AUDACITY_WAVETRACK__
12 #define __AUDACITY_WAVETRACK__
13 
14 #include "Track.h"
15 #include "SampleCount.h"
16 
17 #include <vector>
18 #include <functional>
19 #include <wx/longlong.h>
20 
21 #include "WaveTrackLocation.h"
22 
23 class ProgressDialog;
24 
25 class SampleBlockFactory;
26 using SampleBlockFactoryPtr = std::shared_ptr<SampleBlockFactory>;
27 
28 class SpectrogramSettings;
29 class WaveformSettings;
30 class TimeWarper;
31 
32 class Sequence;
33 class WaveClip;
34 
35 // Array of pointers that assume ownership
36 using WaveClipHolder = std::shared_ptr< WaveClip >;
37 using WaveClipHolders = std::vector < WaveClipHolder >;
38 using WaveClipConstHolders = std::vector < std::shared_ptr< const WaveClip > >;
39 
40 // Temporary arrays of mere pointers
41 using WaveClipPointers = std::vector < WaveClip* >;
42 using WaveClipConstPointers = std::vector < const WaveClip* >;
43 
44 //
45 // Tolerance for merging wave tracks (in seconds)
46 //
47 #define WAVETRACK_MERGE_POINT_TOLERANCE 0.01
48 
49 /// \brief Structure to hold region of a wavetrack and a comparison function
50 /// for sortability.
51 struct Region
52 {
RegionRegion53    Region() : start(0), end(0) {}
RegionRegion54    Region(double start_, double end_) : start(start_), end(end_) {}
55 
56    double start, end;
57 
58    //used for sorting
59    bool operator < (const Region &b) const
60    {
61       return this->start < b.start;
62    }
63 };
64 
65 using Regions = std::vector < Region >;
66 
67 class Envelope;
68 
69 class AUDACITY_DLL_API WaveTrack final : public PlayableTrack {
70 public:
71 
72    //
73    // Constructor / Destructor / Duplicator
74    //
75 
76    WaveTrack(
77       const SampleBlockFactoryPtr &pFactory, sampleFormat format, double rate);
78    WaveTrack(const WaveTrack &orig);
79 
80    // overwrite data excluding the sample sequence but including display
81    // settings
82    void Reinit(const WaveTrack &orig);
83 private:
84    void Init(const WaveTrack &orig);
85 
86    Track::Holder Clone() const override;
87 
88    friend class WaveTrackFactory;
89 
90    wxString MakeClipCopyName(const wxString& originalName) const;
91    wxString MakeNewClipName() const;
92  public:
93 
94    typedef WaveTrackLocation Location;
95    using Holder = std::shared_ptr<WaveTrack>;
96 
97    virtual ~WaveTrack();
98 
99    double GetOffset() const override;
100    void SetOffset(double o) override;
101    virtual ChannelType GetChannelIgnoringPan() const;
102    ChannelType GetChannel() const override;
103    virtual void SetPanFromChannelType() override;
104 
105    bool LinkConsistencyCheck() override;
106 
107    /** @brief Get the time at which the first clip in the track starts
108     *
109     * @return time in seconds, or zero if there are no clips in the track
110     */
111    double GetStartTime() const override;
112 
113    /** @brief Get the time at which the last clip in the track ends, plus
114     * recorded stuff
115     *
116     * @return time in seconds, or zero if there are no clips in the track.
117     */
118    double GetEndTime() const override;
119 
120    //
121    // Identifying the type of track
122    //
123 
124    //
125    // WaveTrack parameters
126    //
127 
128    double GetRate() const;
129    void SetRate(double newRate);
130 
131    // Multiplicative factor.  Only converted to dB for display.
132    float GetGain() const;
133    void SetGain(float newGain);
134 
135    // -1.0 (left) -> 1.0 (right)
136    float GetPan() const;
137    void SetPan(float newPan) override;
138 
139    // Takes gain and pan into account
140    float GetChannelGain(int channel) const;
141 
142    // Old gain is used in playback in linearly interpolating
143    // the gain.
144    float GetOldChannelGain(int channel) const;
145    void SetOldChannelGain(int channel, float gain);
146 
GetWaveColorIndex()147    int GetWaveColorIndex() const { return mWaveColorIndex; };
148    void SetWaveColorIndex(int colorIndex);
149 
150    sampleCount GetPlaySamplesCount() const;
151    sampleCount GetSequenceSamplesCount() const;
152 
GetSampleFormat()153    sampleFormat GetSampleFormat() const { return mFormat; }
154    void ConvertToSampleFormat(sampleFormat format,
155       const std::function<void(size_t)> & progressReport = {});
156 
157    const SpectrogramSettings &GetSpectrogramSettings() const;
158    SpectrogramSettings &GetSpectrogramSettings();
159    SpectrogramSettings &GetIndependentSpectrogramSettings();
160    void SetSpectrogramSettings(std::unique_ptr<SpectrogramSettings> &&pSettings);
161 
162    const WaveformSettings &GetWaveformSettings() const;
163    WaveformSettings &GetWaveformSettings();
164    void SetWaveformSettings(std::unique_ptr<WaveformSettings> &&pSettings);
165    void UseSpectralPrefs( bool bUse=true );
166    //
167    // High-level editing
168    //
169 
170    Track::Holder Cut(double t0, double t1) override;
171 
172    // Make another track copying format, rate, color, etc. but containing no
173    // clips
174    // It is important to pass the correct factory (that for the project
175    // which will own the copy) in the unusual case that a track is copied from
176    // another project or the clipboard.  For copies within one project, the
177    // default will do.
178    Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory = {} ) const;
179 
180    // If forClipboard is true,
181    // and there is no clip at the end time of the selection, then the result
182    // will contain a "placeholder" clip whose only purpose is to make
183    // GetEndTime() correct.  This clip is not re-copied when pasting.
184    Track::Holder Copy(double t0, double t1, bool forClipboard = true) const override;
185    Track::Holder CopyNonconst(double t0, double t1) /* not override */;
186 
187    void Clear(double t0, double t1) override;
188    void Paste(double t0, const Track *src) override;
189    // May assume precondition: t0 <= t1
190    void ClearAndPaste(double t0, double t1,
191                               const Track *src,
192                               bool preserve = true,
193                               bool merge = true,
194                               const TimeWarper *effectWarper = NULL) /* not override */;
195 
196    void Silence(double t0, double t1) override;
197    void InsertSilence(double t, double len) override;
198 
199    void SplitAt(double t) /* not override */;
200    void Split(double t0, double t1) /* not override */;
201    // Track::Holder CutAndAddCutLine(double t0, double t1) /* not override */;
202    // May assume precondition: t0 <= t1
203    void ClearAndAddCutLine(double t0, double t1) /* not override */;
204 
205    Track::Holder SplitCut(double t0, double t1) /* not override */;
206    // May assume precondition: t0 <= t1
207    void SplitDelete(double t0, double t1) /* not override */;
208    void Join(double t0, double t1) /* not override */;
209    // May assume precondition: t0 <= t1
210    void Disjoin(double t0, double t1) /* not override */;
211 
212    // May assume precondition: t0 <= t1
213    void Trim(double t0, double t1) /* not override */;
214 
215    // May assume precondition: t0 <= t1
216    void HandleClear(double t0, double t1, bool addCutLines, bool split);
217 
218    void SyncLockAdjust(double oldT1, double newT1) override;
219 
220    /** @brief Returns true if there are no WaveClips in the specified region
221     *
222     * @return true if no clips in the track overlap the specified time range,
223     * false otherwise.
224     */
225    bool IsEmpty(double t0, double t1) const;
226 
227    /** @brief Append the sample data to the WaveTrack. You must call Flush()
228     * after the last Append.
229     *
230     * If there is an existing WaveClip in the WaveTrack then the data is
231     * appended to that clip. If there are no WaveClips in the track, then a NEW
232     * one is created.
233     *
234     * @return true if at least one complete block was created
235     */
236    bool Append(constSamplePtr buffer, sampleFormat format,
237                size_t len, unsigned int stride=1);
238    /// Flush must be called after last Append
239    void Flush();
240 
241    ///Invalidates all clips' wavecaches.  Careful, This may not be threadsafe.
242    void ClearWaveCaches();
243 
244    ///
245    /// MM: Now that each wave track can contain multiple clips, we don't
246    /// have a continuous space of samples anymore, but we simulate it,
247    /// because there are a lot of places (e.g. effects) using this interface.
248    /// This interface makes much sense for modifying samples, but note that
249    /// it is not time-accurate, because the "offset" is a double value and
250    /// therefore can lie inbetween samples. But as long as you use the
251    /// same value for "start" in both calls to "Set" and "Get" it is
252    /// guaranteed that the same samples are affected.
253    ///
254 
255    //! Retrieve samples from a track in floating-point format, regardless of the storage format
256    /*!
257     @param buffer receives the samples
258     @param start starting sample, relative to absolute time zero (not to the track's offset value)
259     @param len how many samples to get.  buffer is assumed sufficiently large
260     @param fill how to assign values for sample positions between clips
261     @param mayThrow if false, fill buffer with zeros when there is failure to retrieve samples; else throw
262     @param[out] pNumWithinClips Report how many samples were copied from within clips, rather
263        than filled according to fillFormat; but these were not necessarily one contiguous range.
264     */
265    bool GetFloats(float *buffer, sampleCount start, size_t len,
266       fillFormat fill = fillZero, bool mayThrow = true,
267       sampleCount * pNumWithinClips = nullptr) const
268    {
269       //! Cast the pointer to pass it to Get() which handles multiple destination formats
270       return Get(reinterpret_cast<samplePtr>(buffer),
271          floatSample, start, len, fill, mayThrow, pNumWithinClips);
272    }
273 
274    //! Retrieve samples from a track in a specified format
275    /*!
276     @copydetails WaveTrack::GetFloats()
277     @param format sample format of the destination buffer
278     */
279    bool Get(samplePtr buffer, sampleFormat format,
280       sampleCount start, size_t len,
281       fillFormat fill = fillZero,
282       bool mayThrow = true,
283       // Report how many samples were copied from within clips, rather than
284       // filled according to fillFormat; but these were not necessarily one
285       // contiguous range.
286       sampleCount * pNumWithinClips = nullptr) const;
287 
288    void Set(constSamplePtr buffer, sampleFormat format,
289                    sampleCount start, size_t len);
290 
291    // Fetch envelope values corresponding to uniformly separated sample times
292    // starting at the given time.
293    void GetEnvelopeValues(double *buffer, size_t bufferLen,
294                          double t0) const;
295 
296    // May assume precondition: t0 <= t1
297    std::pair<float, float> GetMinMax(
298       double t0, double t1, bool mayThrow = true) const;
299    // May assume precondition: t0 <= t1
300    float GetRMS(double t0, double t1, bool mayThrow = true) const;
301 
302    //
303    // MM: We now have more than one sequence and envelope per track, so
304    // instead of GetSequence() and GetEnvelope() we have the following
305    // function which give the sequence and envelope which contains the given
306    // time.
307    //
308    Sequence* GetSequenceAtTime(double time);
309    Envelope* GetEnvelopeAtTime(double time);
310 
311    WaveClip* GetClipAtSample(sampleCount sample);
312    WaveClip* GetClipAtTime(double time);
313 
314    //
315    // Getting information about the track's internal block sizes
316    // and alignment for efficiency
317    //
318 
319    // This returns a possibly large or negative value
320    sampleCount GetBlockStart(sampleCount t) const;
321 
322    // These return a nonnegative number of samples meant to size a memory buffer
323    size_t GetBestBlockSize(sampleCount t) const;
324    size_t GetMaxBlockSize() const;
325    size_t GetIdealBlockSize();
326 
327    //
328    // XMLTagHandler callback methods for loading and saving
329    //
330 
331    bool HandleXMLTag(const std::string_view& tag, const AttributesList& attrs) override;
332    void HandleXMLEndTag(const std::string_view& tag) override;
333    XMLTagHandler *HandleXMLChild(const std::string_view& tag) override;
334    void WriteXML(XMLWriter &xmlFile) const override;
335 
336    // Returns true if an error occurred while reading from XML
337    bool GetErrorOpening() override;
338 
339    //
340    // Lock and unlock the track: you must lock the track before
341    // doing a copy and paste between projects.
342    //
343 
344    bool CloseLock(); //should be called when the project closes.
345    // not balanced by unlocking calls.
346 
347    /** @brief Convert correctly between an (absolute) time in seconds and a number of samples.
348     *
349     * This method will not give the correct results if used on a relative time (difference of two
350     * times). Each absolute time must be converted and the numbers of samples differenced:
351     *    sampleCount start = track->TimeToLongSamples(t0);
352     *    sampleCount end = track->TimeToLongSamples(t1);
353     *    sampleCount len = (sampleCount)(end - start);
354     * NOT the likes of:
355     *    sampleCount len = track->TimeToLongSamples(t1 - t0);
356     * See also WaveTrack::TimeToLongSamples().
357     * @param t0 The time (floating point seconds) to convert
358     * @return The number of samples from the start of the track which lie before the given time.
359     */
360    sampleCount TimeToLongSamples(double t0) const;
361    /** @brief Convert correctly between a number of samples and an (absolute) time in seconds.
362     *
363     * @param pos The time number of samples from the start of the track to convert.
364     * @return The time in seconds.
365     */
366    double LongSamplesToTime(sampleCount pos) const;
367 
368    // Get access to the (visible) clips in the tracks, in unspecified order
369    // (not necessarily sequenced in time).
GetClips()370    WaveClipHolders &GetClips() { return mClips; }
GetClips()371    const WaveClipConstHolders &GetClips() const
372       { return reinterpret_cast< const WaveClipConstHolders& >( mClips ); }
373 
374    // Get mutative access to all clips (in some unspecified sequence),
375    // including those hidden in cutlines.
376    class AllClipsIterator
377       : public ValueIterator< WaveClip * >
378    {
379    public:
380       // Constructs an "end" iterator
AllClipsIterator()381       AllClipsIterator () {}
382 
383       // Construct a "begin" iterator
AllClipsIterator(WaveTrack & track)384       explicit AllClipsIterator( WaveTrack &track )
385       {
386          push( track.mClips );
387       }
388 
389       WaveClip *operator * () const
390       {
391          if (mStack.empty())
392             return nullptr;
393          else
394             return mStack.back().first->get();
395       }
396 
397       AllClipsIterator &operator ++ ();
398 
399       // Define == well enough to serve for loop termination test
400       friend bool operator == (
401          const AllClipsIterator &a, const AllClipsIterator &b)
402       { return a.mStack.empty() == b.mStack.empty(); }
403 
404       friend bool operator != (
405          const AllClipsIterator &a, const AllClipsIterator &b)
406       { return !( a == b ); }
407 
408    private:
409 
410       void push( WaveClipHolders &clips );
411 
412       using Iterator = WaveClipHolders::iterator;
413       using Pair = std::pair< Iterator, Iterator >;
414       using Stack = std::vector< Pair >;
415 
416       Stack mStack;
417    };
418 
419    // Get const access to all clips (in some unspecified sequence),
420    // including those hidden in cutlines.
421    class AllClipsConstIterator
422       : public ValueIterator< const WaveClip * >
423    {
424    public:
425       // Constructs an "end" iterator
AllClipsConstIterator()426       AllClipsConstIterator () {}
427 
428       // Construct a "begin" iterator
AllClipsConstIterator(const WaveTrack & track)429       explicit AllClipsConstIterator( const WaveTrack &track )
430          : mIter{ const_cast< WaveTrack& >( track ) }
431       {}
432 
433       const WaveClip *operator * () const
434       { return *mIter; }
435 
436       AllClipsConstIterator &operator ++ ()
437       { ++mIter; return *this; }
438 
439       // Define == well enough to serve for loop termination test
440       friend bool operator == (
441          const AllClipsConstIterator &a, const AllClipsConstIterator &b)
442       { return a.mIter == b.mIter; }
443 
444       friend bool operator != (
445          const AllClipsConstIterator &a, const AllClipsConstIterator &b)
446       { return !( a == b ); }
447 
448    private:
449       AllClipsIterator mIter;
450    };
451 
GetAllClips()452    IteratorRange< AllClipsIterator > GetAllClips()
453    {
454       return { AllClipsIterator{ *this }, AllClipsIterator{ } };
455    }
456 
GetAllClips()457    IteratorRange< AllClipsConstIterator > GetAllClips() const
458    {
459       return { AllClipsConstIterator{ *this }, AllClipsConstIterator{ } };
460    }
461 
462    // Create NEW clip and add it to this track. Returns a pointer
463    // to the newly created clip. Optionally initial offset and
464    // clip name may be provided
465    WaveClip* CreateClip(double offset = .0, const wxString& name = wxEmptyString);
466 
467    /** @brief Get access to the most recently added clip, or create a clip,
468    *  if there is not already one.  THIS IS NOT NECESSARILY RIGHTMOST.
469    *
470    *  @return a pointer to the most recently added WaveClip
471    */
472    WaveClip* NewestOrNewClip();
473 
474    /** @brief Get access to the last (rightmost) clip, or create a clip,
475    *  if there is not already one.
476    *
477    *  @return a pointer to a WaveClip at the end of the track
478    */
479    WaveClip* RightmostOrNewClip();
480 
481    // Get the linear index of a given clip (-1 if the clip is not found)
482    int GetClipIndex(const WaveClip* clip) const;
483 
484    // Get the nth clip in this WaveTrack (will return NULL if not found).
485    // Use this only in special cases (like getting the linked clip), because
486    // it is much slower than GetClipIterator().
487    WaveClip *GetClipByIndex(int index);
488    const WaveClip* GetClipByIndex(int index) const;
489 
490    // Get number of clips in this WaveTrack
491    int GetNumClips() const;
492 
493    // Add all wave clips to the given array 'clips' and sort the array by
494    // clip start time. The array is emptied prior to adding the clips.
495    WaveClipPointers SortedClipArray();
496    WaveClipConstPointers SortedClipArray() const;
497 
498    //! Decide whether the clips could be offset (and inserted) together without overlapping other clips
499    /*!
500    @return true if possible to offset by `(allowedAmount ? *allowedAmount : amount)`
501     */
502    bool CanOffsetClips(
503       const std::vector<WaveClip*> &clips, //!< not necessarily in this track
504       double amount, //!< signed
505       double *allowedAmount = nullptr /*!<
506          [out] if null, test exact amount only; else, largest (in magnitude) possible offset with same sign */
507    );
508 
509    // Before moving a clip into a track (or inserting a clip), use this
510    // function to see if the times are valid (i.e. don't overlap with
511    // existing clips).
512    bool CanInsertClip(WaveClip* clip, double &slideBy, double &tolerance) const;
513 
514    // Remove the clip from the track and return a SMART pointer to it.
515    // You assume responsibility for its memory!
516    std::shared_ptr<WaveClip> RemoveAndReturnClip(WaveClip* clip);
517 
518    //! Append a clip to the track; which must have the same block factory as this track; return success
519    bool AddClip(const std::shared_ptr<WaveClip> &clip);
520 
521    // Merge two clips, that is append data from clip2 to clip1,
522    // then remove clip2 from track.
523    // clipidx1 and clipidx2 are indices into the clip list.
524    void MergeClips(int clipidx1, int clipidx2);
525 
526    // Cache special locations (e.g. cut lines) for later speedy access
527    void UpdateLocationsCache() const;
528 
529    // Get cached locations
GetCachedLocations()530    const std::vector<Location> &GetCachedLocations() const { return mDisplayLocationsCache; }
531 
532    // Expand cut line (that is, re-insert audio, then DELETE audio saved in cut line)
533    void ExpandCutLine(double cutLinePosition, double* cutlineStart = NULL, double* cutlineEnd = NULL);
534 
535    // Remove cut line, without expanding the audio in it
536    bool RemoveCutLine(double cutLinePosition);
537 
538    // This track has been merged into a stereo track.  Copy shared parameters
539    // from the NEW partner.
540    void Merge(const Track &orig) override;
541 
542    // Resample track (i.e. all clips in the track)
543    void Resample(int rate, ProgressDialog *progress = NULL);
544 
GetLastScaleType()545    int GetLastScaleType() const { return mLastScaleType; }
546    void SetLastScaleType() const;
547 
GetLastdBRange()548    int GetLastdBRange() const { return mLastdBRange; }
549    void SetLastdBRange() const;
550 
551    void GetDisplayBounds(float *min, float *max) const;
552    void SetDisplayBounds(float min, float max) const;
553    void GetSpectrumBounds(float *min, float *max) const;
554    void SetSpectrumBounds(float min, float max) const;
555 
556    // For display purposes, calculate the y coordinate where the midline of
557    // the wave should be drawn, if display minimum and maximum map to the
558    // bottom and top.  Maybe that is out of bounds.
559    int ZeroLevelYCoordinate(wxRect rect) const;
560 
561    class IntervalData final : public Track::IntervalData {
562    public:
IntervalData(const std::shared_ptr<WaveClip> & pClip)563       explicit IntervalData( const std::shared_ptr<WaveClip> &pClip )
564       : pClip{ pClip }
565       {}
GetClip()566       std::shared_ptr<const WaveClip> GetClip() const { return pClip; }
GetClip()567       std::shared_ptr<WaveClip> &GetClip() { return pClip; }
568    private:
569       std::shared_ptr<WaveClip> pClip;
570    };
571 
572    Track::Holder PasteInto( AudacityProject & ) const override;
573 
574    ConstIntervals GetIntervals() const override;
575    Intervals GetIntervals() override;
576 
577    //! Returns nullptr if clip with such name was not found
578    const WaveClip* FindClipByName(const wxString& name) const;
579  protected:
580    //
581    // Protected variables
582    //
583 
584    WaveClipHolders mClips;
585 
586    sampleFormat  mFormat;
587    int           mRate;
588    float         mGain;
589    float         mPan;
590    int           mWaveColorIndex;
591    float         mOldGain[2];
592 
593 
594    //
595    // Data that should be part of GUIWaveTrack
596    // and will be taken out of the WaveTrack class:
597    //
598    mutable float         mDisplayMin;
599    mutable float         mDisplayMax;
600    mutable float         mSpectrumMin;
601    mutable float         mSpectrumMax;
602 
603    mutable int   mLastScaleType; // last scale type choice
604    mutable int           mLastdBRange;
605    mutable std::vector <Location> mDisplayLocationsCache;
606 
607    //
608    // Protected methods
609    //
610 
611 private:
612 
613    void PasteWaveTrack(double t0, const WaveTrack* other);
614 
GetKind()615    TrackKind GetKind() const override { return TrackKind::Wave; }
616 
617    //
618    // Private variables
619    //
620 
621    SampleBlockFactoryPtr mpFactory;
622 
623    wxCriticalSection mFlushCriticalSection;
624    wxCriticalSection mAppendCriticalSection;
625    double mLegacyProjectFileOffset;
626 
627    std::unique_ptr<SpectrogramSettings> mpSpectrumSettings;
628    std::unique_ptr<WaveformSettings> mpWaveformSettings;
629 };
630 
631 //! A short-lived object, during whose lifetime, the contents of the WaveTrack are assumed not to change.
632 /*! It can replace repeated calls to WaveTrack::Get() (each of which opens and closes at least one block).
633  */
634 class AUDACITY_DLL_API WaveTrackCache {
635 public:
WaveTrackCache()636    WaveTrackCache()
637       : mBufferSize(0)
638       , mOverlapBuffer()
639       , mNValidBuffers(0)
640    {
641    }
642 
WaveTrackCache(const std::shared_ptr<const WaveTrack> & pTrack)643    explicit WaveTrackCache(const std::shared_ptr<const WaveTrack> &pTrack)
644       : mBufferSize(0)
645       , mOverlapBuffer()
646       , mNValidBuffers(0)
647    {
648       SetTrack(pTrack);
649    }
650    ~WaveTrackCache();
651 
GetTrack()652    const std::shared_ptr<const WaveTrack>& GetTrack() const { return mPTrack; }
653    void SetTrack(const std::shared_ptr<const WaveTrack> &pTrack);
654 
655    //! Retrieve samples as floats from the track or from the memory cache
656    /*! Uses fillZero always
657     @return null on failure; this object owns the memory; may be invalidated if GetFloats() is called again
658    */
659    const float *GetFloats(sampleCount start, size_t len, bool mayThrow);
660 
661 private:
662    void Free();
663 
664    struct Buffer {
665       Floats data;
666       sampleCount start;
667       sampleCount len;
668 
BufferBuffer669       Buffer() : start(0), len(0) {}
FreeBuffer670       void Free() { data.reset(); start = 0; len = 0; }
endBuffer671       sampleCount end() const { return start + len; }
672 
swapBuffer673       void swap ( Buffer &other )
674       {
675          data .swap ( other.data );
676          std::swap( start, other.start );
677          std::swap( len, other.len );
678       }
679    };
680 
681    std::shared_ptr<const WaveTrack> mPTrack;
682    size_t mBufferSize;
683    Buffer mBuffers[2];
684    GrowableSampleBuffer mOverlapBuffer;
685    int mNValidBuffers;
686 };
687 
688 #include <unordered_set>
689 class SampleBlock;
690 using SampleBlockID = long long;
691 using SampleBlockIDSet = std::unordered_set<SampleBlockID>;
692 class TrackList;
693 using BlockVisitor = std::function< void(SampleBlock&) >;
694 using BlockInspector = std::function< void(const SampleBlock&) >;
695 
696 // Function to visit all sample blocks from a list of tracks.
697 // If a set is supplied, then only visit once each unique block ID not already
698 // in that set, and accumulate those into the set as a side-effect.
699 // The visitor function may be null.
700 void VisitBlocks(TrackList &tracks, BlockVisitor visitor,
701    SampleBlockIDSet *pIDs = nullptr);
702 
703 // Non-mutating version of the above
704 void InspectBlocks(const TrackList &tracks, BlockInspector inspector,
705    SampleBlockIDSet *pIDs = nullptr);
706 
707 class ProjectRate;
708 
709 class AUDACITY_DLL_API WaveTrackFactory final
710    : public ClientData::Base
711 {
712  public:
713    static WaveTrackFactory &Get( AudacityProject &project );
714    static const WaveTrackFactory &Get( const AudacityProject &project );
715    static WaveTrackFactory &Reset( AudacityProject &project );
716    static void Destroy( AudacityProject &project );
717 
WaveTrackFactory(const ProjectRate & rate,const SampleBlockFactoryPtr & pFactory)718    WaveTrackFactory(
719       const ProjectRate& rate,
720       const SampleBlockFactoryPtr &pFactory)
721        : mRate{ rate }
722        , mpFactory(pFactory)
723    {
724    }
725    WaveTrackFactory( const WaveTrackFactory & ) PROHIBITED;
726    WaveTrackFactory &operator=( const WaveTrackFactory & ) PROHIBITED;
727 
GetSampleBlockFactory()728    const SampleBlockFactoryPtr &GetSampleBlockFactory() const
729    { return mpFactory; }
730 
731  private:
732    const ProjectRate &mRate;
733    SampleBlockFactoryPtr mpFactory;
734  public:
735    std::shared_ptr<WaveTrack> DuplicateWaveTrack(const WaveTrack &orig);
736    std::shared_ptr<WaveTrack> NewWaveTrack(
737       sampleFormat format = (sampleFormat)0,
738       double rate = 0);
739 };
740 
741 #endif // __AUDACITY_WAVETRACK__
742