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.

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