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