|
|
|
@@ -1,412 +1,412 @@ |
|
|
|
/*
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
|
|
|
Copyright 2004-7 by Raw Material Software ltd.
|
|
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the
|
|
|
|
GNU General Public License, as published by the Free Software Foundation;
|
|
|
|
either version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
JUCE is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with JUCE; if not, visit www.gnu.org/licenses or write to the
|
|
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
|
|
|
|
Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
If you'd like to release a closed-source product which uses JUCE, commercial
|
|
|
|
licenses are also available: visit www.rawmaterialsoftware.com/juce for
|
|
|
|
more information.
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "../../../../juce_Config.h"
|
|
|
|
|
|
|
|
#if JUCE_QUICKTIME
|
|
|
|
|
|
|
|
#if ! defined (_WIN32)
|
|
|
|
#include <Quicktime/Movies.h>
|
|
|
|
#include <Quicktime/QTML.h>
|
|
|
|
#include <Quicktime/QuickTimeComponents.h>
|
|
|
|
#include <Quicktime/MediaHandlers.h>
|
|
|
|
#include <Quicktime/ImageCodec.h>
|
|
|
|
#else
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning (push)
|
|
|
|
#pragma warning (disable : 4100)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* If you've got an include error here, you probably need to install the QuickTime SDK and
|
|
|
|
add its header directory to your include path.
|
|
|
|
|
|
|
|
Alternatively, if you don't need any QuickTime services, just turn off the JUC_QUICKTIME
|
|
|
|
flag in juce_Config.h
|
|
|
|
*/
|
|
|
|
#include <Movies.h>
|
|
|
|
#include <QTML.h>
|
|
|
|
#include <QuickTimeComponents.h>
|
|
|
|
#include <MediaHandlers.h>
|
|
|
|
#include <ImageCodec.h>
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning (pop)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "../../../juce_core/basics/juce_StandardHeader.h"
|
|
|
|
|
|
|
|
BEGIN_JUCE_NAMESPACE
|
|
|
|
|
|
|
|
#include "juce_QuickTimeAudioFormat.h"
|
|
|
|
#include "../../../juce_core/text/juce_LocalisedStrings.h"
|
|
|
|
#include "../../../juce_core/threads/juce_Thread.h"
|
|
|
|
#include "../../../juce_core/io/network/juce_URL.h"
|
|
|
|
|
|
|
|
bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle);
|
|
|
|
|
|
|
|
#define qtFormatName TRANS("QuickTime file")
|
|
|
|
static const tchar* const extensions[] = { T(".mov"), T(".mp3"), 0 };
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
class QTAudioReader : public AudioFormatReader
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QTAudioReader (InputStream* const input_, const int trackNum_)
|
|
|
|
: AudioFormatReader (input_, qtFormatName),
|
|
|
|
ok (false),
|
|
|
|
movie (0),
|
|
|
|
trackNum (trackNum_),
|
|
|
|
extractor (0),
|
|
|
|
lastSampleRead (0),
|
|
|
|
lastThreadId (0),
|
|
|
|
dataHandle (0)
|
|
|
|
{
|
|
|
|
bufferList = (AudioBufferList*) juce_calloc (256);
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
if (InitializeQTML (0) != noErr)
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
if (EnterMovies() != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#if JUCE_MAC
|
|
|
|
EnterMoviesOnThread (0);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle);
|
|
|
|
|
|
|
|
if (! opened)
|
|
|
|
return;
|
|
|
|
|
|
|
|
{
|
|
|
|
const int numTracks = GetMovieTrackCount (movie);
|
|
|
|
int trackCount = 0;
|
|
|
|
|
|
|
|
for (int i = 1; i <= numTracks; ++i)
|
|
|
|
{
|
|
|
|
track = GetMovieIndTrack (movie, i);
|
|
|
|
media = GetTrackMedia (track);
|
|
|
|
|
|
|
|
OSType mediaType;
|
|
|
|
GetMediaHandlerDescription (media, &mediaType, 0, 0);
|
|
|
|
|
|
|
|
if (mediaType == SoundMediaType
|
|
|
|
&& trackCount++ == trackNum_)
|
|
|
|
{
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! ok)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ok = false;
|
|
|
|
|
|
|
|
lengthInSamples = GetMediaDecodeDuration (media);
|
|
|
|
usesFloatingPointData = false;
|
|
|
|
|
|
|
|
samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media));
|
|
|
|
|
|
|
|
trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame
|
|
|
|
/ GetMediaTimeScale (media);
|
|
|
|
|
|
|
|
OSStatus err = MovieAudioExtractionBegin (movie, 0, &extractor);
|
|
|
|
|
|
|
|
unsigned long output_layout_size;
|
|
|
|
err = MovieAudioExtractionGetPropertyInfo (extractor,
|
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio,
|
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
|
|
|
|
0, &output_layout_size, 0);
|
|
|
|
if (err != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
AudioChannelLayout* const qt_audio_channel_layout
|
|
|
|
= (AudioChannelLayout*) juce_calloc (output_layout_size);
|
|
|
|
|
|
|
|
err = MovieAudioExtractionGetProperty (extractor,
|
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio,
|
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
|
|
|
|
output_layout_size, qt_audio_channel_layout, 0);
|
|
|
|
|
|
|
|
qt_audio_channel_layout->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
|
|
|
|
|
|
|
|
err = MovieAudioExtractionSetProperty (extractor,
|
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio,
|
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout,
|
|
|
|
sizeof (qt_audio_channel_layout),
|
|
|
|
qt_audio_channel_layout);
|
|
|
|
|
|
|
|
juce_free (qt_audio_channel_layout);
|
|
|
|
|
|
|
|
err = MovieAudioExtractionGetProperty (extractor,
|
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio,
|
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
|
|
|
|
sizeof (inputStreamDesc),
|
|
|
|
&inputStreamDesc, 0);
|
|
|
|
if (err != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger
|
|
|
|
| kAudioFormatFlagIsPacked
|
|
|
|
| kAudioFormatFlagsNativeEndian;
|
|
|
|
inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8;
|
|
|
|
inputStreamDesc.mChannelsPerFrame = jmin (2, inputStreamDesc.mChannelsPerFrame);
|
|
|
|
inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame;
|
|
|
|
inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame;
|
|
|
|
|
|
|
|
err = MovieAudioExtractionSetProperty (extractor,
|
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio,
|
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
|
|
|
|
sizeof (inputStreamDesc),
|
|
|
|
&inputStreamDesc);
|
|
|
|
if (err != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Boolean allChannelsDiscrete = false;
|
|
|
|
err = MovieAudioExtractionSetProperty (extractor,
|
|
|
|
kQTPropertyClass_MovieAudioExtraction_Movie,
|
|
|
|
kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
|
|
|
|
sizeof (allChannelsDiscrete),
|
|
|
|
&allChannelsDiscrete);
|
|
|
|
|
|
|
|
if (err != noErr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bufferList->mNumberBuffers = 1;
|
|
|
|
bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame;
|
|
|
|
bufferList->mBuffers[0].mDataByteSize = (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16;
|
|
|
|
bufferList->mBuffers[0].mData = malloc (bufferList->mBuffers[0].mDataByteSize);
|
|
|
|
|
|
|
|
sampleRate = inputStreamDesc.mSampleRate;
|
|
|
|
bitsPerSample = 16;
|
|
|
|
numChannels = inputStreamDesc.mChannelsPerFrame;
|
|
|
|
|
|
|
|
detachThread();
|
|
|
|
ok = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
~QTAudioReader()
|
|
|
|
{
|
|
|
|
if (dataHandle != 0)
|
|
|
|
DisposeHandle (dataHandle);
|
|
|
|
|
|
|
|
if (extractor != 0)
|
|
|
|
{
|
|
|
|
MovieAudioExtractionEnd (extractor);
|
|
|
|
extractor = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
checkThreadIsAttached();
|
|
|
|
DisposeMovie (movie);
|
|
|
|
|
|
|
|
juce_free (bufferList->mBuffers[0].mData);
|
|
|
|
juce_free (bufferList);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool read (int** destSamples,
|
|
|
|
int64 startSample,
|
|
|
|
int numSamples)
|
|
|
|
{
|
|
|
|
checkThreadIsAttached();
|
|
|
|
int done = 0;
|
|
|
|
|
|
|
|
while (numSamples > 0)
|
|
|
|
{
|
|
|
|
if (! loadFrame ((int) startSample))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const int numToDo = jmin (numSamples, samplesPerFrame);
|
|
|
|
|
|
|
|
for (unsigned int j = 0; j < inputStreamDesc.mChannelsPerFrame; ++j)
|
|
|
|
{
|
|
|
|
if (destSamples[j] != 0)
|
|
|
|
{
|
|
|
|
const short* const src = ((const short*) bufferList->mBuffers[0].mData) + j;
|
|
|
|
|
|
|
|
for (int i = 0; i < numToDo; ++i)
|
|
|
|
destSamples[j][done + i] = src [i << 1] << 16;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done += numToDo;
|
|
|
|
startSample += numToDo;
|
|
|
|
numSamples -= numToDo;
|
|
|
|
}
|
|
|
|
|
|
|
|
detachThread();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool loadFrame (const int sampleNum)
|
|
|
|
{
|
|
|
|
if (lastSampleRead != sampleNum)
|
|
|
|
{
|
|
|
|
TimeRecord time;
|
|
|
|
time.scale = (TimeScale) inputStreamDesc.mSampleRate;
|
|
|
|
time.base = 0;
|
|
|
|
time.value.hi = 0;
|
|
|
|
time.value.lo = (UInt32) sampleNum;
|
|
|
|
|
|
|
|
OSStatus err = MovieAudioExtractionSetProperty (extractor,
|
|
|
|
kQTPropertyClass_MovieAudioExtraction_Movie,
|
|
|
|
kQTMovieAudioExtractionMoviePropertyID_CurrentTime,
|
|
|
|
sizeof (time), &time);
|
|
|
|
|
|
|
|
if (err != noErr)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * samplesPerFrame;
|
|
|
|
|
|
|
|
UInt32 outFlags = 0;
|
|
|
|
UInt32 actualNumSamples = samplesPerFrame;
|
|
|
|
OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumSamples,
|
|
|
|
bufferList, &outFlags);
|
|
|
|
|
|
|
|
lastSampleRead = sampleNum + samplesPerFrame;
|
|
|
|
|
|
|
|
return err == noErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
juce_UseDebuggingNewOperator
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Movie movie;
|
|
|
|
Media media;
|
|
|
|
Track track;
|
|
|
|
const int trackNum;
|
|
|
|
double trackUnitsPerFrame;
|
|
|
|
int samplesPerFrame;
|
|
|
|
int lastSampleRead, lastThreadId;
|
|
|
|
MovieAudioExtractionRef extractor;
|
|
|
|
AudioStreamBasicDescription inputStreamDesc;
|
|
|
|
AudioBufferList* bufferList;
|
|
|
|
Handle dataHandle;
|
|
|
|
|
|
|
|
/*OSErr readMovieStream (long offset, long size, void* dataPtr)
|
|
|
|
{
|
|
|
|
input->setPosition (offset);
|
|
|
|
input->read (dataPtr, size);
|
|
|
|
return noErr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static OSErr readMovieStreamProc (long offset, long size, void* dataPtr, void* userRef)
|
|
|
|
{
|
|
|
|
return ((QTAudioReader*) userRef)->readMovieStream (offset, size, dataPtr);
|
|
|
|
}*/
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
void checkThreadIsAttached()
|
|
|
|
{
|
|
|
|
#if JUCE_MAC
|
|
|
|
if (Thread::getCurrentThreadId() != lastThreadId)
|
|
|
|
EnterMoviesOnThread (0);
|
|
|
|
AttachMovieToCurrentThread (movie);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void detachThread()
|
|
|
|
{
|
|
|
|
#if JUCE_MAC
|
|
|
|
DetachMovieFromCurrentThread (movie);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
QuickTimeAudioFormat::QuickTimeAudioFormat()
|
|
|
|
: AudioFormat (qtFormatName, (const tchar**) extensions)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QuickTimeAudioFormat::~QuickTimeAudioFormat()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
const Array <int> QuickTimeAudioFormat::getPossibleSampleRates()
|
|
|
|
{
|
|
|
|
return Array<int>();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Array <int> QuickTimeAudioFormat::getPossibleBitDepths()
|
|
|
|
{
|
|
|
|
return Array<int>();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QuickTimeAudioFormat::canDoStereo()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QuickTimeAudioFormat::canDoMono()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==============================================================================
|
|
|
|
AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream,
|
|
|
|
const bool deleteStreamIfOpeningFails)
|
|
|
|
{
|
|
|
|
QTAudioReader* r = new QTAudioReader (sourceStream, 0);
|
|
|
|
|
|
|
|
if (! r->ok)
|
|
|
|
{
|
|
|
|
if (! deleteStreamIfOpeningFails)
|
|
|
|
r->input = 0;
|
|
|
|
|
|
|
|
deleteAndZero (r);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioFormatWriter* QuickTimeAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/,
|
|
|
|
double /*sampleRateToUse*/,
|
|
|
|
unsigned int /*numberOfChannels*/,
|
|
|
|
int /*bitsPerSample*/,
|
|
|
|
const StringPairArray& /*metadataValues*/,
|
|
|
|
int /*qualityOptionIndex*/)
|
|
|
|
{
|
|
|
|
jassertfalse // not yet implemented!
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
END_JUCE_NAMESPACE
|
|
|
|
|
|
|
|
#endif
|
|
|
|
/* |
|
|
|
============================================================================== |
|
|
|
|
|
|
|
This file is part of the JUCE library - "Jules' Utility Class Extensions" |
|
|
|
Copyright 2004-7 by Raw Material Software ltd. |
|
|
|
|
|
|
|
------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
JUCE can be redistributed and/or modified under the terms of the |
|
|
|
GNU General Public License, as published by the Free Software Foundation; |
|
|
|
either version 2 of the License, or (at your option) any later version. |
|
|
|
|
|
|
|
JUCE is distributed in the hope that it will be useful, |
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
|
GNU General Public License for more details. |
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License |
|
|
|
along with JUCE; if not, visit www.gnu.org/licenses or write to the |
|
|
|
Free Software Foundation, Inc., 59 Temple Place, Suite 330, |
|
|
|
Boston, MA 02111-1307 USA |
|
|
|
|
|
|
|
------------------------------------------------------------------------------ |
|
|
|
|
|
|
|
If you'd like to release a closed-source product which uses JUCE, commercial |
|
|
|
licenses are also available: visit www.rawmaterialsoftware.com/juce for |
|
|
|
more information. |
|
|
|
|
|
|
|
============================================================================== |
|
|
|
*/ |
|
|
|
|
|
|
|
#include "../../../../juce_Config.h" |
|
|
|
|
|
|
|
#if JUCE_QUICKTIME |
|
|
|
|
|
|
|
#if ! defined (_WIN32) |
|
|
|
#include <Quicktime/Movies.h> |
|
|
|
#include <Quicktime/QTML.h> |
|
|
|
#include <Quicktime/QuickTimeComponents.h> |
|
|
|
#include <Quicktime/MediaHandlers.h> |
|
|
|
#include <Quicktime/ImageCodec.h> |
|
|
|
#else |
|
|
|
#ifdef _MSC_VER |
|
|
|
#pragma warning (push) |
|
|
|
#pragma warning (disable : 4100) |
|
|
|
#endif |
|
|
|
|
|
|
|
/* If you've got an include error here, you probably need to install the QuickTime SDK and |
|
|
|
add its header directory to your include path. |
|
|
|
|
|
|
|
Alternatively, if you don't need any QuickTime services, just turn off the JUC_QUICKTIME |
|
|
|
flag in juce_Config.h |
|
|
|
*/ |
|
|
|
#include <Movies.h> |
|
|
|
#include <QTML.h> |
|
|
|
#include <QuickTimeComponents.h> |
|
|
|
#include <MediaHandlers.h> |
|
|
|
#include <ImageCodec.h> |
|
|
|
|
|
|
|
#ifdef _MSC_VER |
|
|
|
#pragma warning (pop) |
|
|
|
#endif |
|
|
|
#endif |
|
|
|
|
|
|
|
#include "../../../juce_core/basics/juce_StandardHeader.h" |
|
|
|
|
|
|
|
BEGIN_JUCE_NAMESPACE |
|
|
|
|
|
|
|
#include "juce_QuickTimeAudioFormat.h" |
|
|
|
#include "../../../juce_core/text/juce_LocalisedStrings.h" |
|
|
|
#include "../../../juce_core/threads/juce_Thread.h" |
|
|
|
#include "../../../juce_core/io/network/juce_URL.h" |
|
|
|
|
|
|
|
bool juce_OpenQuickTimeMovieFromStream (InputStream* input, Movie& movie, Handle& dataHandle); |
|
|
|
|
|
|
|
#define qtFormatName TRANS("QuickTime file") |
|
|
|
static const tchar* const extensions[] = { T(".mov"), T(".mp3"), T(".mp4"), 0 }; |
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
class QTAudioReader : public AudioFormatReader |
|
|
|
{ |
|
|
|
public: |
|
|
|
QTAudioReader (InputStream* const input_, const int trackNum_) |
|
|
|
: AudioFormatReader (input_, qtFormatName), |
|
|
|
ok (false), |
|
|
|
movie (0), |
|
|
|
trackNum (trackNum_), |
|
|
|
extractor (0), |
|
|
|
lastSampleRead (0), |
|
|
|
lastThreadId (0), |
|
|
|
dataHandle (0) |
|
|
|
{ |
|
|
|
bufferList = (AudioBufferList*) juce_calloc (256); |
|
|
|
|
|
|
|
#ifdef WIN32 |
|
|
|
if (InitializeQTML (0) != noErr) |
|
|
|
return; |
|
|
|
#endif |
|
|
|
if (EnterMovies() != noErr) |
|
|
|
return; |
|
|
|
|
|
|
|
#if JUCE_MAC |
|
|
|
EnterMoviesOnThread (0); |
|
|
|
#endif |
|
|
|
|
|
|
|
bool opened = juce_OpenQuickTimeMovieFromStream (input_, movie, dataHandle); |
|
|
|
|
|
|
|
if (! opened) |
|
|
|
return; |
|
|
|
|
|
|
|
{ |
|
|
|
const int numTracks = GetMovieTrackCount (movie); |
|
|
|
int trackCount = 0; |
|
|
|
|
|
|
|
for (int i = 1; i <= numTracks; ++i) |
|
|
|
{ |
|
|
|
track = GetMovieIndTrack (movie, i); |
|
|
|
media = GetTrackMedia (track); |
|
|
|
|
|
|
|
OSType mediaType; |
|
|
|
GetMediaHandlerDescription (media, &mediaType, 0, 0); |
|
|
|
|
|
|
|
if (mediaType == SoundMediaType |
|
|
|
&& trackCount++ == trackNum_) |
|
|
|
{ |
|
|
|
ok = true; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (! ok) |
|
|
|
return; |
|
|
|
|
|
|
|
ok = false; |
|
|
|
|
|
|
|
lengthInSamples = GetMediaDecodeDuration (media); |
|
|
|
usesFloatingPointData = false; |
|
|
|
|
|
|
|
samplesPerFrame = (int) (GetMediaDecodeDuration (media) / GetMediaSampleCount (media)); |
|
|
|
|
|
|
|
trackUnitsPerFrame = GetMovieTimeScale (movie) * samplesPerFrame |
|
|
|
/ GetMediaTimeScale (media); |
|
|
|
|
|
|
|
OSStatus err = MovieAudioExtractionBegin (movie, 0, &extractor); |
|
|
|
|
|
|
|
unsigned long output_layout_size; |
|
|
|
err = MovieAudioExtractionGetPropertyInfo (extractor, |
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio, |
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, |
|
|
|
0, &output_layout_size, 0); |
|
|
|
if (err != noErr) |
|
|
|
return; |
|
|
|
|
|
|
|
AudioChannelLayout* const qt_audio_channel_layout |
|
|
|
= (AudioChannelLayout*) juce_calloc (output_layout_size); |
|
|
|
|
|
|
|
err = MovieAudioExtractionGetProperty (extractor, |
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio, |
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, |
|
|
|
output_layout_size, qt_audio_channel_layout, 0); |
|
|
|
|
|
|
|
qt_audio_channel_layout->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; |
|
|
|
|
|
|
|
err = MovieAudioExtractionSetProperty (extractor, |
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio, |
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioChannelLayout, |
|
|
|
sizeof (qt_audio_channel_layout), |
|
|
|
qt_audio_channel_layout); |
|
|
|
|
|
|
|
juce_free (qt_audio_channel_layout); |
|
|
|
|
|
|
|
err = MovieAudioExtractionGetProperty (extractor, |
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio, |
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, |
|
|
|
sizeof (inputStreamDesc), |
|
|
|
&inputStreamDesc, 0); |
|
|
|
if (err != noErr) |
|
|
|
return; |
|
|
|
|
|
|
|
inputStreamDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger |
|
|
|
| kAudioFormatFlagIsPacked |
|
|
|
| kAudioFormatFlagsNativeEndian; |
|
|
|
inputStreamDesc.mBitsPerChannel = sizeof (SInt16) * 8; |
|
|
|
inputStreamDesc.mChannelsPerFrame = jmin (2, inputStreamDesc.mChannelsPerFrame); |
|
|
|
inputStreamDesc.mBytesPerFrame = sizeof (SInt16) * inputStreamDesc.mChannelsPerFrame; |
|
|
|
inputStreamDesc.mBytesPerPacket = inputStreamDesc.mBytesPerFrame; |
|
|
|
|
|
|
|
err = MovieAudioExtractionSetProperty (extractor, |
|
|
|
kQTPropertyClass_MovieAudioExtraction_Audio, |
|
|
|
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription, |
|
|
|
sizeof (inputStreamDesc), |
|
|
|
&inputStreamDesc); |
|
|
|
if (err != noErr) |
|
|
|
return; |
|
|
|
|
|
|
|
Boolean allChannelsDiscrete = false; |
|
|
|
err = MovieAudioExtractionSetProperty (extractor, |
|
|
|
kQTPropertyClass_MovieAudioExtraction_Movie, |
|
|
|
kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete, |
|
|
|
sizeof (allChannelsDiscrete), |
|
|
|
&allChannelsDiscrete); |
|
|
|
|
|
|
|
if (err != noErr) |
|
|
|
return; |
|
|
|
|
|
|
|
bufferList->mNumberBuffers = 1; |
|
|
|
bufferList->mBuffers[0].mNumberChannels = inputStreamDesc.mChannelsPerFrame; |
|
|
|
bufferList->mBuffers[0].mDataByteSize = (UInt32) (samplesPerFrame * inputStreamDesc.mBytesPerFrame) + 16; |
|
|
|
bufferList->mBuffers[0].mData = malloc (bufferList->mBuffers[0].mDataByteSize); |
|
|
|
|
|
|
|
sampleRate = inputStreamDesc.mSampleRate; |
|
|
|
bitsPerSample = 16; |
|
|
|
numChannels = inputStreamDesc.mChannelsPerFrame; |
|
|
|
|
|
|
|
detachThread(); |
|
|
|
ok = true; |
|
|
|
} |
|
|
|
|
|
|
|
~QTAudioReader() |
|
|
|
{ |
|
|
|
if (dataHandle != 0) |
|
|
|
DisposeHandle (dataHandle); |
|
|
|
|
|
|
|
if (extractor != 0) |
|
|
|
{ |
|
|
|
MovieAudioExtractionEnd (extractor); |
|
|
|
extractor = 0; |
|
|
|
} |
|
|
|
|
|
|
|
checkThreadIsAttached(); |
|
|
|
DisposeMovie (movie); |
|
|
|
|
|
|
|
juce_free (bufferList->mBuffers[0].mData); |
|
|
|
juce_free (bufferList); |
|
|
|
} |
|
|
|
|
|
|
|
bool read (int** destSamples, |
|
|
|
int64 startSample, |
|
|
|
int numSamples) |
|
|
|
{ |
|
|
|
checkThreadIsAttached(); |
|
|
|
int done = 0; |
|
|
|
|
|
|
|
while (numSamples > 0) |
|
|
|
{ |
|
|
|
if (! loadFrame ((int) startSample)) |
|
|
|
return false; |
|
|
|
|
|
|
|
const int numToDo = jmin (numSamples, samplesPerFrame); |
|
|
|
|
|
|
|
for (unsigned int j = 0; j < inputStreamDesc.mChannelsPerFrame; ++j) |
|
|
|
{ |
|
|
|
if (destSamples[j] != 0) |
|
|
|
{ |
|
|
|
const short* const src = ((const short*) bufferList->mBuffers[0].mData) + j; |
|
|
|
|
|
|
|
for (int i = 0; i < numToDo; ++i) |
|
|
|
destSamples[j][done + i] = src [i << 1] << 16; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
done += numToDo; |
|
|
|
startSample += numToDo; |
|
|
|
numSamples -= numToDo; |
|
|
|
} |
|
|
|
|
|
|
|
detachThread(); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool loadFrame (const int sampleNum) |
|
|
|
{ |
|
|
|
if (lastSampleRead != sampleNum) |
|
|
|
{ |
|
|
|
TimeRecord time; |
|
|
|
time.scale = (TimeScale) inputStreamDesc.mSampleRate; |
|
|
|
time.base = 0; |
|
|
|
time.value.hi = 0; |
|
|
|
time.value.lo = (UInt32) sampleNum; |
|
|
|
|
|
|
|
OSStatus err = MovieAudioExtractionSetProperty (extractor, |
|
|
|
kQTPropertyClass_MovieAudioExtraction_Movie, |
|
|
|
kQTMovieAudioExtractionMoviePropertyID_CurrentTime, |
|
|
|
sizeof (time), &time); |
|
|
|
|
|
|
|
if (err != noErr) |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * samplesPerFrame; |
|
|
|
|
|
|
|
UInt32 outFlags = 0; |
|
|
|
UInt32 actualNumSamples = samplesPerFrame; |
|
|
|
OSStatus err = MovieAudioExtractionFillBuffer (extractor, &actualNumSamples, |
|
|
|
bufferList, &outFlags); |
|
|
|
|
|
|
|
lastSampleRead = sampleNum + samplesPerFrame; |
|
|
|
|
|
|
|
return err == noErr; |
|
|
|
} |
|
|
|
|
|
|
|
juce_UseDebuggingNewOperator |
|
|
|
|
|
|
|
bool ok; |
|
|
|
|
|
|
|
private: |
|
|
|
Movie movie; |
|
|
|
Media media; |
|
|
|
Track track; |
|
|
|
const int trackNum; |
|
|
|
double trackUnitsPerFrame; |
|
|
|
int samplesPerFrame; |
|
|
|
int lastSampleRead, lastThreadId; |
|
|
|
MovieAudioExtractionRef extractor; |
|
|
|
AudioStreamBasicDescription inputStreamDesc; |
|
|
|
AudioBufferList* bufferList; |
|
|
|
Handle dataHandle; |
|
|
|
|
|
|
|
/*OSErr readMovieStream (long offset, long size, void* dataPtr) |
|
|
|
{ |
|
|
|
input->setPosition (offset); |
|
|
|
input->read (dataPtr, size); |
|
|
|
return noErr; |
|
|
|
} |
|
|
|
|
|
|
|
static OSErr readMovieStreamProc (long offset, long size, void* dataPtr, void* userRef) |
|
|
|
{ |
|
|
|
return ((QTAudioReader*) userRef)->readMovieStream (offset, size, dataPtr); |
|
|
|
}*/ |
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
void checkThreadIsAttached() |
|
|
|
{ |
|
|
|
#if JUCE_MAC |
|
|
|
if (Thread::getCurrentThreadId() != lastThreadId) |
|
|
|
EnterMoviesOnThread (0); |
|
|
|
AttachMovieToCurrentThread (movie); |
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
void detachThread() |
|
|
|
{ |
|
|
|
#if JUCE_MAC |
|
|
|
DetachMovieFromCurrentThread (movie); |
|
|
|
#endif |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
QuickTimeAudioFormat::QuickTimeAudioFormat() |
|
|
|
: AudioFormat (qtFormatName, (const tchar**) extensions) |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
QuickTimeAudioFormat::~QuickTimeAudioFormat() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
const Array <int> QuickTimeAudioFormat::getPossibleSampleRates() |
|
|
|
{ |
|
|
|
return Array<int>(); |
|
|
|
} |
|
|
|
|
|
|
|
const Array <int> QuickTimeAudioFormat::getPossibleBitDepths() |
|
|
|
{ |
|
|
|
return Array<int>(); |
|
|
|
} |
|
|
|
|
|
|
|
bool QuickTimeAudioFormat::canDoStereo() |
|
|
|
{ |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool QuickTimeAudioFormat::canDoMono() |
|
|
|
{ |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
//============================================================================== |
|
|
|
AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream, |
|
|
|
const bool deleteStreamIfOpeningFails) |
|
|
|
{ |
|
|
|
QTAudioReader* r = new QTAudioReader (sourceStream, 0); |
|
|
|
|
|
|
|
if (! r->ok) |
|
|
|
{ |
|
|
|
if (! deleteStreamIfOpeningFails) |
|
|
|
r->input = 0; |
|
|
|
|
|
|
|
deleteAndZero (r); |
|
|
|
} |
|
|
|
|
|
|
|
return r; |
|
|
|
} |
|
|
|
|
|
|
|
AudioFormatWriter* QuickTimeAudioFormat::createWriterFor (OutputStream* /*streamToWriteTo*/, |
|
|
|
double /*sampleRateToUse*/, |
|
|
|
unsigned int /*numberOfChannels*/, |
|
|
|
int /*bitsPerSample*/, |
|
|
|
const StringPairArray& /*metadataValues*/, |
|
|
|
int /*qualityOptionIndex*/) |
|
|
|
{ |
|
|
|
jassertfalse // not yet implemented! |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
END_JUCE_NAMESPACE |
|
|
|
|
|
|
|
#endif |