Permalink
Browse files

Initial check-in.

git-svn-id: svn://witness.is-a-geek.org/svn@1 378c4bed-2673-4746-83ae-d22ddc8c5b7c
  • Loading branch information...
0 parents commit 5d77434263ebf8cff90d2df19a1d926643f9fc23 uli committed Jul 4, 2009
Showing with 363 additions and 0 deletions.
  1. +52 −0 UKSound.h
  2. +204 −0 UKSound.m
  3. +42 −0 UKSystemSound.h
  4. +65 −0 UKSystemSound.m
@@ -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
@@ -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
@@ -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
@@ -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.