1 #pragma once 2 3 #include <mfidl.h> 4 #include <mfreadwrite.h> 5 6 #include "sources/soundsourceprovider.h" 7 #include "util/readaheadsamplebuffer.h" 8 9 namespace mixxx { 10 11 class StreamUnitConverter final { 12 const static SINT kStreamUnitsPerSecond = 1000 * 1000 * 10; // frame length = 100 ns 13 14 public: StreamUnitConverter()15 StreamUnitConverter() 16 : m_pAudioSource(nullptr), 17 m_fromSampleFramesToStreamUnits(0), 18 m_fromStreamUnitsToSampleFrames(0) { 19 } StreamUnitConverter(const AudioSource * pAudioSource)20 explicit StreamUnitConverter(const AudioSource* pAudioSource) 21 : m_pAudioSource(pAudioSource), 22 m_fromSampleFramesToStreamUnits(double(kStreamUnitsPerSecond) / double(pAudioSource->getSignalInfo().getSampleRate())), 23 m_fromStreamUnitsToSampleFrames(double(pAudioSource->getSignalInfo().getSampleRate()) / double(kStreamUnitsPerSecond)) { 24 // The stream units should actually be much shorter than 25 // sample frames to minimize jitter and rounding. Even a 26 // frame at 192 kHz has a length of about 5000 ns >> 100 ns. 27 DEBUG_ASSERT(m_fromSampleFramesToStreamUnits > 50); 28 DEBUG_ASSERT(m_fromStreamUnitsToSampleFrames < 0.02); 29 } 30 fromFrameIndex(SINT frameIndex)31 LONGLONG fromFrameIndex(SINT frameIndex) const { 32 DEBUG_ASSERT(m_fromSampleFramesToStreamUnits > 0); 33 // Used for seeking, so we need to round down to hit the 34 // corresponding stream unit where the given stream unit 35 // starts. The reader will skip samples until it reaches 36 // the actual target position for reading. 37 const SINT frameIndexOffset = frameIndex - m_pAudioSource->frameIndexMin(); 38 return static_cast<LONGLONG>(floor(frameIndexOffset * m_fromSampleFramesToStreamUnits)); 39 } 40 toFrameIndex(LONGLONG streamPos)41 SINT toFrameIndex(LONGLONG streamPos) const { 42 DEBUG_ASSERT(m_fromStreamUnitsToSampleFrames > 0); 43 // The stream reports positions in units of 100ns. We have 44 // to round(!) this value to obtain the actual position in 45 // sample frames. 46 const SINT frameIndexOffset = static_cast<SINT>(round(streamPos * m_fromStreamUnitsToSampleFrames)); 47 return m_pAudioSource->frameIndexMin() + frameIndexOffset; 48 } 49 50 private: 51 const AudioSource* m_pAudioSource; 52 double m_fromSampleFramesToStreamUnits; 53 double m_fromStreamUnitsToSampleFrames; 54 }; 55 56 class SoundSourceMediaFoundation : public SoundSource { 57 public: 58 static const QString kDisplayName; 59 60 explicit SoundSourceMediaFoundation(const QUrl& url); 61 ~SoundSourceMediaFoundation() override; 62 63 void close() override; 64 65 protected: 66 ReadableSampleFrames readSampleFramesClamped( 67 const WritableSampleFrames& sampleFrames) override; 68 69 private: 70 OpenResult tryOpen( 71 OpenMode mode, 72 const AudioSource::OpenParams& params) override; 73 74 bool configureAudioStream(const AudioSource::OpenParams& params); 75 bool readProperties(); 76 77 void seekSampleFrame(SINT frameIndex); 78 79 HRESULT m_hrCoInitialize; 80 HRESULT m_hrMFStartup; 81 82 IMFSourceReader* m_pSourceReader; 83 84 StreamUnitConverter m_streamUnitConverter; 85 86 SINT m_currentFrameIndex; 87 88 ReadAheadSampleBuffer m_sampleBuffer; 89 }; 90 91 class SoundSourceProviderMediaFoundation : public SoundSourceProvider { 92 public: 93 static const QString kDisplayName; 94 static const QStringList kSupportedFileExtensions; 95 getDisplayName()96 QString getDisplayName() const override { 97 return kDisplayName; 98 } 99 getSupportedFileExtensions()100 QStringList getSupportedFileExtensions() const override { 101 return kSupportedFileExtensions; 102 } 103 104 SoundSourceProviderPriority getPriorityHint( 105 const QString& supportedFileExtension) const override; 106 107 SoundSourcePointer newSoundSource(const QUrl& url) override; 108 }; 109 110 } // namespace mixxx 111