The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

356 lines
13KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace WindowsMediaCodec
  20. {
  21. class JuceIStream : public ComBaseClassHelper <IStream>
  22. {
  23. public:
  24. JuceIStream (InputStream& in) noexcept
  25. : ComBaseClassHelper <IStream> (0), source (in)
  26. {
  27. }
  28. JUCE_COMRESULT Commit (DWORD) { return S_OK; }
  29. JUCE_COMRESULT Write (const void*, ULONG, ULONG*) { return E_NOTIMPL; }
  30. JUCE_COMRESULT Clone (IStream**) { return E_NOTIMPL; }
  31. JUCE_COMRESULT SetSize (ULARGE_INTEGER) { return E_NOTIMPL; }
  32. JUCE_COMRESULT Revert() { return E_NOTIMPL; }
  33. JUCE_COMRESULT LockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
  34. JUCE_COMRESULT UnlockRegion (ULARGE_INTEGER, ULARGE_INTEGER, DWORD) { return E_NOTIMPL; }
  35. JUCE_COMRESULT Read (void* dest, ULONG numBytes, ULONG* bytesRead)
  36. {
  37. const int numRead = source.read (dest, numBytes);
  38. if (bytesRead != nullptr)
  39. *bytesRead = numRead;
  40. return (numRead == (int) numBytes) ? S_OK : S_FALSE;
  41. }
  42. JUCE_COMRESULT Seek (LARGE_INTEGER position, DWORD origin, ULARGE_INTEGER* resultPosition)
  43. {
  44. int64 newPos = (int64) position.QuadPart;
  45. if (origin == STREAM_SEEK_CUR)
  46. {
  47. newPos += source.getPosition();
  48. }
  49. else if (origin == STREAM_SEEK_END)
  50. {
  51. const int64 len = source.getTotalLength();
  52. if (len < 0)
  53. return E_NOTIMPL;
  54. newPos += len;
  55. }
  56. if (resultPosition != nullptr)
  57. resultPosition->QuadPart = newPos;
  58. return source.setPosition (newPos) ? S_OK : E_NOTIMPL;
  59. }
  60. JUCE_COMRESULT CopyTo (IStream* destStream, ULARGE_INTEGER numBytesToDo,
  61. ULARGE_INTEGER* bytesRead, ULARGE_INTEGER* bytesWritten)
  62. {
  63. uint64 totalCopied = 0;
  64. int64 numBytes = numBytesToDo.QuadPart;
  65. while (numBytes > 0 && ! source.isExhausted())
  66. {
  67. char buffer [1024];
  68. const int numToCopy = (int) jmin ((int64) sizeof (buffer), (int64) numBytes);
  69. const int numRead = source.read (buffer, numToCopy);
  70. if (numRead <= 0)
  71. break;
  72. destStream->Write (buffer, numRead, nullptr);
  73. totalCopied += numRead;
  74. }
  75. if (bytesRead != nullptr) bytesRead->QuadPart = totalCopied;
  76. if (bytesWritten != nullptr) bytesWritten->QuadPart = totalCopied;
  77. return S_OK;
  78. }
  79. JUCE_COMRESULT Stat (STATSTG* stat, DWORD)
  80. {
  81. if (stat == nullptr)
  82. return STG_E_INVALIDPOINTER;
  83. zerostruct (*stat);
  84. stat->type = STGTY_STREAM;
  85. stat->cbSize.QuadPart = jmax ((int64) 0, source.getTotalLength());
  86. return S_OK;
  87. }
  88. private:
  89. InputStream& source;
  90. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceIStream)
  91. };
  92. //==============================================================================
  93. static const char* wmFormatName = "Windows Media";
  94. static const char* const extensions[] = { ".mp3", ".wmv", ".asf", ".wm", ".wma", 0 };
  95. //==============================================================================
  96. class WMAudioReader : public AudioFormatReader
  97. {
  98. public:
  99. WMAudioReader (InputStream* const input_)
  100. : AudioFormatReader (input_, TRANS (wmFormatName)),
  101. wmvCoreLib ("Wmvcore.dll")
  102. {
  103. JUCE_LOAD_WINAPI_FUNCTION (wmvCoreLib, WMCreateSyncReader, wmCreateSyncReader,
  104. HRESULT, (IUnknown*, DWORD, IWMSyncReader**))
  105. if (wmCreateSyncReader != nullptr)
  106. {
  107. checkCoInitialiseCalled();
  108. HRESULT hr = wmCreateSyncReader (nullptr, WMT_RIGHT_PLAYBACK, wmSyncReader.resetAndGetPointerAddress());
  109. if (SUCCEEDED (hr))
  110. hr = wmSyncReader->OpenStream (new JuceIStream (*input));
  111. if (SUCCEEDED (hr))
  112. {
  113. WORD streamNum = 1;
  114. hr = wmSyncReader->GetStreamNumberForOutput (0, &streamNum);
  115. hr = wmSyncReader->SetReadStreamSamples (streamNum, false);
  116. scanFileForDetails();
  117. }
  118. }
  119. }
  120. ~WMAudioReader()
  121. {
  122. if (wmSyncReader != nullptr)
  123. wmSyncReader->Close();
  124. }
  125. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  126. int64 startSampleInFile, int numSamples) override
  127. {
  128. if (sampleRate <= 0)
  129. return false;
  130. checkCoInitialiseCalled();
  131. clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer,
  132. startSampleInFile, numSamples, lengthInSamples);
  133. const int stride = numChannels * sizeof (int16);
  134. while (numSamples > 0)
  135. {
  136. if (! bufferedRange.contains (startSampleInFile))
  137. {
  138. const bool hasJumped = (startSampleInFile != bufferedRange.getEnd());
  139. if (hasJumped)
  140. wmSyncReader->SetRange ((QWORD) (startSampleInFile * 10000000 / (int64) sampleRate), 0);
  141. ComSmartPtr<INSSBuffer> sampleBuffer;
  142. QWORD sampleTime, duration;
  143. DWORD flags, outputNum;
  144. WORD streamNum;
  145. HRESULT hr = wmSyncReader->GetNextSample (1, sampleBuffer.resetAndGetPointerAddress(),
  146. &sampleTime, &duration, &flags, &outputNum, &streamNum);
  147. if (sampleBuffer != nullptr)
  148. {
  149. BYTE* rawData = nullptr;
  150. DWORD dataLength = 0;
  151. hr = sampleBuffer->GetBufferAndLength (&rawData, &dataLength);
  152. if (dataLength == 0)
  153. return false;
  154. if (hasJumped)
  155. bufferedRange.setStart ((int64) ((sampleTime * (int64) sampleRate) / 10000000));
  156. else
  157. bufferedRange.setStart (bufferedRange.getEnd()); // (because the positions returned often aren't continguous)
  158. bufferedRange.setLength ((int64) (dataLength / stride));
  159. buffer.ensureSize ((int) dataLength);
  160. memcpy (buffer.getData(), rawData, (size_t) dataLength);
  161. }
  162. else if (hr == NS_E_NO_MORE_SAMPLES)
  163. {
  164. bufferedRange.setStart (startSampleInFile);
  165. bufferedRange.setLength (256);
  166. buffer.ensureSize (256 * stride);
  167. buffer.fillWith (0);
  168. }
  169. else
  170. {
  171. return false;
  172. }
  173. }
  174. const int offsetInBuffer = (int) (startSampleInFile - bufferedRange.getStart());
  175. const int16* const rawData = static_cast<const int16*> (addBytesToPointer (buffer.getData(), offsetInBuffer * stride));
  176. const int numToDo = jmin (numSamples, (int) (bufferedRange.getLength() - offsetInBuffer));
  177. for (int i = 0; i < numDestChannels; ++i)
  178. {
  179. jassert (destSamples[i] != nullptr);
  180. const int srcChan = jmin (i, (int) numChannels - 1);
  181. const int16* src = rawData + srcChan;
  182. int* const dst = destSamples[i] + startOffsetInDestBuffer;
  183. for (int j = 0; j < numToDo; ++j)
  184. {
  185. dst[j] = ((uint32) *src) << 16;
  186. src += numChannels;
  187. }
  188. }
  189. startSampleInFile += numToDo;
  190. startOffsetInDestBuffer += numToDo;
  191. numSamples -= numToDo;
  192. }
  193. return true;
  194. }
  195. private:
  196. DynamicLibrary wmvCoreLib;
  197. ComSmartPtr<IWMSyncReader> wmSyncReader;
  198. MemoryBlock buffer;
  199. Range<int64> bufferedRange;
  200. void checkCoInitialiseCalled()
  201. {
  202. CoInitialize (0);
  203. }
  204. void scanFileForDetails()
  205. {
  206. ComSmartPtr<IWMHeaderInfo> wmHeaderInfo;
  207. HRESULT hr = wmSyncReader.QueryInterface (wmHeaderInfo);
  208. if (SUCCEEDED (hr))
  209. {
  210. QWORD lengthInNanoseconds = 0;
  211. WORD lengthOfLength = sizeof (lengthInNanoseconds);
  212. WORD streamNum = 0;
  213. WMT_ATTR_DATATYPE wmAttrDataType;
  214. hr = wmHeaderInfo->GetAttributeByName (&streamNum, L"Duration", &wmAttrDataType,
  215. (BYTE*) &lengthInNanoseconds, &lengthOfLength);
  216. ComSmartPtr<IWMProfile> wmProfile;
  217. hr = wmSyncReader.QueryInterface (wmProfile);
  218. if (SUCCEEDED (hr))
  219. {
  220. ComSmartPtr<IWMStreamConfig> wmStreamConfig;
  221. hr = wmProfile->GetStream (0, wmStreamConfig.resetAndGetPointerAddress());
  222. if (SUCCEEDED (hr))
  223. {
  224. ComSmartPtr<IWMMediaProps> wmMediaProperties;
  225. hr = wmStreamConfig.QueryInterface (wmMediaProperties);
  226. if (SUCCEEDED (hr))
  227. {
  228. DWORD sizeMediaType;
  229. hr = wmMediaProperties->GetMediaType (0, &sizeMediaType);
  230. HeapBlock<WM_MEDIA_TYPE> mediaType;
  231. mediaType.malloc (sizeMediaType, 1);
  232. hr = wmMediaProperties->GetMediaType (mediaType, &sizeMediaType);
  233. if (mediaType->majortype == WMMEDIATYPE_Audio)
  234. {
  235. const WAVEFORMATEX* const inputFormat = reinterpret_cast<WAVEFORMATEX*> (mediaType->pbFormat);
  236. sampleRate = inputFormat->nSamplesPerSec;
  237. numChannels = inputFormat->nChannels;
  238. bitsPerSample = inputFormat->wBitsPerSample != 0 ? inputFormat->wBitsPerSample : 16;
  239. lengthInSamples = (lengthInNanoseconds * (int) sampleRate) / 10000000;
  240. }
  241. }
  242. }
  243. }
  244. }
  245. }
  246. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WMAudioReader)
  247. };
  248. }
  249. //==============================================================================
  250. WindowsMediaAudioFormat::WindowsMediaAudioFormat()
  251. : AudioFormat (TRANS (WindowsMediaCodec::wmFormatName),
  252. StringArray (WindowsMediaCodec::extensions))
  253. {
  254. }
  255. WindowsMediaAudioFormat::~WindowsMediaAudioFormat() {}
  256. Array<int> WindowsMediaAudioFormat::getPossibleSampleRates() { return Array<int>(); }
  257. Array<int> WindowsMediaAudioFormat::getPossibleBitDepths() { return Array<int>(); }
  258. bool WindowsMediaAudioFormat::canDoStereo() { return true; }
  259. bool WindowsMediaAudioFormat::canDoMono() { return true; }
  260. bool WindowsMediaAudioFormat::isCompressed() { return true; }
  261. //==============================================================================
  262. AudioFormatReader* WindowsMediaAudioFormat::createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails)
  263. {
  264. ScopedPointer<WindowsMediaCodec::WMAudioReader> r (new WindowsMediaCodec::WMAudioReader (sourceStream));
  265. if (r->sampleRate > 0)
  266. return r.release();
  267. if (! deleteStreamIfOpeningFails)
  268. r->input = nullptr;
  269. return nullptr;
  270. }
  271. AudioFormatWriter* WindowsMediaAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/, double /*sampleRateToUse*/,
  272. unsigned int /*numberOfChannels*/, int /*bitsPerSample*/,
  273. const StringPairArray& /*metadataValues*/, int /*qualityOptionIndex*/)
  274. {
  275. jassertfalse; // not yet implemented!
  276. return nullptr;
  277. }