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.

370 lines
13KB

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