Skip to content

Commit

Permalink
Initial check-in.
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 4 changed files with 363 additions and 0 deletions.
52 changes: 52 additions & 0 deletions UKSound.h
@@ -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
204 changes: 204 additions & 0 deletions UKSound.m
@@ -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
42 changes: 42 additions & 0 deletions UKSystemSound.h
@@ -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
65 changes: 65 additions & 0 deletions UKSystemSound.m
@@ -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

0 comments on commit 5d77434

Please sign in to comment.