// MyFindQueueBuffer
//
// Returns the index of the specified buffer in the audioQueueBuffer array.
//
// This function is unchanged from Apple's example in AudioFileStreamExample.
//
int MyFindQueueBuffer(xxxxx* myData,
AudioQueueBufferRef inBuffer)
{
for (unsigned
int i = 0; i < kNumAQBufs; ++i) {
if (inBuffer == myData->buffers)
return i;
}
return -1;
}
//
// MyAudioQueueOutputCallback
//
// Called from the AudioQueue when playback of specific buffers completes. This
// function signals from the AudioQueue thread to the AudioStream thread that
// the buffer is idle and available for copying data.
//
// This function is unchanged from Apple's example in AudioFileStreamExample.
//
void MyAudioQueueOutputCallback(void* inClientData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer)
{
// this is called by the audio queue when it has finished decoding our data.
// The buffer is now free to be reused.
xxxxx* myData = (xxxxx*)inClientData;
unsigned int bufIndex = MyFindQueueBuffer(myData, inBuffer);
// signal waiting thread that the buffer is free.
pthread_mutex_lock(&myData->mutex);
myData->inuse[bufIndex] =
false;
pthread_cond_signal(&myData->cond);
pthread_mutex_unlock(&myData->mutex);
}
//
// MyAudioQueueIsRunningCallback
//
// Called from the AudioQueue when playback is started or stopped. This
// information is used to toggle the observable "isPlaying" property and
// set the "finished" flag.
//
void MyAudioQueueIsRunningCallback(void *inUserData,
AudioQueueRef inAQ,
AudioQueuePropertyID inID)
{
xxxxx *myData = (xxxxx *)inUserData;
NSAutoreleasePool *pool = [[NSAutoreleasePool
alloc]
init];
if (myData.isPlaying)
{
myData->trackEnded =
true;
myData.isPlaying =
false;
#ifdef TARGET_OS_IPHONE
AudioSessionSetActive(false);
#endif
}
else
{
myData.isPlaying =
true;
if (myData->trackEnded)
{
myData.isPlaying =
false;
}
//
// Note about this bug avoidance quirk:
//
// On cleanup of the AudioQueue thread, on rare occasions, there would
// be a crash in CFSetContainsValue as a CFRunLoopObserver was getting
// removed from the CFRunLoop.
//
// After lots of testing, it appeared that the audio thread was
// attempting to remove CFRunLoop observers from the CFRunLoop after the
// thread had already deallocated the run loop.
//
// By creating an NSRunLoop for the AudioQueue thread, it changes the
// thread destruction order and seems to avoid this crash bug -- or
// at least I haven't had it since (nasty hard to reproduce error!)
//
[NSRunLoop currentRunLoop];
}
[pool
release];
}
#ifdef TARGET_OS_IPHONE
//
// MyAudioSessionInterruptionListener
//
// Invoked if the audio session is interrupted (like when the phone rings)
//
void MyAudioSessionInterruptionListener(void *inClientData,
UInt32 inInterruptionState)
{
}
#endif
#pragma mark -
#pragma mark CFReadStream Callback Function Implementations
//
// ReadStreamCallBack
//
// This is the callback for the CFReadStream from the network connection. This
// is where all network data is passed to the AudioFileStream.
//
// Invoked when an error occurs, the stream ends or we have data to read.
//
void ReadStreamCallBack(CFReadStreamRef stream,
CFStreamEventType eventType,
void* dataIn)
{
xxxxx *myData = (xxxxx *)dataIn;
if (eventType ==
kCFStreamEventErrorOccurred)
{
myData->failed =
YES;
}
else
if (eventType ==
kCFStreamEventEndEncountered)
{
if (myData->failed || myData->trackEnded)
{
return;
}
//
// If there is a partially filled buffer, pass it to the AudioQueue for
// processing
//
if (myData->bytesFilled)
{
MyEnqueueBuffer(myData);
}
//
// If the AudioQueue started, then flush it (to make certain everything
// sent thus far will be processed) and subsequently stop the queue.
//
if (myData->started)
{
OSStatus err = AudioQueueFlush(myData->queue);
if (err) { NSLog(@"AudioQueueFlush");
return; }
err = AudioQueueStop(myData->queue,
false);
if (err) { NSLog(@"AudioQueueStop");
return; }
CFReadStreamClose(stream);
CFRelease(stream);
myData->stream =
nil;
}
else
{
//
// If we have reached the end of the file without starting, then we
// have failed to find any audio in the file. Abort.
//
myData->failed =
YES;
}
}
else
if (eventType ==
kCFStreamEventHasBytesAvailable)
{
if (myData->failed || myData->trackEnded)
{
return;
}
//
// Read the bytes from the stream
//
UInt8 bytes[kAQBufSize];
CFIndex length = CFReadStreamRead(stream, bytes, kAQBufSize);
if (length == -1)
{
myData->failed =
YES;
return;
}
//
// Parse the bytes read by sending them through the AudioFileStream
//
if (length > 0)
{
if (myData->discontinuous)
{
OSStatus err = AudioFileStreamParseBytes(myData->audioFileStream, length, bytes, kAudioFileStreamParseFlag_Discontinuity);
if (err) { NSLog(@"AudioFileStreamParseBytes"); myData->failed =
true;}
}
else
{
OSStatus err = AudioFileStreamParseBytes(myData->audioFileStream, length, bytes,
0);
if (err) { NSLog(@"AudioFileStreamParseBytes"); myData->failed =
true; }
}
}
}
}
@interface xxxxx (private)
static
void propertyListenerCallback(void *inUserData,
AudioQueueRef queueObject,
AudioQueuePropertyID propertyID);
- (void) playBackIsRunningStateChanged;
static
void BufferCallback(void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef buffer);
- (void) callbackForBuffer:(AudioQueueBufferRef)buffer;
- (UInt32) readPacketsIntoBuffer:(AudioQueueBufferRef)buffer;
@end
@implementation xxxxx
@synthesize isPlaying, trackClosed;
#pragma mark -
#pragma mark xxxxx
- (void)dealloc
{
[self
close];
if (packetDescs !=
nil)
free(packetDescs);
[url release];
[super dealloc];
}
- (void)close
{
// it is preferrable to call close first, if there is a problem waiting for an autorelease
if (trackClosed)
return;
trackClosed =
YES;
AudioQueueStop(queue,
YES);
// <-- YES means stop immediately
AudioQueueDispose(queue,
YES);
AudioFileClose(audioFile);
kxxxTrackActive =
NO;
}
- (id)initWithURL:(NSURL*)newUrl
{
self = [super init];
if (self !=
nil)
{
url = [newUrl retain];
}
return
self;
}
- (id)initWithPath:(NSString*)path
{
UInt32 size, maxPacketSize;
char *cookie;
int i;
if (kxxxTrackActive)
{
NSLog(@"Other music is playing.");
return nil;
}
if (path == nil)
return nil;
if(!(self = [super init]))
return nil;
// try to open up the file using the specified path
if (noErr != AudioFileOpenURL((CFURLRef)[NSURL fileURLWithPath:path],
0x01, 0, &audioFile))
{
NSLog(@"File can not be opened!");
return nil;
}