Mercurial > sdl-ios-xcode
diff src/cdrom/macosx/AudioFilePlayer.cpp @ 613:9c6717a1c66f
Added MacOS X CD-ROM audio support (thanks Max and Darrell)
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Tue, 15 Apr 2003 16:33:56 +0000 |
parents | |
children | de1b2c3063b9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cdrom/macosx/AudioFilePlayer.cpp Tue Apr 15 16:33:56 2003 +0000 @@ -0,0 +1,377 @@ +/* + 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