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.

401 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 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. #include "../../core/juce_TargetPlatform.h"
  19. #include "../../../juce_Config.h"
  20. #if JUCE_QUICKTIME && ! (JUCE_64BIT || JUCE_IPHONE)
  21. #if ! JUCE_WINDOWS
  22. #include <QuickTime/Movies.h>
  23. #include <QuickTime/QTML.h>
  24. #include <QuickTime/QuickTimeComponents.h>
  25. #include <QuickTime/MediaHandlers.h>
  26. #include <QuickTime/ImageCodec.h>
  27. #else
  28. #if JUCE_MSVC
  29. #pragma warning (push)
  30. #pragma warning (disable : 4100)
  31. #endif
  32. /* If you've got an include error here, you probably need to install the QuickTime SDK and
  33. add its header directory to your include path.
  34. Alternatively, if you don't need any QuickTime services, just turn off the JUC_QUICKTIME
  35. flag in juce_Config.h
  36. */
  37. #include <Movies.h>
  38. #include <QTML.h>
  39. #include <QuickTimeComponents.h>
  40. #include <MediaHandlers.h>
  41. #include <ImageCodec.h>
  42. #if JUCE_MSVC
  43. #pragma warning (pop)
  44. #endif
  45. #endif
  46. #include "../../core/juce_StandardHeader.h"
  47. BEGIN_JUCE_NAMESPACE
  48. #include "juce_QuickTimeAudioFormat.h"
  49. #include "../../text/juce_LocalisedStrings.h"
  50. #include "../../threads/juce_Thread.h"
  51. #include "../../io/network/juce_URL.h"
  52. bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle);
  53. static const char* const quickTimeFormatName = "QuickTime file";
  54. static const tchar* const quickTimeExtensions[] = { T(".mov"), T(".mp3"), T(".mp4"), 0 };
  55. //==============================================================================
  56. class QTAudioReader : public AudioFormatReader
  57. {
  58. public:
  59. QTAudioReader (InputStream* const input_, const int trackNum_)
  60. : AudioFormatReader (input_, TRANS (quickTimeFormatName)),
  61. ok (false),
  62. movie (0),
  63. trackNum (trackNum_),
  64. extractor (0),
  65. lastSampleRead (0),
  66. lastThreadId (0),
  67. dataHandle (0)
  68. {
  69. bufferList.calloc (256, 1);
  70. #ifdef WIN32
  71. if (InitializeQTML (0) != noErr)
  72. return;
  73. #endif
  74. if (EnterMovies() != noErr)
  75. return;
  76. bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle);
  77. if (! opened)
  78. return;
  79. {
  80. const int numTracks = GetMovieTrackCount (movie);
  81. int trackCount = 0;
  82. for (int i = 1; i <= numTracks; ++i)
  83. {
  84. track = GetMovieIndTrack (movie, i);
  85. media = GetTrackMedia (track);
  86. OSType mediaType;
  87. GetMediaHandlerDescription (media, &mediaType, 0, 0);
  88. if (mediaType == SoundMediaType
  89. && trackCount++ == trackNum_)
  90. {
  91. ok = true;
  92. break;
  93. }
  94. }
  95. }
  96. if (! ok)
  97. return;
  98. ok = false;
  99. lengthInSamples = GetMediaDecodeDuration (media);
  100. usesFloatingPointData = false;
  101. samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media));
  102. trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame
  103. / GetMediaTimeScale (media);
  104. OSStatus err = MovieAudioExtractionBegin (movie, 0, &extractor);
  105. unsigned long output_layout_size;
  106. err = MovieAudioExtractionGetPropertyInfo (extractor,
  107. kQTPropertyClass_MovieAudioExtraction_Audio,
  108. kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
  109. 0, &output_layout_size, 0);
  110. if (err != noErr)
  111. return;
  112. HeapBlock <AudioChannelLayout> qt_audio_channel_layout;
  113. qt_audio_channel_layout.calloc (output_layout_size, 1);
  114. err = MovieAudioExtractionGetProperty (extractor,
  115. kQTPropertyClass_MovieAudioExtraction_Audio,
  116. kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
  117. output_layout_size, qt_audio_channel_layout, 0);
  118. qt_audio_channel_layout[0].mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
  119. err = MovieAudioExtractionSetProperty (extractor,
  120. kQTPropertyClass_MovieAudioExtraction_Audio,
  121. kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
  122. output_layout_size,
  123. qt_audio_channel_layout);
  124. err = MovieAudioExtractionGetProperty (extractor,
  125. kQTPropertyClass_MovieAudioExtraction_Audio,
  126. kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
  127. sizeof (inputStreamDesc),
  128. &inputStreamDesc, 0);
  129. if (err != noErr)
  130. return;
  131. inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger
  132. | kAudioFormatFlagIsPacked
  133. | kAudioFormatFlagsNativeEndian;
  134. inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8;
  135. inputStreamDesc.mChannelsPerFrame = jmin (2, inputStreamDesc.mChannelsPerFrame);
  136. inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame;
  137. inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame;
  138. err = MovieAudioExtractionSetProperty (extractor,
  139. kQTPropertyClass_MovieAudioExtraction_Audio,
  140. kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
  141. sizeof (inputStreamDesc),
  142. &inputStreamDesc);
  143. if (err != noErr)
  144. return;
  145. Boolean allChannelsDiscrete = false;
  146. err = MovieAudioExtractionSetProperty (extractor,
  147. kQTPropertyClass_MovieAudioExtraction_Movie,
  148. kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
  149. sizeof (allChannelsDiscrete),
  150. &allChannelsDiscrete);
  151. if (err != noErr)
  152. return;
  153. bufferList->mNumberBuffers = 1;
  154. bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame;
  155. bufferList->mBuffers[0].mDataByteSize = (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16;
  156. bufferList->mBuffers[0].mData = malloc (bufferList->mBuffers[0].mDataByteSize);
  157. sampleRate = inputStreamDesc.mSampleRate;
  158. bitsPerSample = 16;
  159. numChannels = inputStreamDesc.mChannelsPerFrame;
  160. detachThread();
  161. ok = true;
  162. }
  163. ~QTAudioReader()
  164. {
  165. if (dataHandle != 0)
  166. DisposeHandle (dataHandle);
  167. if (extractor != 0)
  168. {
  169. MovieAudioExtractionEnd (extractor);
  170. extractor = 0;
  171. }
  172. checkThreadIsAttached();
  173. DisposeMovie (movie);
  174. juce_free (bufferList->mBuffers[0].mData);
  175. #if JUCE_MAC
  176. ExitMoviesOnThread ();
  177. #endif
  178. }
  179. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  180. int64 startSampleInFile, int numSamples)
  181. {
  182. checkThreadIsAttached();
  183. while (numSamples > 0)
  184. {
  185. if (! loadFrame ((int) startSampleInFile))
  186. return false;
  187. const int numToDo = jmin (numSamples, samplesPerFrame);
  188. for (int j = numDestChannels; --j >= 0;)
  189. {
  190. if (destSamples[j] != 0)
  191. {
  192. const short* const src = ((const short*) bufferList->mBuffers[0].mData) + j;
  193. for (int i = 0; i < numToDo; ++i)
  194. destSamples[j][startOffsetInDestBuffer + i] = src [i << 1] << 16;
  195. }
  196. }
  197. startOffsetInDestBuffer += numToDo;
  198. startSampleInFile += numToDo;
  199. numSamples -= numToDo;
  200. }
  201. detachThread();
  202. return true;
  203. }
  204. bool loadFrame (const int sampleNum)
  205. {
  206. if (lastSampleRead != sampleNum)
  207. {
  208. TimeRecord time;
  209. time.scale = (TimeScale) inputStreamDesc.mSampleRate;
  210. time.base = 0;
  211. time.value.hi = 0;
  212. time.value.lo = (UInt32) sampleNum;
  213. OSStatus err = MovieAudioExtractionSetProperty (extractor,
  214. kQTPropertyClass_MovieAudioExtraction_Movie,
  215. kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
  216. sizeof (time), &time);
  217. if (err != noErr)
  218. return false;
  219. }
  220. bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * samplesPerFrame;
  221. UInt32 outFlags = 0;
  222. UInt32 actualNumSamples = samplesPerFrame;
  223. OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumSamples,
  224. bufferList, &outFlags);
  225. lastSampleRead = sampleNum + samplesPerFrame;
  226. return err == noErr;
  227. }
  228. juce_UseDebuggingNewOperator
  229. bool ok;
  230. private:
  231. Movie movie;
  232. Media media;
  233. Track track;
  234. const int trackNum;
  235. double trackUnitsPerFrame;
  236. int samplesPerFrame;
  237. int lastSampleRead;
  238. Thread::ThreadID lastThreadId;
  239. MovieAudioExtractionRef extractor;
  240. AudioStreamBasicDescription inputStreamDesc;
  241. HeapBlock <AudioBufferList> bufferList;
  242. Handle dataHandle;
  243. /*OSErr readMovieStream (long offset, long size, void* dataPtr)
  244. {
  245. input->setPosition (offset);
  246. input->read (dataPtr, size);
  247. return noErr;
  248. }
  249. static OSErr readMovieStreamProc (long offset, long size, void* dataPtr, void* userRef)
  250. {
  251. return ((QTAudioReader*) userRef)->readMovieStream (offset, size, dataPtr);
  252. }*/
  253. //==============================================================================
  254. void checkThreadIsAttached()
  255. {
  256. #if JUCE_MAC
  257. if (Thread::getCurrentThreadId() != lastThreadId)
  258. EnterMoviesOnThread (0);
  259. AttachMovieToCurrentThread (movie);
  260. #endif
  261. }
  262. void detachThread()
  263. {
  264. #if JUCE_MAC
  265. DetachMovieFromCurrentThread (movie);
  266. #endif
  267. }
  268. };
  269. //==============================================================================
  270. QuickTimeAudioFormat::QuickTimeAudioFormat()
  271. : AudioFormat (TRANS (quickTimeFormatName), (const tchar**) quickTimeExtensions)
  272. {
  273. }
  274. QuickTimeAudioFormat::~QuickTimeAudioFormat()
  275. {
  276. }
  277. const Array <int> QuickTimeAudioFormat::getPossibleSampleRates()
  278. {
  279. return Array<int>();
  280. }
  281. const Array <int> QuickTimeAudioFormat::getPossibleBitDepths()
  282. {
  283. return Array<int>();
  284. }
  285. bool QuickTimeAudioFormat::canDoStereo()
  286. {
  287. return true;
  288. }
  289. bool QuickTimeAudioFormat::canDoMono()
  290. {
  291. return true;
  292. }
  293. //==============================================================================
  294. AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream,
  295. const bool deleteStreamIfOpeningFails)
  296. {
  297. ScopedPointer <QTAudioReader> r (new QTAudioReader (sourceStream, 0));
  298. if (r->ok)
  299. return r.release();
  300. if (! deleteStreamIfOpeningFails)
  301. r->input = 0;
  302. return 0;
  303. }
  304. AudioFormatWriter* QuickTimeAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/,
  305. double /*sampleRateToUse*/,
  306. unsigned int /*numberOfChannels*/,
  307. int /*bitsPerSample*/,
  308. const StringPairArray& /*metadataValues*/,
  309. int /*qualityOptionIndex*/)
  310. {
  311. jassertfalse // not yet implemented!
  312. return 0;
  313. }
  314. END_JUCE_NAMESPACE
  315. #endif