Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Development

Reply
 
LinkBack Thread Tools Display Modes
Old 09-30-2008, 02:13 PM   #1 (permalink)
New Member
 
Join Date: Aug 2008
Posts: 17
Zarianna is on a distinguished road
Default How to Detect System Sound Completion?

Hello all,

I'm quite new to the iPhone platform and even more new to the concept of Audio Services.

I am able to play a system sound but if the play function is called many times, the sound will restart and try to play again. I'd like to have the play function wait until the first sound is finished before playing again.

I've looked over the iPhone Dev Reference Guide on Playing System Sound and they outline a function that will keep track if a sound is completed but I am not sure how to implement this. There is a function called "AudioServicesSystemSoundCompletionProc" which is invoked as a callback. I've looked everywhere for an example how to use this function but I have not been able to find it it any sample code (neither metronome, GLPaint nor BubbleLevel) and if anyone out there can point me how I could use this function, I would greatly appreciate it.

I have added a listener with AudioServicesSystemSoundCompleted. I am unclear how to use this in conjunction with the "AudioServicesSystemSoundCompletionProc" method if at all. :x

Any help is appreciated.
Zarianna is offline   Reply With Quote
Old 09-30-2008, 10:25 PM   #2 (permalink)
Registered Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
smasher will become famous soon enough
Default You have to pass a function in.

To tell when a sound is finished playing, do something like this:

Code:
-(void)playAndRelease{
	AudioServicesAddSystemSoundCompletion (_soundID,NULL,NULL,
		completionCallback,
		(void*) self);
	AudioServicesPlaySystemSound(_soundID);
}

static void completionCallback (SystemSoundID  mySSID, void* myself) {
	//NSLog(@"completion Callback");
	AudioServicesRemoveSystemSoundCompletion (mySSID);
	
	[(SoundEffect*)myself release];
}
You're wrting the function "completionCallback", and then passing it and the current object into "AudioServicesAddSystemSoundCompletion" .

When the sound is done, it will call your function (a regular C function, not an objective-C method) with your object as a parameter. You can then cast that object back to whatever you want, and call methods on it. I'm catsing it back to a (SoundEffect*) , one of my custom classes.
smasher is offline   Reply With Quote
Old 10-15-2008, 11:02 AM   #3 (permalink)
New Member
 
Join Date: Jul 2008
Location: SLC, UT
Posts: 13
sclyde is on a distinguished road
Default

Quote:
Originally Posted by smasher View Post
To tell when a sound is finished playing, do something like this:

Code:
-(void)playAndRelease{
	AudioServicesAddSystemSoundCompletion (_soundID,NULL,NULL,
		completionCallback,
		(void*) self);
	AudioServicesPlaySystemSound(_soundID);
}

static void completionCallback (SystemSoundID  mySSID, void* myself) {
	//NSLog(@"completion Callback");
	AudioServicesRemoveSystemSoundCompletion (mySSID);
	
	[(SoundEffect*)myself release];
}
You're wrting the function "completionCallback", and then passing it and the current object into "AudioServicesAddSystemSoundCompletion" .

When the sound is done, it will call your function (a regular C function, not an objective-C method) with your object as a parameter. You can then cast that object back to whatever you want, and call methods on it. I'm catsing it back to a (SoundEffect*) , one of my custom classes.
I'm using this callback almost exactly as written above and it is never reaching the callback. Is it a problem that my sounds are very short? Some are less than a second anywhere up to about 3 seconds.

Basically I'm trying to play a collection of sounds all in a row one after the other - but they all play at the same time, so I want to use something to wait until the end of each sound to play the next - I figured this callback might do the job, but it seems to never get called for me.

Any thoughts?
sclyde is offline   Reply With Quote
Old 10-17-2008, 10:12 AM   #4 (permalink)
New Member
 
Join Date: Jul 2008
Location: SLC, UT
Posts: 13
sclyde is on a distinguished road
Default

Just wanted to give this a quick bump - anyone have any ideas? I can't think of anything that would be causing the callback to not be hit.

Thanks for any help.
sclyde is offline   Reply With Quote
Old 10-17-2008, 05:34 PM   #5 (permalink)
Registered Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
smasher will become famous soon enough
Default

Quote:
Originally Posted by sclyde View Post
Just wanted to give this a quick bump - anyone have any ideas? I can't think of anything that would be causing the callback to not be hit.

Thanks for any help.
I'll look at your code if you post it. Did you try an NSLog inside your callback function, to see if it's being called at all?
__________________

Free Games!
smasher is offline   Reply With Quote
Old 10-20-2008, 02:16 PM   #6 (permalink)
New Member
 
Join Date: Jul 2008
Location: SLC, UT
Posts: 13
sclyde is on a distinguished road
Default

I'll have to post my code when I get home. I don't have any NSLogs in there, but I had break points galore, none of which ever got hit - and of course what it was supposed to do never happened.
sclyde is offline   Reply With Quote
Old 10-28-2008, 06:31 PM   #7 (permalink)
New Member
 
Join Date: Jul 2008
Location: SLC, UT
Posts: 13
sclyde is on a distinguished road
Default

Ok, I kept forgetting to post my code - here it is still not working:

Code:
void MyAudioServicesSystemSoundCompletionProc(SystemSoundID ssID, void *clientData) {
	[((MyAppDelegate*)clientData) setSoundPlaying:NO];
}

- (void)playWavNamed:(NSString*)audioFile {
	AudioServicesDisposeSystemSoundID(lastSoundID);
	[self setSoundPlaying:YES]; // self happens to be MyAppDelegate
	
	SystemSoundID soundID;
	NSString *filePath = [[NSBundle mainBundle] pathForResource:audioFile ofType:@"wav" inDirectory:@""];
	NSURL *fileUrl = [NSURL fileURLWithPath:filePath isDirectory:NO];
	AudioServicesCreateSystemSoundID((CFURLRef)fileUrl, &soundID);
	AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, MyAudioServicesSystemSoundCompletionProc, self);
	AudioServicesPlaySystemSound(soundID);
	[self setLastSoundID:soundID];
}
The playWavNamed method is called in a loop, which is this:

Code:
NSMutableArray *sounds = [appDelegate getSounds];
				
				for (Sound *sound in sounds)
				{
					[appDelegate playSound:sound];
					
					while ([appDelegate soundPlaying]) {}
				}
The idea is to wait until each sound is done playing before continuing the loop into playing the next sound. All this currently does it get locked up in the while loop all the while MyAudioServicesSystemSoundCompletionProc never gets called - so soundPlaying is always true.
sclyde is offline   Reply With Quote
Old 11-14-2008, 10:17 AM   #8 (permalink)
Registered Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
smasher will become famous soon enough
Default

Your "While" loop is the problem there - you can't hog the processor like that; that will prevent the systemSoundCompletionProc from being called - it'll also keep any touch events from being called.

I'd get rid of the while loop AND the for loop, and change your systemSoundCompletionProc - instead of calling setSoundPlaying:NO, write a method that plays the next sound, and call that instead. (You'll need an int to keep tack of which sound is next, etc.

Hope that helps.
__________________

Free Games!
smasher is offline   Reply With Quote
Old 12-02-2009, 03:13 PM   #9 (permalink)
Registered Member
 
Join Date: Dec 2009
Posts: 64
Lukapple80 is on a distinguished road
Default

Hi!
I've same problem. Trying to make sound queue with audio toolbox.

soundQueue.h :
Code:
@interface soundQueue : NSObject {
	NSMutableArray *sndQueue; 
...
-(void)initWithArray:(NSMutableArray *)arraySound;
-(void)addSoundToQueue:(NSString*)snd;
-(NSString*)getSoundFromQueue; // get next sound from queue
-(void)playQueue;
then I try to play queue of sounds, one sound after another...

soundQueue.m:
Code:
void MyAudioServicesSystemSoundCompletionProc(SystemSoundID ssID, void *clientData)
{
	soundQueue* pSoundQueue = clientData;
//I DON'T KNOW HOW TO START NEXT SONG FROM HERE, How can I call "playQueue" from this procedure ?
//Something like:
  [pSoundQueue playQueue]; // ???
}



-(void)playSnd:(NSString*) snd
{
	NSString *path = [[NSBundle mainBundle] pathForResource:snd ofType:@"wav"];
	SystemSoundID soundID;
	AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path], &soundID);
	
	AudioServicesAddSystemSoundCompletion (soundID,NULL,NULL,MyAudioServicesSystemSoundCompletionProc,self);
	AudioServicesPlaySystemSound (soundID);	
	
}

-(void)playQueue
{
	NSString *snd = [self getSoundFromQueue];
	if (snd != nil)
	{
		[self playSnd:snd];
	}
}
Please help.
Lukapple80 is offline   Reply With Quote
Old 12-02-2009, 04:47 PM   #10 (permalink)
Registered Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
smasher will become famous soon enough
Default

That looks like it should work - you're passing self into AudioServicesAddSystemSoundCompletion; "self" is the sound queue, right?

Then you get it back as clientData when the sound is complete in MyAudioServicesSystemSoundCompletionProc and you call playQueue. You should probably also destroy the system sounds ID there.

Everything looks shipshape - what's not working? Put NSlogs in MyAudioServicesSystemSoundCompletionProc and playQueue and see if they're being run.
__________________

Free Games!

Last edited by smasher; 12-02-2009 at 05:02 PM.
smasher is offline   Reply With Quote
Old 12-03-2009, 12:47 AM   #11 (permalink)
Registered Member
 
Join Date: Dec 2009
Posts: 64
Lukapple80 is on a distinguished road
Default

Hi,


Code:
OSStatus AudioServicesAddSystemSoundCompletion (
   SystemSoundID                           inSystemSoundID,
   CFRunLoopRef                            inRunLoop,
   CFStringRef                             inRunLoopMode,
   AudioServicesSystemSoundCompletionProc  inCompletionRoutine,
 void                                    *inClientData
);
I want to pass whole object into "inClientData"...
Sounds are stored in soundQueue.h instance variable:
Code:
	NSMutableArray *sndQueue;
then I want to call object method, that removes&returns next song from sound queue (NSMutableArray *sndQueue).
It hangs here:
Code:
[pSoundQueue playQueue];
I'll post logs when I'm back from work.

Regards,
L
Lukapple80 is offline   Reply With Quote
Old 12-03-2009, 11:39 AM   #12 (permalink)
Registered Member
 
Join Date: Dec 2009
Posts: 64
Lukapple80 is on a distinguished road
Default

Here is my class.

soundQueue.h:
Code:
#import <Foundation/Foundation.h>

@interface soundQueue : NSObject {
	NSMutableArray *sndQueue;
}
@property(nonatomic, retain) NSMutableArray *sndQueue;


-(id)init;

-(void)initWithArray:(NSMutableArray *)arraySound;

-(void)addSoundToQueue:(NSString*)snd;

-(NSString*)getSoundFromQueue;

-(void)playQueue;

@end
soundQueue.m:

Code:
#import "soundQueue.h"
#import <AudioToolbox/AudioToolbox.h>


@implementation soundQueue
@synthesize sndQueue;

-(id)init
{
	if (self = [super init])
	{
		sndQueue = [[NSMutableArray alloc] init];
	}
	return self;
}

-(void)initWithArray:(NSMutableArray *)arraySound
{	
	[self init];
	for (NSString* snd in arraySound) 
	{
		[self addSoundToQueue:snd];
	}
}

-(void)addSoundToQueue:(NSString*)snd
{
	[sndQueue addObject:snd];
}

-(NSString*)getSoundFromQueue
{
	NSString* snd;
	snd = [sndQueue objectAtIndex:0]; //1st sound
	[sndQueue removeObjectAtIndex:0]; 
	return snd;
}

void MyAudioServicesSystemSoundCompletionProc(SystemSoundID ssID, void *clientData)
{
	//cleanup
	AudioServicesDisposeSystemSoundID(ssID);
	soundQueue* pSoundQueue = clientData;
	[pSoundQueue playQueue]; //it crashes here: EXC_BAD_ACCESS
}

-(void)playQueue
{
    NSString *nextSound = [self getSoundFromQueue];
	if (nextSound != NULL) { 
		NSString *path = [[NSBundle mainBundle] pathForResource:nextSound ofType:@"wav"];
		SystemSoundID soundID;
		AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path], &soundID);
	
		AudioServicesAddSystemSoundCompletion (soundID,NULL,NULL,MyAudioServicesSystemSoundCompletionProc, /*(void*)*/ self);
		AudioServicesPlaySystemSound (soundID);	
	}
	
}

@end
Lukapple80 is offline   Reply With Quote
Old 12-03-2009, 12:15 PM   #13 (permalink)
Registered Member
iPhone Dev SDK Supporter
 
smasher's Avatar
 
Join Date: Jul 2008
Location: San Mateo, CA (San Fran)
Posts: 3,858
smasher will become famous soon enough
Default

I can't spot the error yet. Can you show the block of code when you create your soundQueue? You don't release it, do you?

PS objectAtIndex won't return nil when there's no more objects, it'll cause an error. Check the length of the array before accessing object 0.

PPS - your initWithArray method is strange - it shares a name with another method of a different type, which causes a warning, and any init method should return self. Right now you probably init twice, right?

Here's a better initWithArray:

Code:
-(id)initWithArray:(NSMutableArray *)arraySound
{	
	self = [self init];
	if (self!=nil){
		[sndQueue addObjectsFromArray:arraySound];
	}
	
	return self;
}
When I make that change your code works for me - except for the exception when the queue runs dry (see PS above).
__________________

Free Games!

Last edited by smasher; 12-03-2009 at 12:37 PM.
smasher is offline   Reply With Quote
Old 12-03-2009, 01:13 PM   #14 (permalink)
Registered Member
 
Join Date: Dec 2009
Posts: 64
Lukapple80 is on a distinguished road
Default

Thanks a lot for your help! it works like a charm now. I released object too soon.
Lukapple80 is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 323
14 members and 309 guests
chemistry, Domele, Fstuff, givensur, heshiming, HowEver, iAppDeveloper, iphonedevshani, jbro, JoeRCruso, kapps11, newDev, SLIC, stanny
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,648
Threads: 94,112
Posts: 402,873
Top Poster: BrianSlick (7,990)
Welcome to our newest member, brandon6031
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 07:25 PM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0