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.

388 lines
14KB

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