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.

389 lines
14KB

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