summaryrefslogtreecommitdiffstats
path: root/src/sources/audiosource.h
blob: dee01aa35945cf0f46e7151a6bd36b1977adfe16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
#pragma once

#include "audio/streaminfo.h"
#include "engine/engine.h"
#include "sources/urlresource.h"
#include "util/indexrange.h"
#include "util/memory.h"
#include "util/samplebuffer.h"

namespace mixxx {

class SampleFrames {
  public:
    SampleFrames() = default;
    explicit SampleFrames(
            IndexRange frameIndexRange)
            : m_frameIndexRange(frameIndexRange) {
    }
    /*non-virtual*/ ~SampleFrames() = default;

    IndexRange frameIndexRange() const {
        return m_frameIndexRange;
    }

    SINT frameLength() const {
        return m_frameIndexRange.length();
    }

  private:
    IndexRange m_frameIndexRange;
};

// Associates a range of frame indices with the corresponding
// readable sample data as a slice in some sample buffer. The
// memory is owned by the external sample buffer that must not
// be modified or destroyed while reading sample data!
class ReadableSampleFrames final : public SampleFrames {
  public:
    ReadableSampleFrames() = default;
    explicit ReadableSampleFrames(
            IndexRange frameIndexRange,
            SampleBuffer::ReadableSlice readableSlice = SampleBuffer::ReadableSlice())
            : SampleFrames(frameIndexRange),
              m_readableSlice(readableSlice) {
    }
    /*non-virtual*/ ~ReadableSampleFrames() = default;

    // The readable slice should cover the whole range of
    // frame indices and starts with the first frame. An
    // empty slice indicates that no sample data is available
    // for reading.
    SampleBuffer::ReadableSlice readableSlice() const {
        return m_readableSlice;
    }

    SINT readableLength(SINT offset = 0) const {
        return m_readableSlice.length(offset);
    }

    const CSAMPLE* readableData(SINT offset = 0) const {
        return m_readableSlice.data(offset);
    }

  private:
    SampleBuffer::ReadableSlice m_readableSlice;
};

// Associates a range of frame indices with the corresponding
// writable sample data as a slice in some sample buffer. The
// memory is owned by the external sample buffer that must not
// be modified or destroyed while writing sample data!
class WritableSampleFrames final : public SampleFrames {
  public:
    WritableSampleFrames() = default;
    explicit WritableSampleFrames(
            IndexRange frameIndexRange,
            SampleBuffer::WritableSlice writableSlice = SampleBuffer::WritableSlice())
            : SampleFrames(frameIndexRange),
              m_writableSlice(writableSlice) {
    }
    /*non-virtual*/ ~WritableSampleFrames() = default;

    // The writable slice should cover the whole range of
    // frame indices and starts with the first frame. An
    // empty slice indicates that no sample data must
    // be written.
    SampleBuffer::WritableSlice writableSlice() const {
        return m_writableSlice;
    }

    SINT writableLength(SINT offset = 0) const {
        return m_writableSlice.length(offset);
    }

    CSAMPLE* writableData(SINT offset = 0) const {
        return m_writableSlice.data(offset);
    }

  private:
    SampleBuffer::WritableSlice m_writableSlice;
};

class IAudioSourceReader {
  public:
    virtual ~IAudioSourceReader() = default;

  protected:
    // Reads as much of the the requested sample frames and writes
    // them into the provided buffer. The capacity of the buffer
    // and the requested range have already been checked and
    // adjusted (= clamped) before if necessary.
    //
    // Returns the number of and decoded sample frames in a readable
    // buffer. The returned buffer is just a view/slice of the provided
    // writable buffer if the result is not empty. If the result is
    // empty the internal memory pointer of the returned buffer might
    // be null.
    virtual ReadableSampleFrames readSampleFramesClamped(
            const WritableSampleFrames& sampleFrames) = 0;

    // The following function is required for accessing the protected
    // read function from siblings implementing this interface, e.g.
    // for proxies and adapters.
    static ReadableSampleFrames readSampleFramesClampedOn(
            IAudioSourceReader& that,
            const WritableSampleFrames& sampleFrames) {
        return that.readSampleFramesClamped(sampleFrames);
    }
};

// Common base class for audio sources.
//
// Both the number of channels and the sample rate must
// be constant and are not allowed to change over time.
//
// Samples in a sample buffer are stored as consecutive frames,
// i.e. the samples of the channels are interleaved.
//
// Audio sources are implicitly opened upon creation and
// closed upon destruction.
class AudioSource : public UrlResource, public virtual /*implements*/ IAudioSourceReader {
  public:
    ~AudioSource() override = default;

    /// Defines how thoroughly the stream properties should be verified
    /// when opening an audio stream.
    enum class OpenMode {
        /// In Strict mode the opening operation should be aborted
        /// as soon as any inconsistencies of the stream properties
        /// are detected when setting up the decoding.
        Strict,

        /// Opening in Permissive mode is used only after opening
        /// in Strict mode has been aborted by all available
        /// SoundSource implementations.
        ///
        /// Example: Assume safe default values if mandatory stream
        /// properties could not be determined when setting up the
        /// decoding.
        Permissive,
    };

    /// Result of opening an audio stream.
    enum class OpenResult {
        /// The file has been opened successfully and should be readable.
        Succeeded,

        /// If a SoundSource is not able to open a file because of
        /// internal errors of if the format of the content is not
        /// supported it should return Aborted.
        ///
        /// Returning this error result gives other decoders with a
        /// lower priority the chance to open the same file.
        /// Example: A SoundSourceProvider has been registered for
        /// files with a certain extension, but the corresponding
        /// SoundSource does only support a subset of all possible
        /// data formats that might be stored in files with this
        /// extension.

        Aborted,
        /// If a SoundSource return Failed while opening a file the
        /// file itself is supposed to be corrupt.
        ///
        /// If this happens during OpenMode::Strict then other decoders
        /// with a lower priority are still given a chance to try
        /// opening the file. In OpenMode::Permissive the first
        /// SoundSource that returns Failed will declare this file
        /// corrupt.
        Failed,
    };

    // Parameters for opening audio sources
    class OpenParams {
      public:
        OpenParams() = default;
        OpenParams(
                audio::ChannelCount channelCount,
                audio::SampleRate sampleRate)
                : m_signalInfo(
                          channelCount,
                          sampleRate) {
        }

        const audio::SignalInfo& getSignalInfo() const {
            return m_signalInfo;
        }

        void setChannelCount(
                audio::ChannelCount channelCount) {
            m_signalInfo.setChannelCount(channelCount);
        }

        void setSampleRate(
                audio::SampleRate sampleRate) {
            m_signalInfo.setSampleRate(sampleRate);
        }

      private:
        audio::SignalInfo m_signalInfo;
    };

    // Opens the AudioSource for reading audio data.
    //
    // Since reopening is not supported close() will be called
    // implicitly before the AudioSource is actually opened.
    //
    // Optionally the caller may provide the desired properties of
    // the decoded audio signal. Some decoders are able to reduce
    // the number of channels or do resampling efficiently on the
    // fly while decoding the input data.
    OpenResult open(
            OpenMode mode,
            const OpenParams& params = OpenParams());

    // Closes the AudioSource and frees all resources.
    //
    // Might be called even if the AudioSource has never been
    // opened, has already been closed, or if opening has failed.
    virtual void close() = 0;

    const audio::SignalInfo& getSignalInfo() const {
        return m_signalInfo;
    }

    const audio::Bitrate getBitrate() const {
        return m_bitrate;
    }

    audio::StreamInfo getStreamInfo() const {
        return audio::StreamInfo(
                getSignalInfo(),
                getBitrate(),
                Duration::fromSeconds(getDuration()));
    }

    // The total length of audio data is bounded and measured in frames.
    IndexRange frameIndexRange() const {
        return m_frameIndexRange;
    }

    // The total length of audio data.
    SINT frameLength() const {
        return m_frameIndexRange.length();
    }

    // The index of the first frame.
    SINT frameIndexMin() const {
        DEBUG_ASSERT(m_frameIndexRange.start() <= m_frameIndexRange.end());
        return m_frameIndexRange.start();
    }

    // The index after the last frame.
    SINT frameIndexMax() const {
        DEBUG_ASSERT(m_frameIndexRange.start() <= m_frameIndexRange.end());
        return m_frameIndexRange.end();
    }

    // The sample frame index is valid within the range
    // [frameIndexMin(), frameIndexMax()]
    // including the upper bound of the range!
    bool isValidFrameIndex(SINT frameIndex) const {
        return m_frameIndexRange.clampIndex(frameIndex) == frameIndex;
    }

    // The actual duration in seconds.
    // Well defined only for valid files!
    inline bool hasDuration() const {
        return getSignalInfo().getSampleRate().isValid();
    }
    inline double getDuration() const {
        DEBUG_ASSERT(hasDuration()); // prevents division by zero
        return getSignalInfo().frames2secs(frameLength());
    }

    /// Verifies various properties to ensure that the audio data is
    /// actually readable. Warning messages are logged for properties
    /// with invalid values for diagnostic purposes. Also performs a
    /// basic read test.
    bool verifyReadable();

    ReadableSampleFrames readSampleFrames(
            const WritableSampleFrames& sampleFrames);

  protected:
    explicit AudioSource(const QUrl& url);

    bool initChannelCountOnce(audio::ChannelCount channelCount);
    bool initChannelCountOnce(int channelCount) {
        return initChannelCountOnce(audio::ChannelCount(channelCount));
    }

    bool initSampleRateOnce(audio::SampleRate sampleRate);
    bool initSampleRateOnce(SINT sampleRate) {
        return initSampleRateOnce(audio::SampleRate(sampleRate));
    }

    bool initBitrateOnce(audio::Bitrate bitrate);
    bool initBitrateOnce(SINT bitrate) {
        return initBitrateOnce(audio::Bitrate(bitrate));
    }

    bool initFrameIndexRangeOnce(
            IndexRange frameIndexRange);
    // The frame index range needs to be adjusted while
    // reading. This virtual function is an ugly hack!