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.

354 lines
13KB

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