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