Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
git-svn-id: svn://witness.is-a-geek.org/svn@1 378c4bed-2673-4746-83ae-d22ddc8c5b7c
- Loading branch information
uli
committed
Jul 4, 2009
0 parents
commit 5d77434
Showing
4 changed files
with
363 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// | ||
// UKSound.h | ||
// MobileMoose | ||
// | ||
// Created by Uli Kusterer on 14.07.08. | ||
// Copyright 2008 The Void Software. All rights reserved. | ||
// | ||
|
||
#import <UIKit/UIKit.h> | ||
#import <AudioToolbox/AudioToolbox.h> | ||
|
||
|
||
#define kNumberBuffers 2 | ||
|
||
|
||
@class UKSound; | ||
|
||
|
||
@protocol UKSoundDelegate | ||
|
||
@optional | ||
-(void) sound: (UKSound*)sender didFinishPlaying: (BOOL)state; | ||
|
||
@end | ||
|
||
|
||
|
||
@interface UKSound : NSObject | ||
{ | ||
AudioFileID mAudioFile; | ||
AudioStreamBasicDescription mDataFormat; | ||
AudioQueueRef mQueue; | ||
AudioQueueBufferRef mBuffers[kNumberBuffers]; | ||
UInt64 mPacketIndex; | ||
UInt32 mNumPacketsToRead; | ||
AudioStreamPacketDescription * mPacketDescs; | ||
BOOL mDone; | ||
id<UKSoundDelegate> delegate; | ||
int maxBufferSizeBytes; | ||
} | ||
|
||
@property (assign) id<UKSoundDelegate> delegate; | ||
|
||
-(id) initWithContentsOfURL: (NSURL*)theURL; | ||
|
||
-(void) play; | ||
|
||
|
||
// private: | ||
-(void) audioQueue: (AudioQueueRef)inAQ processBuffer: (AudioQueueBufferRef)inCompleteAQBuffer; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
// | ||
// UKSound.m | ||
// MobileMoose | ||
// | ||
// Created by Uli Kusterer on 14.07.08. | ||
// Copyright 2008 The Void Software. All rights reserved. | ||
// | ||
|
||
#import "UKSound.h" | ||
|
||
|
||
static void UKSoundAQBufferCallback(void * inUserData, | ||
AudioQueueRef inAQ, | ||
AudioQueueBufferRef inCompleteAQBuffer) | ||
{ | ||
UKSound* myself = (UKSound*)inUserData; | ||
|
||
[myself audioQueue: inAQ processBuffer: inCompleteAQBuffer]; | ||
} | ||
|
||
|
||
static void UKSoundAQPropertyListenerCallback( void * inUserData, | ||
AudioQueueRef inAQ, | ||
AudioQueuePropertyID inID) | ||
{ | ||
[(UKSound*)inUserData performSelectorOnMainThread: @selector(notifyDelegatePlaybackStateChanged:) withObject: nil waitUntilDone: NO]; | ||
} | ||
|
||
|
||
@implementation UKSound | ||
|
||
@synthesize delegate; | ||
|
||
-(id) initWithContentsOfURL: (NSURL*)theURL | ||
{ | ||
self = [super init]; | ||
if( self ) | ||
{ | ||
maxBufferSizeBytes = 0x10000; | ||
OSStatus err = AudioFileOpenURL( (CFURLRef)theURL, kAudioFileReadPermission, 0, &mAudioFile ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't open AudioFile."); | ||
UInt32 size = sizeof(mDataFormat); | ||
err = AudioFileGetProperty( mAudioFile, kAudioFilePropertyDataFormat, &size, &mDataFormat ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't determine audio file format."); | ||
err = AudioQueueNewOutput( &mDataFormat, UKSoundAQBufferCallback, self, NULL, NULL, 0, &mQueue ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't create new output for queue."); | ||
|
||
// We have a couple of things to take care of now | ||
// (1) Setting up the conditions around VBR or a CBR format - affects how we will read from the file | ||
// if format is VBR we need to use a packet table. | ||
if( mDataFormat.mBytesPerPacket == 0 || mDataFormat.mFramesPerPacket == 0 ) | ||
{ | ||
// first check to see what the max size of a packet is - if it is bigger | ||
// than our allocation default size, that needs to become larger | ||
UInt32 maxPacketSize; | ||
size = sizeof(maxPacketSize); | ||
err = AudioFileGetProperty( mAudioFile, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't get max packet size of audio file."); | ||
if( maxPacketSize > maxBufferSizeBytes ) | ||
maxBufferSizeBytes = maxPacketSize; | ||
|
||
// we also need packet descpriptions for the file reading | ||
mNumPacketsToRead = maxBufferSizeBytes / maxPacketSize; | ||
mPacketDescs = malloc( sizeof(AudioStreamPacketDescription) * mNumPacketsToRead ); | ||
} | ||
else | ||
{ | ||
mNumPacketsToRead = maxBufferSizeBytes / mDataFormat.mBytesPerPacket; | ||
mPacketDescs = NULL; | ||
} | ||
|
||
// (2) If the file has a cookie, we should get it and set it on the AQ | ||
size = sizeof(UInt32); | ||
err = AudioFileGetPropertyInfo( mAudioFile, kAudioFilePropertyMagicCookieData, &size, NULL ); | ||
if( !err && size ) | ||
{ | ||
char* cookie = malloc( size ); | ||
err = AudioFileGetProperty( mAudioFile, kAudioFilePropertyMagicCookieData, &size, cookie ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't get magic cookie of audio file."); | ||
err = AudioQueueSetProperty( mQueue, kAudioQueueProperty_MagicCookie, cookie, size ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't transfer magic cookie of audio file to qudio queue."); | ||
free( cookie ); | ||
} | ||
|
||
err = AudioQueueAddPropertyListener( mQueue, kAudioQueueProperty_IsRunning, | ||
UKSoundAQPropertyListenerCallback, | ||
self ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't register for playback state changes."); | ||
|
||
// prime the queue with some data before starting | ||
mDone = false; | ||
mPacketIndex = 0; | ||
for( int i = 0; i < kNumberBuffers; ++i ) | ||
{ | ||
err = AudioQueueAllocateBuffer( mQueue, maxBufferSizeBytes, &mBuffers[i] ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't allocate buffer %d.", i); | ||
|
||
UKSoundAQBufferCallback( self, mQueue, mBuffers[i] ); | ||
|
||
if( mDone ) break; | ||
} | ||
} | ||
|
||
return self; | ||
} | ||
|
||
|
||
-(void) dealloc | ||
{ | ||
OSStatus err = AudioQueueDispose( mQueue, true ); | ||
err = AudioFileClose( mAudioFile ); | ||
if( mPacketDescs ) | ||
free( mPacketDescs ); | ||
|
||
[super dealloc]; | ||
} | ||
|
||
|
||
-(void) play | ||
{ | ||
OSStatus err = AudioQueueStart( mQueue, NULL ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't start audio queue."); | ||
else | ||
[self retain]; | ||
} | ||
|
||
|
||
-(BOOL) isPlaying | ||
{ | ||
UInt32 state = NO, | ||
size = sizeof(UInt32); | ||
OSStatus err = AudioQueueGetProperty( mQueue, kAudioQueueProperty_IsRunning, &state, &size ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't get play state of queue."); | ||
|
||
return state; | ||
} | ||
|
||
|
||
-(void) notifyDelegatePlaybackStateChanged: (id)sender; | ||
{ | ||
if( ![self isPlaying] ) | ||
{ | ||
[delegate sound: self didFinishPlaying: YES]; | ||
|
||
AudioQueueStop( mQueue, false ); | ||
[self release]; | ||
} | ||
} | ||
|
||
|
||
-(void) audioQueue: (AudioQueueRef)inAQ processBuffer: (AudioQueueBufferRef)inCompleteAQBuffer | ||
{ | ||
if( mDone ) | ||
return; | ||
|
||
UInt32 numBytes; | ||
UInt32 nPackets = mNumPacketsToRead; | ||
|
||
// Read nPackets worth of data into buffer | ||
OSStatus err = AudioFileReadPackets( mAudioFile, false, &numBytes, mPacketDescs, mPacketIndex, &nPackets, | ||
inCompleteAQBuffer->mAudioData); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't read into buffer."); | ||
|
||
if (nPackets > 0) | ||
{ | ||
inCompleteAQBuffer->mAudioDataByteSize = numBytes; | ||
|
||
// Queues the buffer for audio input/output. | ||
err = AudioQueueEnqueueBuffer( inAQ, inCompleteAQBuffer, (mPacketDescs ? nPackets : 0), mPacketDescs ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't enqueue buffer."); | ||
|
||
mPacketIndex += nPackets; | ||
} | ||
else | ||
{ | ||
UInt32 state = NO, | ||
size = sizeof(UInt32); | ||
OSStatus err = AudioQueueGetProperty( mQueue, kAudioQueueProperty_IsRunning, &state, &size ); | ||
|
||
// I should be calling the following, but it always makes the app hang. | ||
if( state ) | ||
{ | ||
err = AudioQueueStop( mQueue, false ); | ||
if( err != noErr ) | ||
NSLog(@"Couldn't stop queue."); | ||
// reading nPackets == 0 is our EOF condition | ||
} | ||
mDone = true; | ||
} | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// | ||
// UKSystemSound.h | ||
// iPhoneTestApp | ||
// | ||
// Created by Uli Kusterer on 19.06.08. | ||
// Copyright 2008 The Void Software. All rights reserved. | ||
// | ||
|
||
/* | ||
An NSSound-like class for the iPhone OS. | ||
*/ | ||
|
||
#import <UIKit/UIKit.h> | ||
#import <AudioToolbox/AudioToolbox.h> | ||
|
||
|
||
@class UKSystemSound; | ||
|
||
|
||
@protocol UKSystemSoundDelegate | ||
|
||
@optional | ||
|
||
-(void) sound: (UKSystemSound *)sound didFinishPlaying: (BOOL)aBool; | ||
|
||
@end | ||
|
||
|
||
@interface UKSystemSound : NSObject | ||
{ | ||
BOOL soundIDValid; | ||
SystemSoundID systemSoundID; | ||
id<UKSystemSoundDelegate> delegate; | ||
} | ||
|
||
-(id) initWithContentsOfURL: (NSURL*)fileURL byReference: (BOOL)ignored; // CAF, AIFF or WAV files only. | ||
|
||
@property (nonatomic,assign) id<UKSystemSoundDelegate> delegate; | ||
|
||
-(void) play; | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// | ||
// UKSystemSound.m | ||
// iPhoneTestApp | ||
// | ||
// Created by Uli Kusterer on 19.06.08. | ||
// Copyright 2008 The Void Software. All rights reserved. | ||
// | ||
|
||
#import "UKSystemSound.h" | ||
|
||
|
||
void UKSystemSoundAudioServicesSystemSoundCompletionProc( SystemSoundID ssID, void *clientData ) | ||
{ | ||
if( clientData ) | ||
[[(UKSystemSound*)clientData delegate] sound: (UKSystemSound*)clientData didFinishPlaying: YES]; | ||
AudioServicesRemoveSystemSoundCompletion( ssID ); | ||
[(UKSystemSound*)clientData release]; | ||
} | ||
|
||
|
||
@implementation UKSystemSound | ||
|
||
-(id) initWithContentsOfURL: (NSURL*)fileURL byReference: (BOOL)ignored | ||
{ | ||
self = [super init]; | ||
if( self ) | ||
{ | ||
OSStatus err = AudioServicesCreateSystemSoundID( (CFURLRef) fileURL, &systemSoundID ); | ||
if( err != noErr ) | ||
{ | ||
[self autorelease]; | ||
return nil; | ||
} | ||
else | ||
soundIDValid = YES; | ||
} | ||
return self; | ||
} | ||
|
||
-(void) dealloc | ||
{ | ||
if( soundIDValid ) | ||
{ | ||
AudioServicesDisposeSystemSoundID( systemSoundID ); | ||
soundIDValid = NO; | ||
} | ||
|
||
[super dealloc]; | ||
} | ||
|
||
|
||
@synthesize delegate; | ||
|
||
-(void) play | ||
{ | ||
if( soundIDValid ) | ||
{ | ||
AudioServicesPlaySystemSound( systemSoundID ); | ||
[self retain]; | ||
AudioServicesAddSystemSoundCompletion( systemSoundID, CFRunLoopGetCurrent(), NULL, | ||
UKSystemSoundAudioServicesSystemSoundCompletionProc, self ); | ||
} | ||
} | ||
|
||
@end |