OpenShot Library | OpenShotAudio  0.2.2
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 
27 namespace juce
28 {
29 
30 namespace WindowsMediaCodec
31 {
32 
33 class JuceIStream : public ComBaseClassHelper<IStream>
34 {
35 public:
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, (size_t) numBytes);
52 
53  if (bytesRead != nullptr)
54  *bytesRead = (ULONG) 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 
120 private:
121  InputStream& source;
122 
123  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceIStream)
124 };
125 
126 //==============================================================================
127 static const char* wmFormatName = "Windows Media";
128 static const char* const extensions[] = { ".mp3", ".wmv", ".asf", ".wm", ".wma", 0 };
129 
130 //==============================================================================
132 {
133 public:
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 
161  ~WMAudioReader()
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 contiguous)
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 
256 private:
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 //==============================================================================
324 WindowsMediaAudioFormat::WindowsMediaAudioFormat()
325  : AudioFormat (TRANS (WindowsMediaCodec::wmFormatName),
326  StringArray (WindowsMediaCodec::extensions))
327 {
328 }
329 
330 WindowsMediaAudioFormat::~WindowsMediaAudioFormat() {}
331 
334 
336 bool WindowsMediaAudioFormat::canDoMono() { return true; }
338 
339 //==============================================================================
340 AudioFormatReader* 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 
353 AudioFormatWriter* 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
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.
AudioFormatReader(InputStream *sourceStream, const String &formatName)
Creates an AudioFormatReader object.
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 * getData() noexcept
Returns a void pointer to the 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.
The base class for streams that write data to some kind of destination.
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.
virtual AudioFormatWriter * createWriterFor(OutputStream *streamToWriteTo, double sampleRateToUse, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray &metadataValues, int qualityOptionIndex)=0
Tries to create an object that can write to a stream with this audio format.
AudioFormatReader * createReaderFor(InputStream *, bool deleteStreamIfOpeningFails) override
Tries to create an object that can read from a stream containing audio data in this 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.