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.

331 lines
12KB

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