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.

390 lines
14KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #if JUCE_QUICKTIME && ! (JUCE_64BIT || JUCE_IOS)
  20. } // (juce namespace)
  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 set the JUCE_QUICKTIME flag to 0.
  35. */
  36. #undef SIZE_MAX
  37. #include <Movies.h>
  38. #include <QTML.h>
  39. #include <QuickTimeComponents.h>
  40. #include <MediaHandlers.h>
  41. #include <ImageCodec.h>
  42. #undef SIZE_MAX
  43. #if JUCE_MSVC
  44. #pragma warning (pop)
  45. #endif
  46. #endif
  47. namespace juce
  48. {
  49. bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle);
  50. static const char* const quickTimeFormatName = "QuickTime file";
  51. //==============================================================================
  52. class QTAudioReader : public AudioFormatReader
  53. {
  54. public:
  55. QTAudioReader (InputStream* const input_, const int trackNum_)
  56. : AudioFormatReader (input_, quickTimeFormatName),
  57. ok (false),
  58. movie (0),
  59. trackNum (trackNum_),
  60. lastSampleRead (0),
  61. lastThreadId (0),
  62. extractor (0),
  63. dataHandle (0)
  64. {
  65. JUCE_AUTORELEASEPOOL
  66. {
  67. bufferList.calloc (256, 1);
  68. #if JUCE_WINDOWS
  69. if (InitializeQTML (0) != noErr)
  70. return;
  71. #endif
  72. if (EnterMovies() != noErr)
  73. return;
  74. bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle);
  75. if (! opened)
  76. return;
  77. {
  78. const int numTracks = GetMovieTrackCount (movie);
  79. int trackCount = 0;
  80. for (int i = 1; i <= numTracks; ++i)
  81. {
  82. track = GetMovieIndTrack (movie, i);
  83. media = GetTrackMedia (track);
  84. OSType mediaType;
  85. GetMediaHandlerDescription (media, &mediaType, 0, 0);
  86. if (mediaType == SoundMediaType
  87. && trackCount++ == trackNum_)
  88. {
  89. ok = true;
  90. break;
  91. }
  92. }
  93. }
  94. if (! ok)
  95. return;
  96. ok = false;
  97. lengthInSamples = GetMediaDecodeDuration (media);
  98. usesFloatingPointData = false;
  99. samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media));
  100. trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame
  101. / GetMediaTimeScale (media);
  102. MovieAudioExtractionBegin (movie, 0, &extractor);
  103. unsigned long output_layout_size;
  104. OSStatus err = MovieAudioExtractionGetPropertyInfo (extractor,
  105. kQTPropertyClass_MovieAudioExtraction_Audio,
  106. kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
  107. 0, &output_layout_size, 0);
  108. if (err != noErr)
  109. return;
  110. HeapBlock<AudioChannelLayout> qt_audio_channel_layout;
  111. qt_audio_channel_layout.calloc (output_layout_size, 1);
  112. MovieAudioExtractionGetProperty (extractor,
  113. kQTPropertyClass_MovieAudioExtraction_Audio,
  114. kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
  115. output_layout_size, qt_audio_channel_layout, 0);
  116. qt_audio_channel_layout[0].mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
  117. MovieAudioExtractionSetProperty (extractor,
  118. kQTPropertyClass_MovieAudioExtraction_Audio,
  119. kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
  120. output_layout_size,
  121. qt_audio_channel_layout);
  122. err = MovieAudioExtractionGetProperty (extractor,
  123. kQTPropertyClass_MovieAudioExtraction_Audio,
  124. kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
  125. sizeof (inputStreamDesc),
  126. &inputStreamDesc, 0);
  127. if (err != noErr)
  128. return;
  129. inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger
  130. | kAudioFormatFlagIsPacked
  131. | kAudioFormatFlagsNativeEndian;
  132. inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8;
  133. inputStreamDesc.mChannelsPerFrame = jmin ((UInt32) 2, inputStreamDesc.mChannelsPerFrame);
  134. inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame;
  135. inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame;
  136. err = MovieAudioExtractionSetProperty (extractor,
  137. kQTPropertyClass_MovieAudioExtraction_Audio,
  138. kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
  139. sizeof (inputStreamDesc),
  140. &inputStreamDesc);
  141. if (err != noErr)
  142. return;
  143. Boolean allChannelsDiscrete = false;
  144. err = MovieAudioExtractionSetProperty (extractor,
  145. kQTPropertyClass_MovieAudioExtraction_Movie,
  146. kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
  147. sizeof (allChannelsDiscrete),
  148. &allChannelsDiscrete);
  149. if (err != noErr)
  150. return;
  151. bufferList->mNumberBuffers = 1;
  152. bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame;
  153. bufferList->mBuffers[0].mDataByteSize = jmax ((UInt32) 4096, (UInt32) (samplesPerFrame * (int) inputStreamDesc.mBytesPerFrame) + 16);
  154. dataBuffer.malloc (bufferList->mBuffers[0].mDataByteSize);
  155. bufferList->mBuffers[0].mData = dataBuffer;
  156. sampleRate = inputStreamDesc.mSampleRate;
  157. bitsPerSample = 16;
  158. numChannels = inputStreamDesc.mChannelsPerFrame;
  159. detachThread();
  160. ok = true;
  161. }
  162. }
  163. ~QTAudioReader()
  164. {
  165. JUCE_AUTORELEASEPOOL
  166. {
  167. checkThreadIsAttached();
  168. if (dataHandle != nullptr)
  169. DisposeHandle (dataHandle);
  170. if (extractor != nullptr)
  171. {
  172. MovieAudioExtractionEnd (extractor);
  173. extractor = nullptr;
  174. }
  175. DisposeMovie (movie);
  176. #if JUCE_MAC
  177. ExitMoviesOnThread();
  178. #endif
  179. }
  180. }
  181. bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer,
  182. int64 startSampleInFile, int numSamples)
  183. {
  184. JUCE_AUTORELEASEPOOL
  185. {
  186. checkThreadIsAttached();
  187. bool readOk = true;
  188. while (numSamples > 0)
  189. {
  190. if (lastSampleRead != startSampleInFile)
  191. {
  192. TimeRecord time;
  193. time.scale = (TimeScale) inputStreamDesc.mSampleRate;
  194. time.base = 0;
  195. time.value.hi = 0;
  196. time.value.lo = (UInt32) startSampleInFile;
  197. OSStatus err = MovieAudioExtractionSetProperty (extractor,
  198. kQTPropertyClass_MovieAudioExtraction_Movie,
  199. kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
  200. sizeof (time), &time);
  201. if (err != noErr)
  202. {
  203. readOk = false;
  204. break;
  205. }
  206. }
  207. int framesToDo = jmin (numSamples, (int) (bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame));
  208. bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * (UInt32) framesToDo;
  209. UInt32 outFlags = 0;
  210. UInt32 actualNumFrames = (UInt32) framesToDo;
  211. OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumFrames, bufferList, &outFlags);
  212. if (err != noErr)
  213. {
  214. readOk = false;
  215. break;
  216. }
  217. lastSampleRead = startSampleInFile + actualNumFrames;
  218. const int samplesReceived = (int) actualNumFrames;
  219. for (int j = numDestChannels; --j >= 0;)
  220. {
  221. if (destSamples[j] != nullptr)
  222. {
  223. const short* src = ((const short*) bufferList->mBuffers[0].mData) + j;
  224. for (int i = 0; i < samplesReceived; ++i)
  225. {
  226. destSamples[j][startOffsetInDestBuffer + i] = (*src << 16);
  227. src += numChannels;
  228. }
  229. }
  230. }
  231. startOffsetInDestBuffer += samplesReceived;
  232. startSampleInFile += samplesReceived;
  233. numSamples -= samplesReceived;
  234. if (((outFlags & kQTMovieAudioExtractionComplete) != 0 || samplesReceived == 0) && numSamples > 0)
  235. {
  236. for (int j = numDestChannels; --j >= 0;)
  237. if (destSamples[j] != nullptr)
  238. zeromem (destSamples[j] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples);
  239. break;
  240. }
  241. }
  242. detachThread();
  243. return readOk;
  244. }
  245. }
  246. bool ok;
  247. private:
  248. Movie movie;
  249. Media media;
  250. Track track;
  251. const int trackNum;
  252. double trackUnitsPerFrame;
  253. int samplesPerFrame;
  254. int64 lastSampleRead;
  255. Thread::ThreadID lastThreadId;
  256. MovieAudioExtractionRef extractor;
  257. AudioStreamBasicDescription inputStreamDesc;
  258. HeapBlock<AudioBufferList> bufferList;
  259. HeapBlock<char> dataBuffer;
  260. Handle dataHandle;
  261. //==============================================================================
  262. void checkThreadIsAttached()
  263. {
  264. #if JUCE_MAC
  265. if (Thread::getCurrentThreadId() != lastThreadId)
  266. EnterMoviesOnThread (0);
  267. AttachMovieToCurrentThread (movie);
  268. #endif
  269. }
  270. void detachThread()
  271. {
  272. #if JUCE_MAC
  273. DetachMovieFromCurrentThread (movie);
  274. #endif
  275. }
  276. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (QTAudioReader)
  277. };
  278. //==============================================================================
  279. QuickTimeAudioFormat::QuickTimeAudioFormat() : AudioFormat (quickTimeFormatName, ".mov .mp3 .mp4 .m4a")
  280. {
  281. }
  282. QuickTimeAudioFormat::~QuickTimeAudioFormat()
  283. {
  284. }
  285. Array<int> QuickTimeAudioFormat::getPossibleSampleRates() { return Array<int>(); }
  286. Array<int> QuickTimeAudioFormat::getPossibleBitDepths() { return Array<int>(); }
  287. bool QuickTimeAudioFormat::canDoStereo() { return true; }
  288. bool QuickTimeAudioFormat::canDoMono() { return true; }
  289. //==============================================================================
  290. AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream,
  291. const bool deleteStreamIfOpeningFails)
  292. {
  293. ScopedPointer<QTAudioReader> r (new QTAudioReader (sourceStream, 0));
  294. if (r->ok)
  295. return r.release();
  296. if (! deleteStreamIfOpeningFails)
  297. r->input = 0;
  298. return nullptr;
  299. }
  300. AudioFormatWriter* QuickTimeAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/,
  301. double /*sampleRateToUse*/,
  302. unsigned int /*numberOfChannels*/,
  303. int /*bitsPerSample*/,
  304. const StringPairArray& /*metadataValues*/,
  305. int /*qualityOptionIndex*/)
  306. {
  307. jassertfalse; // not yet implemented!
  308. return nullptr;
  309. }
  310. #endif