i'm new with the iphone and objective c, so far, I have been able to code some small examples.
i would like to play a sound and, continue with rest of the code when the sample finishes playing, i.e:
printf("hello");
playASound["file.wav"];
printf("world");
actually i'm getting: print hello, play the file and print world at the same time
but
what i want is: print hello, play the file, print world...
so, the question is how do i get it?
thanks
btw. here is the playASound code:
-(void) playASound: (NSString *) file {
//Get the filename of the sound file:
NSString *path = [NSString stringWithFormat:#"%#/%#",
[[NSBundle mainBundle] resourcePath],
file];
SystemSoundID soundID;
//Get a URL for the sound file
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
//play the file
AudioServicesPlaySystemSound(soundID);
}
From the documentation:
Discussion This function plays a short
sound (30 seconds or less in
duration). Because sound might play
for several seconds, this function is
executed asynchronously. To know when
a sound has finished playing, call the
AudioServicesAddSystemSoundCompletion
function to register a callback
function.
So you'll need to break up your function into two pieces: a function that calls PlayASound and prints "Hello", and a function that is called by the system when the sound is finished playing and prints "World".
// Change PlayASound to return the SystemSoundID it created
-(SystemSoundID) playASound: (NSString *) file {
//Get the filename of the sound file:
NSString *path = [NSString stringWithFormat:#"%#/%#",
[[NSBundle mainBundle] resourcePath],
file];
SystemSoundID soundID;
//Get a URL for the sound file
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
//play the file
AudioServicesPlaySystemSound(soundID);
return soundID;
}
-(void)startSound
{
printf("Hello");
SystemSoundID id = [self playASound:#"file.wav"];
AudioServicesAddSystemSoundCompletion (
id,
NULL,
NULL,
endSound,
NULL
);
}
void endSound (
SystemSoundID ssID,
void *clientData
)
{
printf("world\n");
}
See also docs for AudioServicesAddSystemSoundCompletion and AudioServicesSystemSoundCompletionProc.
Related
I'm trying to play an audio file in the background of my app and it isn't playing. The code that I have is:
SystemSoundID SoundID;
NSString *soundFile = [[NSBundle mainBundle] pathForResource:#"HoHey" ofType:#"mp3"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:soundFile], &SoundID);
AudioServicesPlaySystemSound(SoundID);
NSLog(#"%# is playing\n", soundFile);
The NSLog displays "HoHey.mp3 is playing" but I can't hear it playing. My volume is turned up btw because I hear my videos play when I play them. Any ideas?
You likely have an error somewhere along the way. Try code that looks like this:
SystemSoundID soundID;
NSString *soundFile = [[NSBundle mainBundle] pathForResource:#"HoHey" ofType:#"mp3"];
if(soundFile)
{
OSStatus status = AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:soundFile], &soundID);
if(status == noErr)
{
AudioServicesPlaySystemSound(soundID);
NSLog(#"%# is playing\n", soundFile);
} else {
NSLog( #"no sound id created; error status code is %d", status);
}
} else {
NSLog( #"couldn't find sound file... is it really in your app bundle?");
}
P.S., don't capitalize Objective-C variable names (e.g. "soundID" instead of "SoundID").
I am trying to play a .wav file using Audio Toolbox. I have this so far:
However my file will not play. The If condition is triggered right, however it doesn't play.
It will print the error message if the file isn't there however. Any clue what is wrong?
-(void) playSound : (NSString *) fName : (NSString *) ext
{
SystemSoundID audioEffect;
NSString *path = [[NSBundle mainBundle] pathForResource : fName ofType :ext];
if ([[NSFileManager defaultManager] fileExistsAtPath : path])
{
NSURL *pathURL = [NSURL fileURLWithPath : path];
AudioServicesCreateSystemSoundID((__bridge CFURLRef) pathURL, &audioEffect);
AudioServicesPlaySystemSound(audioEffect);
}
else
{
NSLog(#"error, file not found: %#", path);
}
AudioServicesDisposeSystemSoundID(audioEffect);;
}
You can't dispose of the sound ID immediately after telling it to play, since this causes the playback to stop. That's why you didn't hear it in the first place.
What you should do instead is hold a reference to the ID in a property on whatever object is responsible for creating it (e.g., a view controller). You'd declare it like this in your interface declaration:
#property(nonatomic, assign) SystemSoundID audioEffect;
...and subsequently assign to it like this:
AudioServicesCreateSystemSoundID((__bridge CFURLRef) pathURL, &_audioEffect);
This will not only allow you to properly dispose of it at a later time when you know you're done with it (e.g. in viewDidUnload), but will also prevent you from having to read the file in from disk each time you want to play it back, since each ID can be played an unlimited number of times.
One final hint: you can ask for the URL to a bundle resource directly instead of requesting a string and then transforming it to a URL.
NSURL *audioURL = [[NSBundle mainBundle] URLForResource:fName withExtension:ext];
if([[NSFileManager defaultManager] fileExistsAtPath:[audioURL path]])
// ...
I figured it out from Warrenm's comment. I had to delete the last line, as it was killing the sound milliseconds after it was set to play, and therefore can't be heard.
-(void) playSound : (NSString *) fName : (NSString *) ext
{
SystemSoundID audioEffect;
NSString *path = [[NSBundle mainBundle] pathForResource : fName ofType :ext];
if ([[NSFileManager defaultManager] fileExistsAtPath : path])
{
NSURL *pathURL = [NSURL fileURLWithPath : path];
AudioServicesCreateSystemSoundID((__bridge CFURLRef) pathURL, &audioEffect);
AudioServicesPlaySystemSound(audioEffect);
}
else
{
NSLog(#"error, file not found: %#", path);
}
}
I have an object moving around the screen and once it reaches the edges it it changes direction and plays a sound this all works fine except that when it plays the sound I freezes for about half a second is there any way of making this run smoothly with the sound and object movement?
-(void)viewDidLoader
{
NSString *path1 = [[NSBundle mainBundle] pathForResource:#"ballbounce" ofType:#"mp3"];
ballbounce = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path1] error: NULL];
[ballbounce prepareToPlay];
}
-(void) ballcollition
{
[self ballplaysound]
enemy.center = CGPointMake(enemy.center.x+pos.x,enemy.center.y+pos.y);
if (enemy.center.x > 328 || enemy.center.x < 0)
{
pos.x = -pos.x;
}
}
-(void)ballplaysound
{
if (enemy.center.x > 328 || enemy.center.x < 0 ||enemy.center.y < 0||enemy.center.y < 300)
[ballbounce play];
}
use the System Sound for sound that plays smoothly.
Original NON-ARC answer:
#import <AudioToolbox/AudioToolbox.h>
- (IBAction)soundButton:(id)sender {
NSString *soundPath = [[NSBundle mainBundle] pathForResource:#"alert" ofType:#"wav"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath: soundPath], &soundID);
AudioServicesPlaySystemSound (soundID);
[soundPath release];
}
Updated for ARC, and turned into a utility function:
- (void)soundPlay:(NSString*)waveName {
// wavName = #"alert" without any file extension (not alert.wav)
NSString *soundPath = [[NSBundle mainBundle] pathForResource:waveName ofType: #"wav"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath: soundPath], &soundID);
AudioServicesPlaySystemSound (soundID);
}
Use AudioServicesPlaySystemSound(): https://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/SystemSoundServicesReference/Reference/reference.html
You can gain sound "smoothness" with AVPlayer by simply converting your mp3 to caf format as explained here.
1) Create AVAudioPLayer and cache it in your apps initialization code and never call it again
also call prepare as well.
2) so the method you included should only have to call play;
3) If you still get a skip use
[ballbounce performSelectorOnMainThread:#selector(play) withObject:nil];
Since you didnt include all the code and you are clearly doing drawing - putting the play method
on the queue could let the drawing code finish without interruption.
Doing this correctly might fix you. Using AudioSystemSound is much lower overhead but you should follow the same principles of preparing any often used sounds in your app initialization and not spawning them at the exact time you need them to avoid performance issues.
Pretty simple, I'm using one button that generates a random number and then with a switch/case, it animates an image and plays a sound. My problem is that when you press the button again, the sound that was playing before doesn't stop and the sound overlaps.
Here's the code:
- (IBAction) pushme: (id) sender{
int random = (arc4random()%10);
switch (random) {
case 0: {
[ANIMATION NUMBER 0]
NSString *path = [NSString stringWithFormat:#"%#%#",[[NSBundle mainBundle] resourcePath],#"/sound0.wav"];
SystemSoundID soundID;
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
AudioServicesPlaySystemSound(soundID);
}
break;
case 1: {
[ANIMATION NUMBER 1]
NSString *path = [NSString stringWithFormat:#"%#%#",[[NSBundle mainBundle] resourcePath],#"/sound1.wav"];
SystemSoundID soundID;
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
AudioServicesPlaySystemSound(soundID);
} break; //Lots more cases after this one
I know that this is not the best way to do it, declaring the same variable over and over.
but is there a way to stop it before it plays another one?
I just dicovered the answer to my own question.
What I did was
Declare SoundSystemID soundID; in the header.
Add this line before the random number gets generated: AudioServicesDisposeSystemSoundID(soundID);
Remember that if you are going to play the sound again, you will need to load the resource again after disposing of it.
Thank you guys, anyway.
I searched on here and tried out all the different solutions given, but nothing worked. So let me ask:
I'm trying to play a sound on an iPhone app when a button is pressed. I imported the Audio framework, hooked up the button with the method, have the WAV sound file in the bundle, and use the following code to play the sound:
NSString *path = [NSString stringWithFormat:#"%#%#", [[NSBundle mainBundle] resourcePath], #"/filename.wav"];
SystemSoundID soundID;
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
AudioServicesCreateSystemSoundID((CFURLRef)filePath, &soundID);
AudioServicesPlaySystemSound(soundID);
But it doesn't play sound when I press a button. (Yes, my sound is on.)
Any ideas why this might be, and how I can fix it? I'd be happy to provide any more information if it helps.
First of all, the preferred sound format for iPhone is LE formatted CAF, or mp3 for music. You can convert a wav to caf with the built in terminal utility:
afconvert -f caff -d LEI16 crash.wav crash.caf
Then the easiest away to play a sound is to use the AVAudioPlayer... this quick function can help you load a sound resource:
- (AVAudioPlayer *) soundNamed:(NSString *)name {
NSString * path;
AVAudioPlayer * snd;
NSError * err;
path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:name];
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
NSURL * url = [NSURL fileURLWithPath:path];
snd = [[[AVAudioPlayer alloc] initWithContentsOfURL:url
error:&err] autorelease];
if (! snd) {
NSLog(#"Sound named '%#' had error %#", name, [err localizedDescription]);
} else {
[snd prepareToPlay];
}
} else {
NSLog(#"Sound file '%#' doesn't exist at '%#'", name, path);
}
return snd;
}
I don't know if you have solved the problem, but for me, on iOS 7, sometimes the (__bridge CFURLRef) casting won't work, in which case, if you print out the result of (__bridge CFURLRef)[NSURL fileURLWithPath:path], it will be 0x0. This leads to failure of AudioServicesCreateSystemSoundID(). The return value of the function is type of OSStatus. If it fails, the function will return -50, which means one or more parameters are invalid. (If it executes successfully, it returns 0.)
I solved this by using the C style function getting the path of the file:
CFBundleRef mainBundle = CFBundleGetMainBundle ();
CFURLRef soundFileUrl;
soundFileUrl = CFBundleCopyResourceURL(mainBundle, CFSTR("fileName"), CFSTR("wav"), NULL);
AudioServicesCreateSystemSoundID(soundFileUrl, &soundID);