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)
66 else if (origin == STREAM_SEEK_END)
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;
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);
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);
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),
318 StringArray (WindowsMediaCodec::extensions))
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 )
constexpr ValueType getStart() const noexcept
constexpr ValueType getEnd() const noexcept
void setLength(const ValueType newLength) noexcept
void setStart(const ValueType newStart) noexcept
constexpr ValueType getLength() const noexcept
constexpr bool contains(const ValueType position) const noexcept