Commit c8ea4a77 authored by Sam Lantinga's avatar Sam Lantinga

Added MacOS X CD-ROM audio support (thanks Max and Darrell)

--HG--
extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%40614
parent e44379a4
......@@ -2404,8 +2404,9 @@ case "$target" in
fi
# Set up files for the cdrom library
if test x$enable_cdrom = xyes; then
CDROM_SUBDIRS="$CDROM_SUBDIRS dummy"
CDROM_DRIVERS="$CDROM_DRIVERS dummy/libcdrom_dummy.la"
CDROM_SUBDIRS="$CDROM_SUBDIRS macosx"
CDROM_DRIVERS="$CDROM_DRIVERS macosx/libcdrom_macosx.la"
SYSTEM_LIBS="$SYSTEM_LIBS -framework AudioToolbox -framework AudioUnit -lstdc++"
fi
# Set up files for the thread library
if test x$enable_threads = xyes; then
......@@ -2672,6 +2673,7 @@ src/cdrom/bsdi/Makefile
src/cdrom/freebsd/Makefile
src/cdrom/linux/Makefile
src/cdrom/macos/Makefile
src/cdrom/macosx/Makefile
src/cdrom/openbsd/Makefile
src/cdrom/qnx/Makefile
src/cdrom/win32/Makefile
......
......@@ -5,7 +5,19 @@ noinst_LTLIBRARIES = libcdrom.la
# Define which subdirectories need to be built
SUBDIRS = @CDROM_SUBDIRS@
DIST_SUBDIRS = aix beos bsdi dc dummy freebsd linux macos openbsd qnx win32
DIST_SUBDIRS = \
aix \
beos \
bsdi \
dc \
dummy \
freebsd \
linux \
macos \
macosx \
openbsd \
qnx \
win32
DRIVERS = @CDROM_DRIVERS@
......
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
This file based on Apple sample code. We haven't changed the file name,
so if you want to see the original search for it on apple.com/developer
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AudioFilePlayer.cpp
//
#include "AudioFilePlayer.h"
extern const char* AudioFilePlayerErrorStr (OSStatus error)
{
const char *str;
switch (error) {
case kAudioFileUnspecifiedError: str = "wht?"; break;
case kAudioFileUnsupportedFileTypeError: str = "typ?"; break;
case kAudioFileUnsupportedDataFormatError: str = "fmt?"; break;
case kAudioFileUnsupportedPropertyError: str = "pty?"; break;
case kAudioFileBadPropertySizeError: str = "!siz"; break;
case kAudioFileNotOptimizedError: str = "optm"; break;
case kAudioFilePermissionsError: str = "prm?"; break;
case kAudioFileFormatNameUnavailableError: str = "nme?"; break;
case kAudioFileInvalidChunkError: str = "chk?"; break;
case kAudioFileDoesNotAllow64BitDataSizeError: str = "off?"; break;
default: str = "error unspecified";
}
return str;
}
void ThrowResult (OSStatus result, const char* str)
{
SDL_SetError ("Error: %s %d (%s)",
str, result, AudioFilePlayerErrorStr(result));
throw result;
}
#if DEBUG
void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
{
if (!inDesc) {
printf ("Can't print a NULL desc!\n");
return;
}
printf ("- - - - - - - - - - - - - - - - - - - -\n");
printf (" Sample Rate:%f\n", inDesc->mSampleRate);
printf (" Format ID:%s\n", (char*)&inDesc->mFormatID);
printf (" Format Flags:%lX\n", inDesc->mFormatFlags);
printf (" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
printf (" Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
printf (" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
printf (" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
printf (" Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
printf ("- - - - - - - - - - - - - - - - - - - -\n");
}
#endif
OSStatus AudioFileManager::FileInputProc (void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
AudioBuffer *ioData)
{
AudioFileManager* THIS = (AudioFileManager*)inRefCon;
return THIS->Render(*ioData);
}
OSStatus AudioFileManager::Render (AudioBuffer &ioData)
{
OSStatus result = AudioConverterFillBuffer(mParentConverter,
AudioFileManager::ACInputProc,
this,
&ioData.mDataByteSize,
ioData.mData);
if (result) {
SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
mParent.DoNotification (result);
} else {
mByteCounter += ioData.mDataByteSize / 2;
AfterRender();
}
return result;
}
OSStatus AudioFileManager::ACInputProc (AudioConverterRef inAudioConverter,
UInt32* outDataSize,
void** outData,
void* inUserData)
{
AudioFileManager* THIS = (AudioFileManager*)inUserData;
return THIS->GetFileData(outData, outDataSize);
}
AudioFileManager::~AudioFileManager ()
{
if (mFileBuffer) {
free (mFileBuffer);
mFileBuffer = 0;
}
}
AudioFilePlayer::AudioFilePlayer (const FSRef *inFileRef)
: mConnected (false),
mAudioFileManager (0),
mConverter (0),
mNotifier (0),
mStartFrame (0)
{
SInt64 fileDataSize = 0;
OpenFile (inFileRef, fileDataSize);
// we want about a seconds worth of data for the buffer
int secsBytes = UInt32 (mFileDescription.mSampleRate * mFileDescription.mBytesPerFrame);
#if DEBUG
printf("File format:\n");
PrintStreamDesc (&mFileDescription);
#endif
//round to a 32K boundary
//if ((secsBytes & 0xFFFF8000) > (128 * 1024))
//secsBytes &= 0xFFFF8000;
//else
//secsBytes = (secsBytes + 0x7FFF) & 0xFFFF8000;
mAudioFileManager = new AudioFileReaderThread (*this,
mAudioFileID,
fileDataSize,
secsBytes);
}
// you can put a rate scalar here to play the file faster or slower
// by multiplying the same rate by the desired factor
// eg fileSampleRate * 2 -> twice as fast
// before you create the AudioConverter
void AudioFilePlayer::SetDestination (AudioUnit &inDestUnit,
int inBusNumber)
{
if (mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged
mPlayUnit = inDestUnit;
mBusNumber = inBusNumber;
OSStatus result = noErr;
if (mConverter) {
result = AudioConverterDispose (mConverter);
THROW_RESULT("AudioConverterDispose")
}
AudioStreamBasicDescription destDesc;
UInt32 size = sizeof (destDesc);
result = AudioUnitGetProperty (inDestUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
inBusNumber,
&destDesc,
&size);
THROW_RESULT("AudioUnitGetProperty")
#if DEBUG
printf("Destination format:\n");
PrintStreamDesc (&destDesc);
#endif
//we can "down" cast a component instance to a component
ComponentDescription desc;
result = GetComponentInfo ((Component)inDestUnit, &desc, 0, 0, 0);
THROW_RESULT("GetComponentInfo")
// we're going to use this to know which convert routine to call
// a v1 audio unit will have a type of 'aunt'
// a v2 audio unit will have one of several different types.
mIsAUNTUnit = (desc.componentType == kAudioUnitComponentType);
if (!mIsAUNTUnit) {
result = badComponentInstance;
THROW_RESULT("BAD COMPONENT")
}
// HACK - the AIFF files on CDs are in little endian order!
if (mFileDescription.mFormatFlags == 0xE)
mFileDescription.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
result = AudioConverterNew (&mFileDescription, &destDesc, &mConverter);
THROW_RESULT("AudioConverterNew")
/*
// if we have a mono source, we're going to copy each channel into
// the destination's channel source...
if (mFileDescription.mChannelsPerFrame == 1) {
SInt32* channelMap = new SInt32 [destDesc.mChannelsPerFrame];
for (unsigned int i = 0; i < destDesc.mChannelsPerFrame; ++i)
channelMap[i] = 0; //set first channel to all output channels
result = AudioConverterSetProperty(mConverter,
kAudioConverterChannelMap,
(sizeof(SInt32) * destDesc.mChannelsPerFrame),
channelMap);
THROW_RESULT("AudioConverterSetProperty")
delete [] channelMap;
}
*/
assert (mFileDescription.mChannelsPerFrame == 2);
#if 0
// this uses the better quality SRC
UInt32 srcID = kAudioUnitSRCAlgorithm_Polyphase;
result = AudioConverterSetProperty(mConverter,
kAudioConverterSampleRateConverterAlgorithm,
sizeof(srcID),
&srcID);
THROW_RESULT("AudioConverterSetProperty")
#endif
}
void AudioFilePlayer::SetStartFrame (int frame)
{
SInt64 position = frame * 2352;
mStartFrame = frame;
mAudioFileManager->SetPosition (position);
}
int AudioFilePlayer::GetCurrentFrame ()
{
return mStartFrame + (mAudioFileManager->GetByteCounter() / 2352);
}
void AudioFilePlayer::SetStopFrame (int frame)
{
SInt64 position = frame * 2352;
mAudioFileManager->SetEndOfFile (position);
}
AudioFilePlayer::~AudioFilePlayer()
{
Disconnect();
if (mAudioFileManager) {
delete mAudioFileManager;
mAudioFileManager = 0;
}
if (mAudioFileID) {
::AudioFileClose (mAudioFileID);
mAudioFileID = 0;
}
if (mConverter) {
AudioConverterDispose (mConverter);
mConverter = 0;
}
}
void AudioFilePlayer::Connect()
{
#if DEBUG
printf ("Connect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0));
#endif
if (!mConnected)
{
mAudioFileManager->Connect(mConverter);
// set the render callback for the file data to be supplied to the sound converter AU
if (mIsAUNTUnit) {
mInputCallback.inputProc = AudioFileManager::FileInputProc;
mInputCallback.inputProcRefCon = mAudioFileManager;
OSStatus result = AudioUnitSetProperty (mPlayUnit,
kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input,
mBusNumber,
&mInputCallback,
sizeof(mInputCallback));
THROW_RESULT("AudioUnitSetProperty")
}
mConnected = true;
}
}
// warning noted, now please go away ;-)
// #warning This should redirect the calling of notification code to some other thread
void AudioFilePlayer::DoNotification (OSStatus inStatus) const
{
AudioFilePlayer* THIS = const_cast<AudioFilePlayer*>(this);
if (mNotifier) {
(*mNotifier) (mRefCon, inStatus);
}
else {
SDL_SetError ("Notification posted with no notifier in place");
if (inStatus == kAudioFilePlay_FileIsFinished)
THIS->Disconnect();
else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
THIS->Disconnect();
}
}
void AudioFilePlayer::Disconnect ()
{
#if DEBUG
printf ("Disconnect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0));
#endif
if (mConnected)
{
mConnected = false;
if (mIsAUNTUnit) {
mInputCallback.inputProc = 0;
mInputCallback.inputProcRefCon = 0;
OSStatus result = AudioUnitSetProperty (mPlayUnit,
kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input,
mBusNumber,
&mInputCallback,
sizeof(mInputCallback));
if (result)
SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
}
mAudioFileManager->Disconnect();
}
}
void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize)
{
OSStatus result = AudioFileOpen (inRef, fsRdPerm, 0, &mAudioFileID);
THROW_RESULT("AudioFileOpen")
UInt32 dataSize = sizeof(AudioStreamBasicDescription);
result = AudioFileGetProperty (mAudioFileID,
kAudioFilePropertyDataFormat,
&dataSize,
&mFileDescription);
THROW_RESULT("AudioFileGetProperty")
dataSize = sizeof (SInt64);
result = AudioFileGetProperty (mAudioFileID,
kAudioFilePropertyAudioDataByteCount,
&dataSize,
&outFileDataSize);
THROW_RESULT("AudioFileGetProperty")
}
\ No newline at end of file
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
This file based on Apple sample code. We haven't changed the file name,
so if you want to see the original search for it on apple.com/developer
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AudioFilePlayer.h
//
#ifndef __AudioFilePlayer_H__
#define __AudioFilePlayer_H__
#include <CoreServices/CoreServices.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AudioUnit.h>
#include "SDL_Error.h"
const char* AudioFilePlayerErrorStr (OSStatus error);
void ThrowResult (OSStatus result, const char *str);
#define THROW_RESULT(str) \
if (result) { \
ThrowResult (result, str); \
}
typedef void (*AudioFilePlayNotifier)(void *inRefCon,
OSStatus inStatus);
enum {
kAudioFilePlayErr_FilePlayUnderrun = -10000,
kAudioFilePlay_FileIsFinished = -10001,
kAudioFilePlay_PlayerIsUninitialized = -10002
};
class AudioFileManager;
#pragma mark __________ AudioFilePlayer
class AudioFilePlayer
{
public:
AudioFilePlayer (const FSRef *inFileRef);
~AudioFilePlayer();
void SetDestination (AudioUnit &inDestUnit,
int inBusNumber);
void SetNotifier (AudioFilePlayNotifier inNotifier, void *inRefCon)
{
mNotifier = inNotifier;
mRefCon = inRefCon;
}
void SetStartFrame (int frame); // seek in the file
int GetCurrentFrame (); // get the current frame position
void SetStopFrame (int frame); // set limit in the file
void Connect();
void Disconnect();
void DoNotification (OSStatus inError) const;
bool IsConnected () const { return mConnected; }
UInt32 GetBusNumber () const { return mBusNumber; }
AudioUnit GetDestUnit () const { return mPlayUnit; }
AudioConverterRef GetAudioConverter() const { return mConverter; }
#if DEBUG
void Print() const
{
CAShow (mAudioFileID);
printf ("Destination Bus:%ld\n", GetBusNumber());
printf ("Is 'aunt' unit:%s\n", (mIsAUNTUnit ? "true" : "false"));
printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
if (mConverter) CAShow (mConverter);
printf ("- - - - - - - - - - - - - - \n");
}
#endif
const AudioStreamBasicDescription& GetFileFormat() const { return mFileDescription; }
private:
AudioUnit mPlayUnit;
UInt32 mBusNumber;
AudioFileID mAudioFileID;
AudioUnitInputCallback mInputCallback;
AudioStreamBasicDescription mFileDescription;
bool mConnected;
bool mIsAUNTUnit;
AudioFileManager* mAudioFileManager;
AudioConverterRef mConverter;
AudioFilePlayNotifier mNotifier;
void* mRefCon;
int mStartFrame;
#pragma mark __________ Private_Methods
void OpenFile (const FSRef *inRef, SInt64& outFileSize);
};
#pragma mark __________ AudioFileManager
class AudioFileManager
{
public:
AudioFileManager (AudioFilePlayer& inParent, AudioFileID inFile)
: mParent (inParent),
mAudioFileID (inFile),
mFileBuffer (0),
mByteCounter (0)
{}
virtual ~AudioFileManager();
void Connect (AudioConverterRef inConverter)
{
mParentConverter = inConverter;
DoConnect();
}
// this method should NOT be called by an object of this class
// as it is called by the parent's Disconnect() method
virtual void Disconnect () {}
const AudioFileID& GetFileID() const { return mAudioFileID; }
const char* GetFileBuffer () { return mFileBuffer; }
const AudioFilePlayer& GetParent () const { return mParent; }
virtual void SetPosition (SInt64 pos) = 0; // seek/rewind in the file
virtual int GetByteCounter () { return mByteCounter; } // return actual bytes streamed to audio hardware
virtual void SetEndOfFile (SInt64 pos) = 0; // set the "EOF" (will behave just like it reached eof)
protected:
AudioFilePlayer& mParent;
AudioConverterRef mParentConverter;
const AudioFileID mAudioFileID;
char* mFileBuffer;
OSStatus Render (AudioBuffer &ioData);
int mByteCounter;
virtual OSStatus GetFileData (void** inOutData, UInt32 *inOutDataSize) = 0;
virtual void DoConnect () = 0;
virtual void AfterRender () = 0;
public:
static OSStatus FileInputProc (void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
AudioBuffer *ioData);
static OSStatus ACInputProc (AudioConverterRef inAudioConverter,
UInt32* outDataSize,
void** outData,
void* inUserData);
};
#pragma mark __________ AudioFileReaderThread
class AudioFileReaderThread
: public AudioFileManager
{
public:
const UInt32 mChunkSize;
SInt64 mFileLength;
SInt64 mReadFilePosition;
bool mWriteToFirstBuffer;
bool mFinishedReadingData;
AudioFileReaderThread (AudioFilePlayer &inParent,
AudioFileID &inFile,
SInt64 inFileLength,
UInt32 inChunkSize);
virtual void Disconnect ();
virtual void SetPosition (SInt64 pos); // seek/rewind in the file
virtual void SetEndOfFile (SInt64 pos); // set the "EOF" (will behave just like it reached eof)
protected:
virtual void DoConnect ();
virtual OSStatus GetFileData (void** inOutData, UInt32 *inOutDataSize);
virtual void AfterRender ();
private:
bool mReadFromFirstBuffer;
bool mLockUnsuccessful;
bool mIsEngaged;
int mNumTimesAskedSinceFinished;
};
#endif
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
This file based on Apple sample code. We haven't changed the file name,
so if you want to see the original search for it on apple.com/developer
*/
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// AudioFileReaderThread.cpp
//
#include "AudioFilePlayer.h"
#include <mach/mach.h> //used for setting policy of thread
#include "CAGuard.h"
#include <pthread.h>
#include <list>
class FileReaderThread {
public:
FileReaderThread ();
CAGuard& GetGuard() { return mGuard; }
void AddReader();
void RemoveReader (const AudioFileReaderThread* inItem);
// returns true if succeeded
bool TryNextRead (AudioFileReaderThread* inItem)
{
bool didLock = false;
bool succeeded = false;
if (mGuard.Try (didLock))
{
mFileData.push_back (inItem);
mGuard.Notify();
succeeded = true;
if (didLock)
mGuard.Unlock();
}
return succeeded;
}
int mThreadShouldDie;
private:
typedef std::list<AudioFileReaderThread*> FileData;
CAGuard mGuard;
UInt32 mThreadPriority;
int mNumReaders;
FileData mFileData;
void ReadNextChunk ();
void StartFixedPriorityThread ();
static UInt32 GetThreadBasePriority (pthread_t inThread);
static void* DiskReaderEntry (void *inRefCon);
};
FileReaderThread::FileReaderThread ()
: mThreadPriority (62),
mNumReaders (0)
{
}
void FileReaderThread::AddReader()
{
if (mNumReaders == 0)
{
mThreadShouldDie = false;
StartFixedPriorityThread ();
}
mNumReaders++;
}
void FileReaderThread::RemoveReader (const AudioFileReaderThread* inItem)
{
if (mNumReaders > 0)
{
CAGuard::Locker fileReadLock (mGuard);
for (FileData::iterator iter = mFileData.begin(); iter != mFileData.end(); ++iter)
{
if ((*iter) == inItem) {
mFileData.erase (iter);
}
}
if (--mNumReaders == 0) {
mThreadShouldDie = true;
mGuard.Notify(); // wake up thread so it will quit
mGuard.Wait(); // wait for thread to die
}
}
}
void FileReaderThread::StartFixedPriorityThread ()
{
pthread_attr_t theThreadAttrs;
pthread_t pThread;
OSStatus result = pthread_attr_init(&theThreadAttrs);
THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")
result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")
result = pthread_create (&pThread, &theThreadAttrs, DiskReaderEntry, this);
THROW_RESULT("pthread_create - Create and start the thread.")
pthread_attr_destroy(&theThreadAttrs);
// we've now created the thread and started it
// we'll now set the priority of the thread to the nominated priority
// and we'll also make the thread fixed
thread_extended_policy_data_t theFixedPolicy;
thread_precedence_policy_data_t thePrecedencePolicy;
SInt32 relativePriority;
// make thread fixed
theFixedPolicy.timeshare = false; // set to true for a non-fixed thread
result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")
// set priority
// precedency policy's "importance" value is relative to spawning thread's priority
relativePriority = mThreadPriority - FileReaderThread::GetThreadBasePriority (pthread_self());
thePrecedencePolicy.importance = relativePriority;
result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
THROW_RESULT("thread_policy - Couldn't set thread priority.")
}
UInt32 FileReaderThread::GetThreadBasePriority (pthread_t inThread)
{
thread_basic_info_data_t threadInfo;
policy_info_data_t thePolicyInfo;
unsigned int count;
// get basic info
count = THREAD_BASIC_INFO_COUNT;
thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
switch (threadInfo.policy) {
case POLICY_TIMESHARE:
count = POLICY_TIMESHARE_INFO_COUNT;
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count);
return thePolicyInfo.ts.base_priority;
break;
case POLICY_FIFO:
count = POLICY_FIFO_INFO_COUNT;
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count);
if (thePolicyInfo.fifo.depressed) {
return thePolicyInfo.fifo.depress_priority;
} else {
return thePolicyInfo.fifo.base_priority;
}
break;
case POLICY_RR:
count = POLICY_RR_INFO_COUNT;
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count);
if (thePolicyInfo.rr.depressed) {
return thePolicyInfo.rr.depress_priority;
} else {
return thePolicyInfo.rr.base_priority;
}
break;
}
return 0;
}
void *FileReaderThread::DiskReaderEntry (void *inRefCon)
{
FileReaderThread *This = (FileReaderThread *)inRefCon;
This->ReadNextChunk();
#if DEBUG
printf ("finished with reading file\n");
#endif
return 0;
}
void FileReaderThread::ReadNextChunk ()
{
OSStatus result;
UInt32 dataChunkSize;
AudioFileReaderThread* theItem = 0;
for (;;)
{
{ // this is a scoped based lock
CAGuard::Locker fileReadLock (mGuard);
if (this->mThreadShouldDie) {
mGuard.Notify();
return;
}
if (mFileData.empty())
{
mGuard.Wait();
}
// kill thread
if (this->mThreadShouldDie) {
mGuard.Notify();
return;
}
theItem = mFileData.front();
mFileData.pop_front();
}
if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
else
dataChunkSize = theItem->mChunkSize;
// this is the exit condition for the thread
if (dataChunkSize == 0) {
theItem->mFinishedReadingData = true;
continue;
}
// construct pointer
char* writePtr = const_cast<char*>(theItem->GetFileBuffer() +
(theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
/*
printf ("AudioFileReadBytes: theItem=%.8X fileID=%.8X pos=%.8X sz=%.8X flen=%.8X ptr=%.8X\n",
(unsigned int)theItem, (unsigned int)theItem->GetFileID(),
(unsigned int)theItem->mReadFilePosition, (unsigned int)dataChunkSize,
(unsigned int)theItem->mFileLength, (unsigned int)writePtr);
*/
result = AudioFileReadBytes (theItem->GetFileID(),
false,
theItem->mReadFilePosition,
&dataChunkSize,
writePtr);
if (result) {
theItem->GetParent().DoNotification(result);
continue;
}
if (dataChunkSize != theItem->mChunkSize)
{
writePtr += dataChunkSize;
// can't exit yet.. we still have to pass the partial buffer back
memset (writePtr, 0, (theItem->mChunkSize - dataChunkSize));
}
theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; // switch buffers
theItem->mReadFilePosition += dataChunkSize; // increment count
}
}
static FileReaderThread sReaderThread;
AudioFileReaderThread::AudioFileReaderThread (AudioFilePlayer &inParent,
AudioFileID &inFile,
SInt64 inFileLength,
UInt32 inChunkSize)
: AudioFileManager (inParent, inFile),
mChunkSize (inChunkSize),
mFileLength (inFileLength),
mReadFilePosition (0),
mWriteToFirstBuffer (false),
mFinishedReadingData (false),
mLockUnsuccessful (false),
mIsEngaged (false)
{
mFileBuffer = (char*) malloc (mChunkSize * 2);
assert (mFileBuffer != NULL);
}
void AudioFileReaderThread::DoConnect ()
{
if (!mIsEngaged)
{
//mReadFilePosition = 0;
mFinishedReadingData = false;
mNumTimesAskedSinceFinished = -1;
mLockUnsuccessful = false;
UInt32 dataChunkSize;
if ((mFileLength - mReadFilePosition) < mChunkSize)
dataChunkSize = mFileLength - mReadFilePosition;
else
dataChunkSize = mChunkSize;
OSStatus result = AudioFileReadBytes ( mAudioFileID,
false,
mReadFilePosition,
&dataChunkSize,
mFileBuffer);
THROW_RESULT("AudioFileReadBytes")
mReadFilePosition += dataChunkSize;
mWriteToFirstBuffer = false;
mReadFromFirstBuffer = true;
sReaderThread.AddReader();
mIsEngaged = true;
}
else
throw static_cast<OSStatus>(-1); //thread has already been started
}
void AudioFileReaderThread::Disconnect ()
{
if (mIsEngaged)
{
sReaderThread.RemoveReader (this);
mIsEngaged = false;
}
}
OSStatus AudioFileReaderThread::GetFileData (void** inOutData, UInt32 *inOutDataSize)
{
if (mFinishedReadingData)
{
++mNumTimesAskedSinceFinished;
*inOutDataSize = 0;
*inOutData = 0;
return noErr;
}
if (mReadFromFirstBuffer == mWriteToFirstBuffer) {
#if DEBUG
printf ("* * * * * * * Can't keep up with reading file:%ld\n", mParent.GetBusNumber());
#endif
mParent.DoNotification (kAudioFilePlayErr_FilePlayUnderrun);
*inOutDataSize = 0;
*inOutData = 0;
} else {
*inOutDataSize = mChunkSize;
*inOutData = mReadFromFirstBuffer ? mFileBuffer : (mFileBuffer + mChunkSize);
}
mLockUnsuccessful = !sReaderThread.TryNextRead (this);
mReadFromFirstBuffer = !mReadFromFirstBuffer;
return noErr;
}
void AudioFileReaderThread::AfterRender ()
{
if (mNumTimesAskedSinceFinished > 0)
{
bool didLock = false;
if (sReaderThread.GetGuard().Try (didLock)) {
mParent.DoNotification (kAudioFilePlay_FileIsFinished);
if (didLock)
sReaderThread.GetGuard().Unlock();
}
}
if (mLockUnsuccessful)
mLockUnsuccessful = !sReaderThread.TryNextRead (this);
}
void AudioFileReaderThread::SetPosition (SInt64 pos)
{
if (pos < 0 || pos >= mFileLength) {
SDL_SetError ("AudioFileReaderThread::SetPosition - position invalid: %d filelen=%d\n",
(unsigned int)pos, (unsigned int)mFileLength);
pos = 0;
}
mReadFilePosition = pos;
}
void AudioFileReaderThread::SetEndOfFile (SInt64 pos)
{
if (pos <= 0 || pos > mFileLength) {
SDL_SetError ("AudioFileReaderThread::SetEndOfFile - position beyond actual eof\n");
pos = mFileLength;
}
mFileLength = pos;
}
\ No newline at end of file
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
/*
Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
Copyright: © Copyright 2002 Apple Computer, Inc. All rights reserved.
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
("Apple") in consideration of your agreement to the following terms, and your
use, installation, modification or redistribution of this Apple software
constitutes acceptance of these terms. If you do not agree with these terms,
please do not use, install, modify or redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and subject
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
copyrights in this original Apple software (the "Apple Software"), to use,
reproduce, modify and redistribute the Apple Software, with or without
modifications, in source and/or binary forms; provided that if you redistribute
the Apple Software in its entirety and without modifications, you must retain
this notice and the following text and disclaimers in all such redistributions of
the Apple Software. Neither the name, trademarks, service marks or logos of
Apple Computer, Inc. may be used to endorse or promote products derived from the
Apple Software without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or implied,
are granted by Apple herein, including but not limited to any patent rights that
may be infringed by your derivative works or by other works in which the Apple
Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*=============================================================================
CAGuard.cp
=============================================================================*/
//=============================================================================
// Includes
//=============================================================================
#include <stdio.h>
#define NDEBUG 1
#include <assert.h>
#include "CAGuard.h"
//#warning Need a try-based Locker too
//=============================================================================
// CAGuard
//=============================================================================
CAGuard::CAGuard()
{
OSStatus theError = pthread_mutex_init(&mMutex, NULL);
assert(theError == 0);
theError = pthread_cond_init(&mCondVar, NULL);
assert(theError == 0);
mOwner = 0;
}
CAGuard::~CAGuard()
{
pthread_mutex_destroy(&mMutex);
pthread_cond_destroy(&mCondVar);
}
bool CAGuard::Lock()
{
bool theAnswer = false;
if(pthread_self() != mOwner)
{
OSStatus theError = pthread_mutex_lock(&mMutex);
assert(theError == 0);
mOwner = pthread_self();
theAnswer = true;
}
return theAnswer;
}
void CAGuard::Unlock()
{
assert(pthread_self() == mOwner);
mOwner = 0;
OSStatus theError = pthread_mutex_unlock(&mMutex);
assert(theError == 0);
}
bool CAGuard::Try (bool& outWasLocked)
{
bool theAnswer = false;
outWasLocked = false;
if (pthread_self() == mOwner) {
theAnswer = true;
outWasLocked = false;
} else {
OSStatus theError = pthread_mutex_trylock(&mMutex);
if (theError == 0) {
mOwner = pthread_self();
theAnswer = true;
outWasLocked = true;
}
}
return theAnswer;
}
void CAGuard::Wait()
{
assert(pthread_self() == mOwner);
mOwner = 0;
OSStatus theError = pthread_cond_wait(&mCondVar, &mMutex);
assert(theError == 0);
mOwner = pthread_self();
}
void CAGuard::Notify()
{
OSStatus theError = pthread_cond_signal(&mCondVar);
assert(theError == 0);
}
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
/*
Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
Copyright: © Copyright 2002 Apple Computer, Inc. All rights reserved.
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
("Apple") in consideration of your agreement to the following terms, and your
use, installation, modification or redistribution of this Apple software
constitutes acceptance of these terms. If you do not agree with these terms,
please do not use, install, modify or redistribute this Apple software.
In consideration of your agreement to abide by the following terms, and subject
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
copyrights in this original Apple software (the "Apple Software"), to use,
reproduce, modify and redistribute the Apple Software, with or without
modifications, in source and/or binary forms; provided that if you redistribute
the Apple Software in its entirety and without modifications, you must retain
this notice and the following text and disclaimers in all such redistributions of
the Apple Software. Neither the name, trademarks, service marks or logos of
Apple Computer, Inc. may be used to endorse or promote products derived from the
Apple Software without specific prior written permission from Apple. Except as
expressly stated in this notice, no other rights or licenses, express or implied,
are granted by Apple herein, including but not limited to any patent rights that
may be infringed by your derivative works or by other works in which the Apple
Software may be incorporated.
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
COMBINATION WITH YOUR PRODUCTS.
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*=============================================================================
CAGuard.h
=============================================================================*/
#if !defined(__CAGuard_h__)
#define __CAGuard_h__
//=============================================================================
// Includes
//=============================================================================
#include <CoreAudio/CoreAudioTypes.h>
#include <pthread.h>
//=============================================================================
// CAGuard
//
// This is your typical mutex with signalling implemented via pthreads.
// Lock() will return true if and only if the guard is locked on that call.
// A thread that already has the guard will receive 'false' if it locks it
// again. Use of the stack-based CAGuard::Locker class is highly recommended
// to properly manage the recursive nesting. The Wait calls with timeouts
// will return true if and only if the timeout period expired. They will
// return false if they receive notification any other way.
//=============================================================================
class CAGuard
{
// Construction/Destruction
public:
CAGuard();
virtual ~CAGuard();
// Actions
public:
virtual bool Lock();
virtual void Unlock();
virtual bool Try(bool& outWasLocked); // returns true if lock is free, false if not
virtual void Wait();
virtual void Notify();
// Implementation
protected:
pthread_mutex_t mMutex;
pthread_cond_t mCondVar;
pthread_t mOwner;
// Helper class to manage taking and releasing recursively
public:
class Locker
{
// Construction/Destruction
public:
Locker(CAGuard& inGuard) : mGuard(inGuard), mNeedsRelease(false) { mNeedsRelease = mGuard.Lock(); }
~Locker() { if(mNeedsRelease) { mGuard.Unlock(); } }
private:
Locker(const Locker&);
Locker& operator=(const Locker&);
// Actions
public:
void Wait() { mGuard.Wait(); }
void Notify() { mGuard.Notify(); }
// Implementation
private:
CAGuard& mGuard;
bool mNeedsRelease;
};
};
#endif
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
#include "CDPlayer.h"
#include "AudioFilePlayer.h"
#include "CAGuard.h"
// we're exporting these functions into C land for SDL_syscdrom.c
extern "C" {
//
// Constants
//
#define kAudioCDFilesystemID (UInt16)(('J' << 8) | 'H') // 'JH'; this avoids compiler warning
// XML PList keys
#define kRawTOCDataString "Format 0x02 TOC Data"
#define kSessionsString "Sessions"
#define kSessionTypeString "Session Type"
#define kTrackArrayString "Track Array"
#define kFirstTrackInSessionString "First Track"
#define kLastTrackInSessionString "Last Track"
#define kLeadoutBlockString "Leadout Block"
#define kDataKeyString "Data"
#define kPointKeyString "Point"
#define kSessionNumberKeyString "Session Number"
#define kStartBlockKeyString "Start Block"
//
// Globals
//
#pragma mark -- Globals --
static bool playBackWasInit = false;
static AudioUnit theUnit;
static AudioFilePlayer* thePlayer = NULL;
static CDPlayerCompletionProc completionProc = NULL;
static pthread_mutex_t apiMutex;
static pthread_t callbackThread;
static pthread_mutex_t callbackMutex;
static volatile int runCallBackThread;
static int initMutex = SDL_TRUE;
static SDL_CD* theCDROM;
//
// Prototypes
//
#pragma mark -- Prototypes --
OSStatus CheckInit ();
OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus);
void FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus);
void* RunCallBackThread (void* inRefCon);
#pragma mark -- Public Functions --
void Lock ()
{
if (initMutex) {
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutex_init (&apiMutex, &attr);
pthread_mutexattr_destroy (&attr);
initMutex = SDL_FALSE;
}
pthread_mutex_lock (&apiMutex);
}
void Unlock ()
{
pthread_mutex_unlock (&apiMutex);
}
//
// DetectAudioCDVolumes
//
int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes)
{
int volumeIndex;
int cdVolumeCount = 0;
OSStatus result = noErr;
for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
{
FSVolumeRefNum actualVolume;
HFSUniStr255 volumeName;
FSVolumeInfo volumeInfo;
FSRef rootDirectory;
memset (&volumeInfo, 0, sizeof(volumeInfo));
result = FSGetVolumeInfo (kFSInvalidVolumeRefNum,
volumeIndex,
&actualVolume,
kFSVolInfoFSInfo,
&volumeInfo,
&volumeName,
&rootDirectory);
if (result == noErr)
{
if (volumeInfo.filesystemID == kAudioCDFilesystemID) // It's an audio CD
{
if (volumes != NULL && cdVolumeCount < numVolumes)
volumes[cdVolumeCount] = actualVolume;
cdVolumeCount++;
}
}
else
{
// I'm commenting this out because it seems to be harmless
//SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);
}
}
return cdVolumeCount;
}
//
// ReadTOCData
//
int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD)
{
HFSUniStr255 dataForkName;
OSStatus theErr;
SInt16 forkRefNum;
SInt64 forkSize;
Ptr forkData = 0;
ByteCount actualRead;
CFDataRef dataRef = 0;
CFPropertyListRef propertyListRef = 0;
FSRefParam fsRefPB;
FSRef tocPlistFSRef;
const char* error = "Unspecified Error";
// get stuff from .TOC.plist
fsRefPB.ioCompletion = NULL;
fsRefPB.ioNamePtr = "\p.TOC.plist";
fsRefPB.ioVRefNum = theVolume;
fsRefPB.ioDirID = 0;
fsRefPB.newRef = &tocPlistFSRef;
theErr = PBMakeFSRefSync (&fsRefPB);
if(theErr != noErr) {
error = "PBMakeFSRefSync";
goto bail;
}
// Load and parse the TOC XML data
theErr = FSGetDataForkName (&dataForkName);
if (theErr != noErr) {
error = "FSGetDataForkName";
goto bail;
}
theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum);
if (theErr != noErr) {
error = "FSOpenFork";
goto bail;
}
theErr = FSGetForkSize (forkRefNum, &forkSize);
if (theErr != noErr) {
error = "FSGetForkSize";
goto bail;
}
// Allocate some memory for the XML data
forkData = NewPtr (forkSize);
if(forkData == NULL) {
error = "NewPtr";
goto bail;
}
theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead);
if(theErr != noErr) {
error = "FSReadFork";
goto bail;
}
dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize);
if(dataRef == 0) {
error = "CFDataCreate";
goto bail;
}
propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault,
dataRef,
kCFPropertyListImmutable,
NULL);
if (propertyListRef == NULL) {
error = "CFPropertyListCreateFromXMLData";
goto bail;
}
// Now we got the Property List in memory. Parse it.
// First, make sure the root item is a CFDictionary. If not, release and bail.
if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID())
{
CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef;
CFDataRef theRawTOCDataRef;
CFArrayRef theSessionArrayRef;
CFIndex numSessions;
CFIndex index;
// This is how we get the Raw TOC Data
theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString));
// Get the session array info.
theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString));
// Find out how many sessions there are.
numSessions = CFArrayGetCount (theSessionArrayRef);
// Initialize the total number of tracks to 0
theCD->numtracks = 0;
// Iterate over all sessions, collecting the track data
for(index = 0; index < numSessions; index++)
{
CFDictionaryRef theSessionDict;
CFNumberRef leadoutBlock;
CFArrayRef trackArray;
CFIndex numTracks;
CFIndex trackIndex;
UInt32 value = 0;
theSessionDict = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index);
leadoutBlock = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString));
trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString));
numTracks = CFArrayGetCount (trackArray);
for(trackIndex = 0; trackIndex < numTracks; trackIndex++) {
CFDictionaryRef theTrackDict;
CFNumberRef trackNumber;
CFNumberRef sessionNumber;
CFNumberRef startBlock;
CFBooleanRef isDataTrack;
UInt32 value;
theTrackDict = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex);
trackNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString));
sessionNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString));
startBlock = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString));
isDataTrack = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString));
// Fill in the SDL_CD struct
int idx = theCD->numtracks++;
CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value);
theCD->track[idx].id = value;
CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value);
theCD->track[idx].offset = value;
theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK;
// Since the track lengths are not stored in .TOC.plist we compute them.
if (trackIndex > 0) {
theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset;
}
}
// Compute the length of the last track
CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value);
theCD->track[theCD->numtracks-1].length =
value - theCD->track[theCD->numtracks-1].offset;
// Set offset to leadout track
theCD->track[theCD->numtracks].offset = value;
}
}
theErr = 0;
goto cleanup;
bail:
SDL_SetError ("ReadTOCData: %s returned %d", error, theErr);
theErr = -1;
cleanup:
if (propertyListRef != NULL)
CFRelease(propertyListRef);
if (dataRef != NULL)
CFRelease(dataRef);
if (forkData != NULL)
DisposePtr(forkData);
FSCloseFork (forkRefNum);
return theErr;
}
//
// ListTrackFiles
//
int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks)
{
OSStatus result = -1;
FSIterator iterator;
ItemCount actualObjects;
FSRef rootDirectory;
FSRef ref;
HFSUniStr255 nameStr;
result = FSGetVolumeInfo (theVolume,
0,
NULL,
kFSVolInfoFSInfo,
NULL,
NULL,
&rootDirectory);
if (result != noErr) {
SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result);
goto bail;
}
result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator);
if (result == noErr) {
do
{
result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects,
NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr);
if (result == noErr) {
CFStringRef name;
name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length);
// Look for .aiff extension
if (CFStringHasSuffix (name, CFSTR(".aiff"))) {
// Extract the track id from the filename
int trackID = 0, i = 0;
while (nameStr.unicode[i] >= '0' && nameStr.unicode[i] <= '9') {
trackID = 10 * trackID +(nameStr.unicode[i] - '0');
i++;
}
#if DEBUG_CDROM
printf("Found AIFF for track %d: '%s'\n", trackID,
CFStringGetCStringPtr (name, CFStringGetSystemEncoding()));
#endif
// Track ID's start at 1, but we want to start at 0
trackID--;
assert(0 <= trackID && trackID <= SDL_MAX_TRACKS);
if (trackID < numTracks)
memcpy (&trackFiles[trackID], &ref, sizeof(FSRef));
}
CFRelease (name);
}
} while(noErr == result);
FSCloseIterator (iterator);
}
result = 0;
bail:
return result;
}
//
// LoadFile
//
int LoadFile (const FSRef *ref, int startFrame, int stopFrame)
{
int error = -1;
if (CheckInit () < 0)
goto bail;
// release any currently playing file
if (ReleaseFile () < 0)
goto bail;
#if DEBUG_CDROM
printf ("LoadFile: %d %d\n", startFrame, stopFrame);
#endif
try {
// create a new player, and attach to the audio unit
thePlayer = new AudioFilePlayer(ref);
if (thePlayer == NULL) {
SDL_SetError ("LoadFile: Could not create player");
throw (-3);
}
thePlayer->SetDestination(theUnit, 0);
if (startFrame >= 0)
thePlayer->SetStartFrame (startFrame);
if (stopFrame >= 0 && stopFrame > startFrame)
thePlayer->SetStopFrame (stopFrame);
// we set the notifier later
//thePlayer->SetNotifier(FilePlayNotificationHandler, NULL);
thePlayer->Connect();
#if DEBUG_CDROM
thePlayer->Print();
fflush (stdout);
#endif
}
catch (...)
{
goto bail;
}
error = 0;
bail:
return error;
}
//
// ReleaseFile
//
int ReleaseFile ()
{
int error = -1;
try {
if (thePlayer != NULL) {
thePlayer->Disconnect();
delete thePlayer;
thePlayer = NULL;
}
}
catch (...)
{
goto bail;
}
error = 0;
bail:
return error;
}
//
// PlayFile
//
int PlayFile ()
{
OSStatus result = -1;
if (CheckInit () < 0)
goto bail;
try {
// start processing of the audio unit
result = AudioOutputUnitStart (theUnit);
THROW_RESULT("PlayFile: AudioOutputUnitStart")
}
catch (...)
{
goto bail;
}
result = 0;
bail:
return result;
}
//
// PauseFile
//
int PauseFile ()
{
OSStatus result = -1;
if (CheckInit () < 0)
goto bail;
try {
// stop processing the audio unit
result = AudioOutputUnitStop (theUnit);
THROW_RESULT("PauseFile: AudioOutputUnitStop")
}
catch (...)
{
goto bail;
}
result = 0;
bail:
return result;
}
//
// SetCompletionProc
//
void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom)
{
assert(thePlayer != NULL);
theCDROM = cdrom;
completionProc = proc;
thePlayer->SetNotifier (FilePlayNotificationHandler, cdrom);
}
int GetCurrentFrame ()
{
int frame;
if (thePlayer == NULL)
frame = 0;
else
frame = thePlayer->GetCurrentFrame ();
return frame;
}
#pragma mark -- Private Functions --
OSStatus CheckInit ()
{
if (playBackWasInit)
return 0;
OSStatus result = noErr;
// Create the callback mutex
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutex_init (&callbackMutex, &attr);
pthread_mutexattr_destroy (&attr);
pthread_mutex_lock (&callbackMutex);
// Start callback thread
pthread_attr_t attr1;
pthread_attr_init (&attr1);
pthread_create (&callbackThread, &attr1, RunCallBackThread, NULL);
pthread_attr_destroy (&attr1);
try {
ComponentDescription desc;
desc.componentType = kAudioUnitComponentType;
desc.componentSubType = kAudioUnitSubType_Output;
desc.componentManufacturer = kAudioUnitID_DefaultOutput;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
Component comp = FindNextComponent (NULL, &desc);
if (comp == NULL) {
SDL_SetError ("CheckInit: FindNextComponent returned NULL");
throw(internalComponentErr);
}
result = OpenAComponent (comp, &theUnit);
THROW_RESULT("CheckInit: OpenAComponent")
// you need to initialize the output unit before you set it as a destination
result = AudioUnitInitialize (theUnit);
THROW_RESULT("CheckInit: AudioUnitInitialize")
// In this case we first want to get the output format of the OutputUnit
// Then we set that as the input format. Why?
// So that only a single conversion process is done
// when SetDestination is called it will get the input format of the
// unit its supplying data to. This defaults to 44.1K, stereo, so if
// the device is not that, then we lose a possibly rendering of data
result = MatchAUFormats (theUnit, 0);
THROW_RESULT("CheckInit: MatchAUFormats")
playBackWasInit = true;
}
catch (...)
{
return -1;
}
return 0;
}
OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus)
{
AudioStreamBasicDescription theDesc;
UInt32 size = sizeof (theDesc);
OSStatus result = AudioUnitGetProperty (theUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&theDesc,
&size);
THROW_RESULT("MatchAUFormats: AudioUnitGetProperty")
result = AudioUnitSetProperty (theUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
theInputBus,
&theDesc,
size);
return result;
}
void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus)
{
if (inStatus == kAudioFilePlay_FileIsFinished) {
// notify non-CA thread to perform the callback
pthread_mutex_unlock (&callbackMutex);
} else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) {
SDL_SetError ("CDPlayer Notification: buffer underrun");
} else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) {
SDL_SetError ("CDPlayer Notification: player is uninitialized");
} else {
SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus);
}
}
void* RunCallBackThread (void *param)
{
runCallBackThread = 1;
while (runCallBackThread) {
pthread_mutex_lock (&callbackMutex);
if (completionProc && theCDROM) {
#if DEBUG_CDROM
printf ("callback!\n");
#endif
(*completionProc)(theCDROM);
} else {
#if DEBUG_CDROM
printf ("callback?\n");
#endif
}
}
runCallBackThread = -1;
#if DEBUG_CDROM
printf ("thread dying now...\n");
#endif
return NULL;
}
}; // extern "C"
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
#ifndef __CDPlayer__H__
#define __CDPlayer__H__ 1
#include <CoreFoundation/CoreFoundation.h>
#include <Carbon/Carbon.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include <SDL.h>
#include <string.h>
#include <unistd.h> // for usleep
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*CDPlayerCompletionProc)(SDL_CD *cdrom) ;
void Lock ();
void Unlock();
int LoadFile (const FSRef *ref, int startFrame, int endFrame); // pass -1 to do nothing
int ReleaseFile ();
int PlayFile ();
int PauseFile ();
void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom);
int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD);
int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks);
int DetectAudioCDVolumes (FSVolumeRefNum *volumes, int numVolumes);
int GetCurrentFrame ();
#ifdef __cplusplus
};
#endif
#endif /* __CD_Player__H__ */
## Makefile.am for the Mac OS X cdrom driver for SDL
noinst_LTLIBRARIES = libcdrom_macosx.la
libcdrom_macosx_la_SOURCES = $(SRCS)
# The SDL cdrom driver sources
SRCS = \
SDL_syscdrom.c \
AudioFilePlayer.cpp \
AudioFileReaderThread.cpp \
CAGuard.cpp \
CDPlayer.cpp
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
*/
/***********************************************************************************
Implementation Notes
*********************
This code has several limitations currently (all of which are proabaly fixable):
1. A CD-ROM device is inferred from a mounted cdfs volume, so device 0 is
not necessarily the first CD-ROM device on the system. (Somewhat easy to fix
by useing the device name from the volume id's to reorder the volumes)
2. You can only open and control 1 CD-ROM device at a time. (Challenging to fix,
due to extensive code restructuring)
3. The status reported by SDL_CDStatus only changes to from CD_PLAYING to CD_STOPPED in
1-second intervals (because the audio is buffered in 1-second chunks) If
the audio data is less than 1 second, the remainder is filled with silence.
If you need to play sequences back-to-back that are less that 1 second long,
use the frame position to determine when to play the next sequence, instead
of SDL_CDStatus.
This may be possible to fix with a clever usage of the AudioUnit API.
4. When new volumes are inserted, our volume information is not updated. The only way
to refresh this information is to reinit the CD-ROM subsystem of SDL. To fix this,
one would probably have to fix point 1 above first, then figure out how to register
for a notification when new media is mounted in order to perform an automatic
rescan for cdfs volumes.
So, here comes a description of how this all works.
< Initializing >
To get things rolling, we have to locate mounted volumes that contain
audio (since nearly all Macs don't have analog audio-in on the sound card).
That's easy, since these volumes have a flag that indicates this special
filesystem. See DetectAudioCDVolumes() in CDPlayer.cpp for this code.
Next, we parse the invisible .TOC.plist in the root of the volume, which gets us
the track information (number, offset, length, leadout, etc). See ReadTOCData() in
CDPlayer.cpp for the skinny on this.
< The Playback Loop >
Now come the tricky parts. Let's start with basic audio playback. When a frame
range to play is requested, we must first find the .aiff files on the volume,
hopefully in the right order. Since these files all begin with a number "1 Audio Track",
etc, this is used to determine the correct track order.
Once all files are determined, we have to find what file corresponds to the start
and length parameter to SDL_SYS_CDPlay(). Again, this is quite simple by walking the
cdrom's track list. At this point, we also save the offset to the next track and frames
remaining, if we're going to have to play another file after the first one. See
GetFileForOffset() for this code.
At this point we have all info needed to start playback, so we hand off to the LoadFile()
function, which proceeds to do its magic and plays back the file.
When the file is finished playing, CompletionProc() is invoked, at which time we can
play the next file if the previously saved next track and frames remaining
indicates that we should.
< Magic >
OK, so it's not really magic, but since I don't fully understand all the hidden details it
seems like it to me ;-) The API's involved are the AudioUnit and AudioFile API's. These
appear to be an extension of CoreAudio for creating modular playback and f/x entities.
The important thing is that CPU usage is very low and reliability is very high. You'd
be hard-pressed to find a way to stutter the playback with other CPU-intensive tasks.
One part of this magic is that it uses multiple threads, which carries the usual potential
for disaster if not handled carefully. Playback currently requires 4 additional threads:
1. The coreaudio runloop thread
2. The coreaudio device i/o thread
3. The file streaming thread
4. The notification/callback thread
The first 2 threads are necessary evil - CoreAudio creates this no matter what the situation
is (even the SDL sound implementation creates theses suckers). The last two are are created
by us.
The file is streamed from disk using a threaded double-buffer approach.
This way, the high latency operation of reading from disk can be performed without interrupting
the real-time device thread (which amounts to avoiding dropouts). The device thread grabs the
buffer that isn't being read and sends it to the CoreAudio mixer where it eventually gets
to the sound card.
The device thread posts a notification when the file streaming thread is out of data. This
notification must be handled in a separate thread to avoid potential deadlock in the
device thread. That's where the notification thread comes in. This thread is signaled
whenever a notification needs to be processed, so another file can be played back if need be.
The API in CDPlayer.cpp contains synchronization because otherwise both the notification thread
and main thread (or another other thread using the SDL CD api) can potentially call it at the same time.
************************************************************************************/
#include "SDL_cdrom.h"
#include "SDL_syscdrom.h"
#include "CDPlayer.h"
#define kErrorFakeDevice "Error: Cannot proceed since we're faking a CD-ROM device. Reinit the CD-ROM subsystem to scan for new volumes."
......@@ -64,8 +64,8 @@ static void ListTracks(SDL_CD *cdrom)
trtype="unknown";
break;
}
printf("\tTrack (index %d) %d: %d:%2.2d [%s track]\n", i,
cdrom->track[i].id, m, s, trtype);
printf("\tTrack (index %d) %d: %d:%2.2d / %d [%s track]\n", i,
cdrom->track[i].id, m, s, cdrom->track[i].length, trtype);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment