OpenShot Audio Library | OpenShotAudio  0.3.1
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 //==============================================================================
131 class WMAudioReader : public AudioFormatReader
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 
332 Array<int> WindowsMediaAudioFormat::getPossibleSampleRates() { return {}; }
333 Array<int> WindowsMediaAudioFormat::getPossibleBitDepths() { return {}; }
334 
335 bool WindowsMediaAudioFormat::canDoStereo() { return true; }
336 bool WindowsMediaAudioFormat::canDoMono() { return true; }
337 bool WindowsMediaAudioFormat::isCompressed() { 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