29namespace WindowsMediaCodec
32class JuceIStream final :
public ComBaseClassHelper<IStream>
35 JuceIStream (InputStream& in) noexcept
36 : ComBaseClassHelper (0), source (in)
40 JUCE_COMRESULT Commit (DWORD)
override {
return S_OK; }
41 JUCE_COMRESULT Write (
const void*, ULONG, ULONG*)
override {
return E_NOTIMPL; }
42 JUCE_COMRESULT Clone (IStream**)
override {
return E_NOTIMPL; }
43 JUCE_COMRESULT SetSize (ULARGE_INTEGER)
override {
return E_NOTIMPL; }
44 JUCE_COMRESULT Revert()
override {
return E_NOTIMPL; }
45 JUCE_COMRESULT LockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
override {
return E_NOTIMPL; }
46 JUCE_COMRESULT UnlockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
override {
return E_NOTIMPL; }
48 JUCE_COMRESULT Read (
void* dest, ULONG numBytes, ULONG* bytesRead)
override
50 auto numRead = source.read (dest, (
size_t) numBytes);
52 if (bytesRead !=
nullptr)
53 *bytesRead = (ULONG) numRead;
55 return (numRead == (
int) numBytes) ? S_OK : S_FALSE;
58 JUCE_COMRESULT Seek (LARGE_INTEGER position, DWORD origin, ULARGE_INTEGER* resultPosition)
override
60 auto newPos = (int64) position.QuadPart;
62 if (origin == STREAM_SEEK_CUR)
64 newPos += source.getPosition();
66 else if (origin == STREAM_SEEK_END)
68 auto len = source.getTotalLength();
76 if (resultPosition !=
nullptr)
77 resultPosition->QuadPart = (ULONGLONG) newPos;
79 return source.setPosition (newPos) ? S_OK : E_NOTIMPL;
82 JUCE_COMRESULT CopyTo (IStream* destStream, ULARGE_INTEGER numBytesToDo,
83 ULARGE_INTEGER* bytesRead, ULARGE_INTEGER* bytesWritten)
override
85 uint64 totalCopied = 0;
86 auto numBytes = (int64) numBytesToDo.QuadPart;
88 while (numBytes > 0 && ! source.isExhausted())
92 auto numToCopy = (int) jmin ((int64)
sizeof (buffer), (int64) numBytes);
93 auto numRead = source.read (buffer, numToCopy);
98 destStream->Write (buffer, (ULONG) numRead,
nullptr);
99 totalCopied += (ULONG) numRead;
102 if (bytesRead !=
nullptr) bytesRead->QuadPart = totalCopied;
103 if (bytesWritten !=
nullptr) bytesWritten->QuadPart = totalCopied;
108 JUCE_COMRESULT Stat (STATSTG* stat, DWORD)
override
111 return STG_E_INVALIDPOINTER;
114 stat->type = STGTY_STREAM;
115 stat->cbSize.QuadPart = (ULONGLONG) jmax ((int64) 0, source.getTotalLength());
122 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceIStream)
126static const char* wmFormatName =
"Windows Media";
127static const char*
const extensions[] = {
".mp3",
".wmv",
".asf",
".wm",
".wma",
nullptr };
130class WMAudioReader final :
public AudioFormatReader
133 WMAudioReader (InputStream*
const input_)
135 wmvCoreLib (
"Wmvcore.dll")
137 JUCE_LOAD_WINAPI_FUNCTION (wmvCoreLib, WMCreateSyncReader, wmCreateSyncReader,
138 HRESULT, (IUnknown*, DWORD, IWMSyncReader**))
140 if (wmCreateSyncReader !=
nullptr)
142 checkCoInitialiseCalled();
144 HRESULT hr = wmCreateSyncReader (
nullptr, WMT_RIGHT_PLAYBACK, wmSyncReader.resetAndGetPointerAddress());
147 hr = wmSyncReader->OpenStream (
new JuceIStream (*
input));
152 hr = wmSyncReader->GetStreamNumberForOutput (0, &streamNum);
153 hr = wmSyncReader->SetReadStreamSamples (streamNum,
false);
155 scanFileForDetails();
160 ~WMAudioReader()
override
162 if (wmSyncReader !=
nullptr)
163 wmSyncReader->Close();
166 bool readSamples (
int*
const* destSamples,
int numDestChannels,
int startOffsetInDestBuffer,
167 int64 startSampleInFile,
int numSamples)
override
172 checkCoInitialiseCalled();
177 const auto stride = (int) (
numChannels *
sizeof (int16));
179 while (numSamples > 0)
181 if (! bufferedRange.contains (startSampleInFile))
183 const bool hasJumped = (startSampleInFile != bufferedRange.getEnd());
186 wmSyncReader->SetRange ((QWORD) (startSampleInFile * 10000000 / (int64)
sampleRate), 0);
188 ComSmartPtr<INSSBuffer> sampleBuffer;
189 QWORD sampleTime, duration;
190 DWORD flags, outputNum;
193 HRESULT hr = wmSyncReader->GetNextSample (1, sampleBuffer.resetAndGetPointerAddress(),
194 &sampleTime, &duration, &flags, &outputNum, &streamNum);
196 if (sampleBuffer !=
nullptr)
198 BYTE* rawData =
nullptr;
199 DWORD dataLength = 0;
200 hr = sampleBuffer->GetBufferAndLength (&rawData, &dataLength);
206 bufferedRange.setStart ((int64) ((sampleTime * (QWORD)
sampleRate) / 10000000));
208 bufferedRange.setStart (bufferedRange.getEnd());
210 bufferedRange.setLength ((int64) dataLength / (int64) stride);
212 buffer.ensureSize ((
size_t) dataLength);
213 memcpy (buffer.getData(), rawData, (
size_t) dataLength);
215 else if (hr == NS_E_NO_MORE_SAMPLES)
217 bufferedRange.setStart (startSampleInFile);
218 bufferedRange.setLength (256);
219 buffer.ensureSize (256 * (
size_t) stride);
228 auto offsetInBuffer = (int) (startSampleInFile - bufferedRange.getStart());
229 auto* rawData =
static_cast<const int16*
> (addBytesToPointer (buffer.getData(), offsetInBuffer * stride));
230 auto numToDo = jmin (numSamples, (
int) (bufferedRange.getLength() - offsetInBuffer));
232 for (
int i = 0; i < numDestChannels; ++i)
234 JUCE_BEGIN_IGNORE_WARNINGS_MSVC (28182)
235 jassert (destSamples[i] !=
nullptr);
238 const int16* src = rawData + srcChan;
239 int* const dst = destSamples[i] + startOffsetInDestBuffer;
241 for (
int j = 0; j < numToDo; ++j)
243 dst[j] = (int) (((uint32) *src) << 16);
246 JUCE_END_IGNORE_WARNINGS_MSVC
249 startSampleInFile += numToDo;
250 startOffsetInDestBuffer += numToDo;
251 numSamples -= numToDo;
258 DynamicLibrary wmvCoreLib;
259 ComSmartPtr<IWMSyncReader> wmSyncReader;
261 Range<int64> bufferedRange;
263 void checkCoInitialiseCalled()
265 [[maybe_unused]]
const auto result = CoInitialize (
nullptr);
268 void scanFileForDetails()
270 if (
auto wmHeaderInfo = wmSyncReader.getInterface<IWMHeaderInfo>())
272 QWORD lengthInNanoseconds = 0;
273 WORD lengthOfLength =
sizeof (lengthInNanoseconds);
275 WMT_ATTR_DATATYPE wmAttrDataType;
276 wmHeaderInfo->GetAttributeByName (&streamNum, L
"Duration", &wmAttrDataType,
277 (BYTE*) &lengthInNanoseconds, &lengthOfLength);
279 if (
auto wmProfile = wmSyncReader.getInterface<IWMProfile>())
281 ComSmartPtr<IWMStreamConfig> wmStreamConfig;
282 auto hr = wmProfile->GetStream (0, wmStreamConfig.resetAndGetPointerAddress());
286 if (
auto wmMediaProperties = wmStreamConfig.getInterface<IWMMediaProps>())
289 hr = wmMediaProperties->GetMediaType (
nullptr, &sizeMediaType);
291 HeapBlock<WM_MEDIA_TYPE> mediaType;
292 mediaType.malloc (sizeMediaType, 1);
293 hr = wmMediaProperties->GetMediaType (mediaType, &sizeMediaType);
295 if (mediaType->majortype == WMMEDIATYPE_Audio)
297 auto* inputFormat =
reinterpret_cast<WAVEFORMATEX*
> (mediaType->pbFormat);
301 bitsPerSample = inputFormat->wBitsPerSample != 0 ? inputFormat->wBitsPerSample : 16;
310 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WMAudioReader)
316WindowsMediaAudioFormat::WindowsMediaAudioFormat()
317 :
AudioFormat (TRANS (WindowsMediaCodec::wmFormatName),
322WindowsMediaAudioFormat::~WindowsMediaAudioFormat() =
default;
324Array<int> WindowsMediaAudioFormat::getPossibleSampleRates() {
return {}; }
325Array<int> WindowsMediaAudioFormat::getPossibleBitDepths() {
return {}; }
327bool WindowsMediaAudioFormat::canDoStereo() {
return true; }
328bool WindowsMediaAudioFormat::canDoMono() {
return true; }
329bool WindowsMediaAudioFormat::isCompressed() {
return true; }
332AudioFormatReader* WindowsMediaAudioFormat::createReaderFor (InputStream* sourceStream,
bool deleteStreamIfOpeningFails)
334 std::unique_ptr<WindowsMediaCodec::WMAudioReader> r (
new WindowsMediaCodec::WMAudioReader (sourceStream));
336 if (r->sampleRate > 0)
339 if (! deleteStreamIfOpeningFails)
345AudioFormatWriter* WindowsMediaAudioFormat::createWriterFor (OutputStream* ,
double ,
347 const StringPairArray& ,
int )