OpenShot Library | libopenshot-audio 0.2.0
juce_WindowsMediaAudioFormat.cpp
1/*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2017 - ROLI Ltd.
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 5 End-User License
11 Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
12 27th April 2017).
13
14 End User License Agreement: www.juce.com/juce-5-licence
15 Privacy Policy: www.juce.com/juce-5-privacy-policy
16
17 Or: You may also use this code under the terms of the GPL v3 (see
18 www.gnu.org/licenses).
19
20 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22 DISCLAIMED.
23
24 ==============================================================================
25*/
26
27namespace juce
28{
29
30namespace WindowsMediaCodec
31{
32
33class JuceIStream : public ComBaseClassHelper <IStream>
34{
35public:
36 JuceIStream (InputStream& in) noexcept
37 : ComBaseClassHelper <IStream> (0), source (in)
38 {
39 }
40
41 JUCE_COMRESULT Commit (DWORD) { return S_OK; }
42 JUCE_COMRESULT Write (const void*, ULONG, ULONG*) { return E_NOTIMPL; }
43 JUCE_COMRESULT Clone (IStream**) { return E_NOTIMPL; }
44 JUCE_COMRESULT SetSize (ULARGE_INTEGER) { return E_NOTIMPL; }
45 JUCE_COMRESULT Revert() { return E_NOTIMPL; }
46 JUCE_COMRESULT LockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
47 JUCE_COMRESULT UnlockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
48
49 JUCE_COMRESULT Read (void* dest, ULONG numBytes, ULONG* bytesRead)
50 {
51 auto numRead = source.read (dest, numBytes);
52
53 if (bytesRead != nullptr)
54 *bytesRead = numRead;
55
56 return (numRead == (int) numBytes) ? S_OK : S_FALSE;
57 }
58
59 JUCE_COMRESULT Seek (LARGE_INTEGER position, DWORD origin, ULARGE_INTEGER* resultPosition)
60 {
61 auto newPos = (int64) position.QuadPart;
62
63 if (origin == STREAM_SEEK_CUR)
64 {
65 newPos += source.getPosition();
66 }
67 else if (origin == STREAM_SEEK_END)
68 {
69 auto len = source.getTotalLength();
70
71 if (len < 0)
72 return E_NOTIMPL;
73
74 newPos += len;
75 }
76
77 if (resultPosition != nullptr)
78 resultPosition->QuadPart = newPos;
79
80 return source.setPosition (newPos) ? S_OK : E_NOTIMPL;
81 }
82
83 JUCE_COMRESULT CopyTo (IStream* destStream, ULARGE_INTEGER numBytesToDo,
84 ULARGE_INTEGER* bytesRead, ULARGE_INTEGER* bytesWritten)
85 {
86 uint64 totalCopied = 0;
87 int64 numBytes = numBytesToDo.QuadPart;
88
89 while (numBytes > 0 && ! source.isExhausted())
90 {
91 char buffer [1024];
92
93 auto numToCopy = (int) jmin ((int64) sizeof (buffer), (int64) numBytes);
94 auto numRead = source.read (buffer, numToCopy);
95
96 if (numRead <= 0)
97 break;
98
99 destStream->Write (buffer, numRead, nullptr);
100 totalCopied += numRead;
101 }
102
103 if (bytesRead != nullptr) bytesRead->QuadPart = totalCopied;
104 if (bytesWritten != nullptr) bytesWritten->QuadPart = totalCopied;
105
106 return S_OK;
107 }
108
109 JUCE_COMRESULT Stat (STATSTG* stat, DWORD)
110 {
111 if (stat == nullptr)
112 return STG_E_INVALIDPOINTER;
113
114 zerostruct (*stat);
115 stat->type = STGTY_STREAM;
116 stat->cbSize.QuadPart = jmax ((int64) 0, source.getTotalLength());
117 return S_OK;
118 }
119
120private:
121 InputStream& source;
122
123 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceIStream)
124};
125
126//==============================================================================
127static const char* wmFormatName = "Windows Media";
128static const char* const extensions[] = { ".mp3", ".wmv", ".asf", ".wm", ".wma", 0 };
129
130//==============================================================================
132{
133public:
134 WMAudioReader (InputStream* const input_)
135 : AudioFormatReader (input_, TRANS (wmFormatName)),
136 wmvCoreLib ("Wmvcore.dll")
137 {
138 JUCE_LOAD_WINAPI_FUNCTION (wmvCoreLib, WMCreateSyncReader, wmCreateSyncReader,
139 HRESULT, (IUnknown*, DWORD, IWMSyncReader**))
140
141 if (wmCreateSyncReader != nullptr)
142 {
143 checkCoInitialiseCalled();
144
145 HRESULT hr = wmCreateSyncReader (nullptr, WMT_RIGHT_PLAYBACK, wmSyncReader.resetAndGetPointerAddress());
146
147 if (SUCCEEDED (hr))
148 hr = wmSyncReader->OpenStream (new JuceIStream (*input));
149
150 if (SUCCEEDED (hr))
151 {
152 WORD streamNum = 1;
153 hr = wmSyncReader->GetStreamNumberForOutput (0, &streamNum);
154 hr = wmSyncReader->SetReadStreamSamples (streamNum, false);
155
156 scanFileForDetails();
157 }
158 }
159 }
160
162 {
163 if (wmSyncReader != nullptr)
164 wmSyncReader->Close();
165 }
166
167 bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
168 int64 startSampleInFile, int numSamples) override
169 {
170 if (sampleRate <= 0)
171 return false;
172
173 checkCoInitialiseCalled();
174
175 clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
176 startSampleInFile, numSamples, lengthInSamples);
177
178 const int stride = numChannels * sizeof (int16);
179
180 while (numSamples > 0)
181 {
182 if (! bufferedRange.contains (startSampleInFile))
183 {
184 const bool hasJumped = (startSampleInFile != bufferedRange.getEnd());
185
186 if (hasJumped)
187 wmSyncReader->SetRange ((QWORD) (startSampleInFile * 10000000 / (int64) sampleRate), 0);
188
189 ComSmartPtr<INSSBuffer> sampleBuffer;
190 QWORD sampleTime, duration;
191 DWORD flags, outputNum;
192 WORD streamNum;
193
194 HRESULT hr = wmSyncReader->GetNextSample (1, sampleBuffer.resetAndGetPointerAddress(),
195 &sampleTime, &duration, &flags, &outputNum, &streamNum);
196
197 if (sampleBuffer != nullptr)
198 {
199 BYTE* rawData = nullptr;
200 DWORD dataLength = 0;
201 hr = sampleBuffer->GetBufferAndLength (&rawData, &dataLength);
202
203 if (dataLength == 0)
204 return false;
205
206 if (hasJumped)
207 bufferedRange.setStart ((int64) ((sampleTime * (int64) sampleRate) / 10000000));
208 else
209 bufferedRange.setStart (bufferedRange.getEnd()); // (because the positions returned often aren't continguous)
210
211 bufferedRange.setLength ((int64) (dataLength / stride));
212
213 buffer.ensureSize ((int) dataLength);
214 memcpy (buffer.getData(), rawData, (size_t) dataLength);
215 }
216 else if (hr == NS_E_NO_MORE_SAMPLES)
217 {
218 bufferedRange.setStart (startSampleInFile);
219 bufferedRange.setLength (256);
220 buffer.ensureSize (256 * stride);
221 buffer.fillWith (0);
222 }
223 else
224 {
225 return false;
226 }
227 }
228
229 auto offsetInBuffer = (int) (startSampleInFile - bufferedRange.getStart());
230 auto* rawData = static_cast<const int16*> (addBytesToPointer (buffer.getData(), offsetInBuffer * stride));
231 auto numToDo = jmin (numSamples, (int) (bufferedRange.getLength() - offsetInBuffer));
232
233 for (int i = 0; i < numDestChannels; ++i)
234 {
235 jassert (destSamples[i] != nullptr);
236
237 auto srcChan = jmin (i, (int) numChannels - 1);
238 const int16* src = rawData + srcChan;
239 int* const dst = destSamples[i] + startOffsetInDestBuffer;
240
241 for (int j = 0; j < numToDo; ++j)
242 {
243 dst[j] = ((uint32) *src) << 16;
244 src += numChannels;
245 }
246 }
247
248 startSampleInFile += numToDo;
249 startOffsetInDestBuffer += numToDo;
250 numSamples -= numToDo;
251 }
252
253 return true;
254 }
255
256private:
257 DynamicLibrary wmvCoreLib;
258 ComSmartPtr<IWMSyncReader> wmSyncReader;
259 MemoryBlock buffer;
260 Range<int64> bufferedRange;
261
262 void checkCoInitialiseCalled()
263 {
264 CoInitialize (0);
265 }
266
267 void scanFileForDetails()
268 {
269 ComSmartPtr<IWMHeaderInfo> wmHeaderInfo;
270 HRESULT hr = wmSyncReader.QueryInterface (wmHeaderInfo);
271
272 if (SUCCEEDED (hr))
273 {
274 QWORD lengthInNanoseconds = 0;
275 WORD lengthOfLength = sizeof (lengthInNanoseconds);
276 WORD streamNum = 0;
277 WMT_ATTR_DATATYPE wmAttrDataType;
278 hr = wmHeaderInfo->GetAttributeByName (&streamNum, L"Duration", &wmAttrDataType,
279 (BYTE*) &lengthInNanoseconds, &lengthOfLength);
280
281 ComSmartPtr<IWMProfile> wmProfile;
282 hr = wmSyncReader.QueryInterface (wmProfile);
283
284 if (SUCCEEDED (hr))
285 {
286 ComSmartPtr<IWMStreamConfig> wmStreamConfig;
287 hr = wmProfile->GetStream (0, wmStreamConfig.resetAndGetPointerAddress());
288
289 if (SUCCEEDED (hr))
290 {
291 ComSmartPtr<IWMMediaProps> wmMediaProperties;
292 hr = wmStreamConfig.QueryInterface (wmMediaProperties);
293
294 if (SUCCEEDED (hr))
295 {
296 DWORD sizeMediaType;
297 hr = wmMediaProperties->GetMediaType (0, &sizeMediaType);
298
299 HeapBlock<WM_MEDIA_TYPE> mediaType;
300 mediaType.malloc (sizeMediaType, 1);
301 hr = wmMediaProperties->GetMediaType (mediaType, &sizeMediaType);
302
303 if (mediaType->majortype == WMMEDIATYPE_Audio)
304 {
305 auto* inputFormat = reinterpret_cast<WAVEFORMATEX*> (mediaType->pbFormat);
306
307 sampleRate = inputFormat->nSamplesPerSec;
308 numChannels = inputFormat->nChannels;
309 bitsPerSample = inputFormat->wBitsPerSample != 0 ? inputFormat->wBitsPerSample : 16;
310 lengthInSamples = (lengthInNanoseconds * (int) sampleRate) / 10000000;
311 }
312 }
313 }
314 }
315 }
316 }
317
318 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WMAudioReader)
319};
320
321}
322
323//==============================================================================
324WindowsMediaAudioFormat::WindowsMediaAudioFormat()
325 : AudioFormat (TRANS (WindowsMediaCodec::wmFormatName),
326 StringArray (WindowsMediaCodec::extensions))
327{
328}
329
330WindowsMediaAudioFormat::~WindowsMediaAudioFormat() {}
331
334
338
339//==============================================================================
340AudioFormatReader* WindowsMediaAudioFormat::createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails)
341{
342 std::unique_ptr<WindowsMediaCodec::WMAudioReader> r (new WindowsMediaCodec::WMAudioReader (sourceStream));
343
344 if (r->sampleRate > 0)
345 return r.release();
346
347 if (! deleteStreamIfOpeningFails)
348 r->input = nullptr;
349
350 return nullptr;
351}
352
353AudioFormatWriter* WindowsMediaAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/, double /*sampleRateToUse*/,
354 unsigned int /*numberOfChannels*/, int /*bitsPerSample*/,
355 const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/)
356{
357 jassertfalse; // not yet implemented!
358 return nullptr;
359}
360
361} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:60
Reads samples from an audio file stream.
InputStream * input
The input stream, for use by subclasses.
static void clearSamplesBeyondAvailableLength(int **destChannels, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int &numSamples, int64 fileLengthInSamples)
Used by AudioFormatReader subclasses to clear any parts of the data blocks that lie beyond the end of...
int64 lengthInSamples
The total number of samples in the audio stream.
double sampleRate
The sample-rate of the stream.
unsigned int bitsPerSample
The number of bits per sample, e.g.
unsigned int numChannels
The total number of channels in the audio stream.
Writes samples to an audio file stream.
Handles the opening and closing of DLLs.
Very simple container class to hold a pointer to some data on the heap.
void malloc(SizeType newNumElements, size_t elementSize=sizeof(ElementType))
Allocates a specified amount of memory.
The base class for streams that read data.
virtual int64 getPosition()=0
Returns the offset of the next byte that will be read from the stream.
virtual bool setPosition(int64 newPosition)=0
Tries to move the current read position of the stream.
virtual bool isExhausted()=0
Returns true if the stream has no more data to read.
virtual int64 getTotalLength()=0
Returns the total number of bytes available for reading in this stream.
virtual int read(void *destBuffer, int maxBytesToRead)=0
Reads some data from the stream into a memory buffer.
A class to hold a resizable block of raw data.
void ensureSize(const size_t minimumSize, bool initialiseNewSpaceToZero=false)
Increases the block's size only if it's smaller than a given size.
void fillWith(uint8 valueToUse) noexcept
Fills the entire memory block with a repeated byte value.
void * getData() const noexcept
Returns a void pointer to the data.
The base class for streams that write data to some kind of destination.
A general-purpose range object, that simply represents any linear range with a start and end point.
Definition juce_Range.h:44
JUCE_CONSTEXPR ValueType getStart() const noexcept
Returns the start of the range.
Definition juce_Range.h:84
JUCE_CONSTEXPR bool contains(const ValueType position) const noexcept
Returns true if the given position lies inside this range.
Definition juce_Range.h:213
JUCE_CONSTEXPR ValueType getLength() const noexcept
Returns the length of the range.
Definition juce_Range.h:87
void setLength(const ValueType newLength) noexcept
Changes the length of the range.
Definition juce_Range.h:151
void setStart(const ValueType newStart) noexcept
Changes the start position of the range, leaving the end position unchanged.
Definition juce_Range.h:100
JUCE_CONSTEXPR ValueType getEnd() const noexcept
Returns the end of the range.
Definition juce_Range.h:90
A container for holding a set of strings which are keyed by another string.
bool isCompressed() override
Returns true if the format uses compressed data.
Array< int > getPossibleSampleRates() override
Returns a set of sample rates that the format can read and write.
bool canDoMono() override
Returns true if the format can do 1-channel audio.
Array< int > getPossibleBitDepths() override
Returns a set of bit depths that the format can read and write.
AudioFormatReader * createReaderFor(InputStream *, bool deleteStreamIfOpeningFails) override
Tries to create an object that can read from a stream containing audio data in this format.
AudioFormatWriter * createWriterFor(OutputStream *, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray &metadataValues, int qualityOptionIndex) override
Tries to create an object that can write to a stream with this audio format.
bool canDoStereo() override
Returns true if the format can do 2-channel audio.
bool readSamples(int **destSamples, int numDestChannels, int startOffsetInDestBuffer, int64 startSampleInFile, int numSamples) override
Subclasses must implement this method to perform the low-level read operation.