1 #pragma once
2 
3 #include "audio/streaminfo.h"
4 #include "engine/engine.h"
5 #include "sources/urlresource.h"
6 #include "util/indexrange.h"
7 #include "util/memory.h"
8 #include "util/samplebuffer.h"
9 
10 namespace mixxx {
11 
12 class SampleFrames {
13   public:
14     SampleFrames() = default;
SampleFrames(IndexRange frameIndexRange)15     explicit SampleFrames(
16             IndexRange frameIndexRange)
17             : m_frameIndexRange(frameIndexRange) {
18     }
19     /*non-virtual*/ ~SampleFrames() = default;
20 
frameIndexRange()21     IndexRange frameIndexRange() const {
22         return m_frameIndexRange;
23     }
24 
frameLength()25     SINT frameLength() const {
26         return m_frameIndexRange.length();
27     }
28 
29   private:
30     IndexRange m_frameIndexRange;
31 };
32 
33 // Associates a range of frame indices with the corresponding
34 // readable sample data as a slice in some sample buffer. The
35 // memory is owned by the external sample buffer that must not
36 // be modified or destroyed while reading sample data!
37 class ReadableSampleFrames final : public SampleFrames {
38   public:
39     ReadableSampleFrames() = default;
40     explicit ReadableSampleFrames(
41             IndexRange frameIndexRange,
42             SampleBuffer::ReadableSlice readableSlice = SampleBuffer::ReadableSlice())
SampleFrames(frameIndexRange)43             : SampleFrames(frameIndexRange),
44               m_readableSlice(readableSlice) {
45     }
46     /*non-virtual*/ ~ReadableSampleFrames() = default;
47 
48     // The readable slice should cover the whole range of
49     // frame indices and starts with the first frame. An
50     // empty slice indicates that no sample data is available
51     // for reading.
readableSlice()52     SampleBuffer::ReadableSlice readableSlice() const {
53         return m_readableSlice;
54     }
55 
56     SINT readableLength(SINT offset = 0) const {
57         return m_readableSlice.length(offset);
58     }
59 
60     const CSAMPLE* readableData(SINT offset = 0) const {
61         return m_readableSlice.data(offset);
62     }
63 
64   private:
65     SampleBuffer::ReadableSlice m_readableSlice;
66 };
67 
68 // Associates a range of frame indices with the corresponding
69 // writable sample data as a slice in some sample buffer. The
70 // memory is owned by the external sample buffer that must not
71 // be modified or destroyed while writing sample data!
72 class WritableSampleFrames final : public SampleFrames {
73   public:
74     WritableSampleFrames() = default;
75     explicit WritableSampleFrames(
76             IndexRange frameIndexRange,
77             SampleBuffer::WritableSlice writableSlice = SampleBuffer::WritableSlice())
SampleFrames(frameIndexRange)78             : SampleFrames(frameIndexRange),
79               m_writableSlice(writableSlice) {
80     }
81     /*non-virtual*/ ~WritableSampleFrames() = default;
82 
83     // The writable slice should cover the whole range of
84     // frame indices and starts with the first frame. An
85     // empty slice indicates that no sample data must
86     // be written.
writableSlice()87     SampleBuffer::WritableSlice writableSlice() const {
88         return m_writableSlice;
89     }
90 
91     SINT writableLength(SINT offset = 0) const {
92         return m_writableSlice.length(offset);
93     }
94 
95     CSAMPLE* writableData(SINT offset = 0) const {
96         return m_writableSlice.data(offset);
97     }
98 
99   private:
100     SampleBuffer::WritableSlice m_writableSlice;
101 };
102 
103 class IAudioSourceReader {
104   public:
105     virtual ~IAudioSourceReader() = default;
106 
107   protected:
108     // Reads as much of the the requested sample frames and writes
109     // them into the provided buffer. The capacity of the buffer
110     // and the requested range have already been checked and
111     // adjusted (= clamped) before if necessary.
112     //
113     // Returns the number of and decoded sample frames in a readable
114     // buffer. The returned buffer is just a view/slice of the provided
115     // writable buffer if the result is not empty. If the result is
116     // empty the internal memory pointer of the returned buffer might
117     // be null.
118     virtual ReadableSampleFrames readSampleFramesClamped(
119             const WritableSampleFrames& sampleFrames) = 0;
120 
121     // The following function is required for accessing the protected
122     // read function from siblings implementing this interface, e.g.
123     // for proxies and adapters.
readSampleFramesClampedOn(IAudioSourceReader & that,const WritableSampleFrames & sampleFrames)124     static ReadableSampleFrames readSampleFramesClampedOn(
125             IAudioSourceReader& that,
126             const WritableSampleFrames& sampleFrames) {
127         return that.readSampleFramesClamped(sampleFrames);
128     }
129 };
130 
131 // Common base class for audio sources.
132 //
133 // Both the number of channels and the sample rate must
134 // be constant and are not allowed to change over time.
135 //
136 // Samples in a sample buffer are stored as consecutive frames,
137 // i.e. the samples of the channels are interleaved.
138 //
139 // Audio sources are implicitly opened upon creation and
140 // closed upon destruction.
141 class AudioSource : public UrlResource, public virtual /*implements*/ IAudioSourceReader {
142   public:
143     ~AudioSource() override = default;
144 
145     /// Defines how thoroughly the stream properties should be verified
146     /// when opening an audio stream.
147     enum class OpenMode {
148         /// In Strict mode the opening operation should be aborted
149         /// as soon as any inconsistencies of the stream properties
150         /// are detected when setting up the decoding.
151         Strict,
152 
153         /// Opening in Permissive mode is used only after opening
154         /// in Strict mode has been aborted by all available
155         /// SoundSource implementations.
156         ///
157         /// Example: Assume safe default values if mandatory stream
158         /// properties could not be determined when setting up the
159         /// decoding.
160         Permissive,
161     };
162 
163     /// Result of opening an audio stream.
164     enum class OpenResult {
165         /// The file has been opened successfully and should be readable.
166         Succeeded,
167 
168         /// If a SoundSource is not able to open a file because of
169         /// internal errors of if the format of the content is not
170         /// supported it should return Aborted.
171         ///
172         /// Returing this error result gives other decoders with a
173         /// lower priority the chance to open the same file.
174         /// Example: A SoundSourceProvider has been registered for
175         /// files with a certain extension, but the corresponding
176         /// SoundSource does only support a subset of all possible
177         /// data formats that might be stored in files with this
178         /// extension.
179 
180         Aborted,
181         /// If a SoundSource return Failed while opening a file the
182         /// file itself is supposed to be corrupt.
183         ///
184         /// If this happens during OpenMode::Strict then other decoders
185         /// with a lower priority are still given a chance to try
186         /// opening the file. In OpenMode::Permissive the first
187         /// SoundSource that returns Failed will declare this file
188         /// corrupt.
189         Failed,
190     };
191 
192     // Parameters for opening audio sources
193     class OpenParams {
194       public:
195         OpenParams() = default;
OpenParams(audio::ChannelCount channelCount,audio::SampleRate sampleRate)196         OpenParams(
197                 audio::ChannelCount channelCount,
198                 audio::SampleRate sampleRate)
199                 : m_signalInfo(
200                           channelCount,
201                           sampleRate) {
202         }
203 
getSignalInfo()204         const audio::SignalInfo& getSignalInfo() const {
205             return m_signalInfo;
206         }
207 
setChannelCount(audio::ChannelCount channelCount)208         void setChannelCount(
209                 audio::ChannelCount channelCount) {
210             m_signalInfo.setChannelCount(channelCount);
211         }
212 
setSampleRate(audio::SampleRate sampleRate)213         void setSampleRate(
214                 audio::SampleRate sampleRate) {
215             m_signalInfo.setSampleRate(sampleRate);
216         }
217 
218       private:
219         audio::SignalInfo m_signalInfo;
220     };
221 
222     // Opens the AudioSource for reading audio data.
223     //
224     // Since reopening is not supported close() will be called
225     // implicitly before the AudioSource is actually opened.
226     //
227     // Optionally the caller may provide the desired properties of
228     // the decoded audio signal. Some decoders are able to reduce
229     // the number of channels or do resampling efficiently on the
230     // fly while decoding the input data.
231     OpenResult open(
232             OpenMode mode,
233             const OpenParams& params = OpenParams());
234 
235     // Closes the AudioSource and frees all resources.
236     //
237     // Might be called even if the AudioSource has never been
238     // opened, has already been closed, or if opening has failed.
239     virtual void close() = 0;
240 
getSignalInfo()241     const audio::SignalInfo& getSignalInfo() const {
242         return m_signalInfo;
243     }
244 
getBitrate()245     const audio::Bitrate getBitrate() const {
246         return m_bitrate;
247     }
248 
getStreamInfo()249     audio::StreamInfo getStreamInfo() const {
250         return audio::StreamInfo(
251                 getSignalInfo(),
252                 getBitrate(),
253                 Duration::fromSeconds(getDuration()));
254     }
255 
256     // The total length of audio data is bounded and measured in frames.
frameIndexRange()257     IndexRange frameIndexRange() const {
258         return m_frameIndexRange;
259     }
260 
261     // The total length of audio data.
frameLength()262     SINT frameLength() const {
263         return m_frameIndexRange.length();
264     }
265 
266     // The index of the first frame.
frameIndexMin()267     SINT frameIndexMin() const {
268         DEBUG_ASSERT(m_frameIndexRange.start() <= m_frameIndexRange.end());
269         return m_frameIndexRange.start();
270     }
271 
272     // The index after the last frame.
frameIndexMax()273     SINT frameIndexMax() const {
274         DEBUG_ASSERT(m_frameIndexRange.start() <= m_frameIndexRange.end());
275         return m_frameIndexRange.end();
276     }
277 
278     // The sample frame index is valid within the range
279     // [frameIndexMin(), frameIndexMax()]
280     // including the upper bound of the range!
isValidFrameIndex(SINT frameIndex)281     bool isValidFrameIndex(SINT frameIndex) const {
282         return m_frameIndexRange.clampIndex(frameIndex) == frameIndex;
283     }
284 
285     // The actual duration in seconds.
286     // Well defined only for valid files!
hasDuration()287     inline bool hasDuration() const {
288         return getSignalInfo().getSampleRate().isValid();
289     }
getDuration()290     inline double getDuration() const {
291         DEBUG_ASSERT(hasDuration()); // prevents division by zero
292         return getSignalInfo().frames2secs(frameLength());
293     }
294 
295     /// Verifies various properties to ensure that the audio data is
296     /// actually readable. Warning messages are logged for properties
297     /// with invalid values for diagnostic purposes. Also performs a
298     /// basic read test.
299     bool verifyReadable();
300 
301     ReadableSampleFrames readSampleFrames(
302             const WritableSampleFrames& sampleFrames);
303 
304   protected:
305     explicit AudioSource(const QUrl& url);
306 
307     bool initChannelCountOnce(audio::ChannelCount channelCount);
initChannelCountOnce(int channelCount)308     bool initChannelCountOnce(int channelCount) {
309         return initChannelCountOnce(audio::ChannelCount(channelCount));
310     }
311 
312     bool initSampleRateOnce(audio::SampleRate sampleRate);
initSampleRateOnce(SINT sampleRate)313     bool initSampleRateOnce(SINT sampleRate) {
314         return initSampleRateOnce(audio::SampleRate(sampleRate));
315     }
316 
317     bool initBitrateOnce(audio::Bitrate bitrate);
initBitrateOnce(SINT bitrate)318     bool initBitrateOnce(SINT bitrate) {
319         return initBitrateOnce(audio::Bitrate(bitrate));
320     }
321 
322     bool initFrameIndexRangeOnce(
323             IndexRange frameIndexRange);
324     // The frame index range needs to be adjusted while
325     // reading. This virtual function is an ugly hack!!!
326     // It needs to be overridden in derived proxy classes
327     // that wrap a pointer to the actual audio source and
328     // delegate to that.
329     virtual void adjustFrameIndexRange(
330             IndexRange frameIndexRange);
adjustFrameIndexRangeOn(AudioSource & that,IndexRange frameIndexRange)331     static void adjustFrameIndexRangeOn(
332             AudioSource& that,
333             IndexRange frameIndexRange) {
334         that.adjustFrameIndexRange(frameIndexRange);
335     }
336 
337     // Tries to open the AudioSource for reading audio data according
338     // to the "Template Method" design pattern.
339     //
340     // The invocation of tryOpen() is enclosed in invocations of close():
341     //   - Before: Always
342     //   - After: Upon failure
343     // If tryOpen() throws an exception or returns a result other than
344     // OpenResult::Succeeded an invocation of close() will follow.
345     // Implementations do not need to free internal resources twice in
346     // both tryOpen() upon failure and close(). All internal resources
347     // should be freed in close() instead.
348     //
349     // Exceptions should be handled internally by implementations to
350     // avoid warning messages about unexpected or unknown exceptions.
351     virtual OpenResult tryOpen(
352             OpenMode mode,
353             const OpenParams& params) = 0;
354 
tryOpenOn(AudioSource & that,OpenMode mode,const OpenParams & params)355     static OpenResult tryOpenOn(
356             AudioSource& that,
357             OpenMode mode,
358             const OpenParams& params) {
359         return that.open(mode, params);
360     }
361 
362   private:
363     AudioSource(const AudioSource&) = delete;
364     AudioSource(AudioSource&&) = delete;
365     AudioSource& operator=(const AudioSource&) = delete;
366     AudioSource& operator=(AudioSource&&) = delete;
367 
368     // Ugly workaround for AudioSourceProxy to wrap
369     // an existing AudioSource.
370     friend class AudioSourceProxy;
371     AudioSource(
372             const AudioSource& inner,
373             const audio::SignalInfo& signalInfo);
374 
375     std::optional<WritableSampleFrames> clampWritableSampleFrames(
376             const WritableSampleFrames& sampleFrames) const;
377 
378     audio::SignalInfo m_signalInfo;
379 
380     audio::Bitrate m_bitrate;
381 
382     IndexRange m_frameIndexRange;
383 };
384 
385 typedef std::shared_ptr<AudioSource> AudioSourcePointer;
386 
387 inline QDebug operator<<(QDebug dbg, AudioSource::OpenMode openMode) {
388     switch (openMode) {
389     case AudioSource::OpenMode::Strict:
390         return dbg << "Strict";
391     case AudioSource::OpenMode::Permissive:
392         return dbg << "Permissive";
393     default:
394         DEBUG_ASSERT(!"Unknown OpenMode");
395         return dbg << "Unknown";
396     }
397 }
398 
399 inline QDebug operator<<(QDebug dbg, AudioSource::OpenResult openResult) {
400     switch (openResult) {
401     case AudioSource::OpenResult::Succeeded:
402         return dbg << "Succeeded";
403     case AudioSource::OpenResult::Aborted:
404         return dbg << "Aborted";
405     case AudioSource::OpenResult::Failed:
406         return dbg << "Failed";
407     default:
408         DEBUG_ASSERT(!"Unknown OpenResult");
409         return dbg << "Unknown";
410     }
411 }
412 
413 } // namespace mixxx
414