I'm having trouble using AudioServicesPlaySystemSound. Things work great when output goes through the speakers. However, when a user plugs in headphones, there is no output. Is there an easy way to set up some kind of listener so that audio is automatically routed through the headphones when they are plugged in, and otherwise through the speaker?
I'm using the following method to play short, simple AIF sound samples:
-(void)playAif:(NSString *)filename {
SystemSoundID soundID;
NSString *path = [[NSBundle mainBundle]
pathForResource:filename ofType:#"aif"];
if (path) { // test for path, to guard against crashes
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path],&soundID);
AudioServicesPlaySystemSound (soundID);
}
}
I know I must be missing something, some bit of setup that would do this. Any ideas?
Thanks #Till for pointing me to the relevant portion of the docs. For others with this issue, the solution is to explicitly set the session category, in my case to ambient sound. This code snipped from apple's docs:
UInt32 sessionCategory = kAudioSessionCategory_AmbientSound; // 1
AudioSessionSetProperty (
kAudioSessionProperty_AudioCategory, // 2
sizeof (sessionCategory), // 3
&sessionCategory // 4
);
So, my method for playing audio now looks like this:
-(void)playAif:(NSString *)filename {
// NSLog(#"play: %#", filename);
SystemSoundID soundID;
NSString *path = [[NSBundle mainBundle]
pathForResource:filename ofType:#"aif"];
if (path) { // test for path, to guard against crashes
UInt32 sessionCategory = kAudioSessionCategory_AmbientSound; // 1
AudioSessionSetProperty (
kAudioSessionProperty_AudioCategory, // 2
sizeof (sessionCategory), // 3
&sessionCategory // 4
);
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path],&soundID);
AudioServicesPlaySystemSound (soundID);
}
}
This solves my problem handily! The only worry I have is that setting it explicitly every single time I play a sound might be excessive. Anyone know a better and safer way to set it and forget it? Otherwise, this works delightfully.
Related
I created this method to easily play sounds in an XCode iPhone application.
void playSound(NSString* myString) {
CFBundleRef mainBundle = CFBundleGetMainBundle();
NSString *string = myString;
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (__bridge CFStringRef) string, CFSTR ("wav"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
}
The problem with this is, I don't know how to later stop the sound or to make the sound play on a loop forever until it is stopped. How would I go about doing this? Could I possibly make the method return the sound, and then put that in a variable to later be modified?
I'm afraid i dont know how to stop it, but try this for looping:
soundFile = [NSURL fileURLWithPath: [[NSBundle mainBundle] pathForResource:#"AudioName" ofType:#"wav"]];
sound = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFile error:nil];
sound.numberOfLoops = -1; //infinite
[sound play];
From documentation: "The interface does not provide level, positioning, looping, or timing control, and does not support simultaneous playback...".
http://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/SystemSoundServicesReference/Reference/reference
So for sound playback it is better use AVFoundation classes.
System Sound Services usually is used to play short sounds/alerts/etc and pausing/stopping probably should be avoided. If this is really needed you could try AudioServicesDisposeSystemSoundID(systemSoundID) to stop the sound.
As for looping you could create recursive function to loop the sound using AudioServicesPlaySystemSoundWithCompletion.
Example (swift 3):
func playSystemSound(systemSoundID: SystemSoundID, count: Int){
AudioServicesPlaySystemSoundWithCompletion(systemSoundID) {
if count > 0 {
self.playSystemSound(systemSoundID: systemSoundID, count: count - 1)
}
}
}
So in your example you would just replace AudioServicesPlaySystemSound(soundID); with playSystemSound(systemSoundID: soundID, count: numOfPlays)
In case if you want to decide to play a sound based on a bool variable,
private func playSound() {
if isStarted {
AudioServicesPlaySystemSoundWithCompletion(kSystemSoundID_Vibrate, {
if self.isStarted {
self.playSound()
}
})
}
}
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.
we are trying to use remoteio for audio recording in conjunction
with the AudioServicesPlaySystemSound function for audio playback. the problem is that whenever remoteio is running the playback volume drops
significantly. it seems like if some final mixing takes place behind the scene but we do not no how to change this behavior.
the implementation of the remoteio is based on the fallowing blog
http://atastypixel.com/blog/using-remoteio-audio-unit/
for audio playback we just use code like this one
NSString *sndPath = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"wav" inDirectory:#"/"];
CFURLRef sndURL = (CFURLRef)[[NSURL alloc] initFileURLWithPath:sndPath];
SystemSoundID soundID;
int e = AudioServicesCreateSystemSoundID(sndURL, &soundID);
if (e) {
NSLog(#"couldn't create sound");
exit(0);
}
AudioServicesPlaySystemSound(soundID);
thanks a lot for any help
You may have to disable the voice processing AGC.
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.
Here is the code:
-(void)stop
{
NSLog(#"Disposing Sounds");
AudioServicesDisposeSystemSoundID (soundID);
//AudioServicesRemoveSystemSoundCompletion (soundID);
}
static void completionCallback (SystemSoundID mySSID, void* myself) {
NSLog(#"completion Callback");
}
- (void) playall: (id) sender {
[self stop];
AudioServicesAddSystemSoundCompletion (soundID,NULL,NULL,
completionCallback,
(void*) self);
OSStatus err = kAudioServicesNoError;
NSString *aiffPath = [[NSBundle mainBundle] pathForResource:#"slide1" ofType:#"m4a"];
NSURL *aiffURL = [NSURL fileURLWithPath:aiffPath];
err = AudioServicesCreateSystemSoundID((CFURLRef) aiffURL, &soundID);
AudioServicesPlaySystemSound (soundID);
NSLog(#"Done Playing");
}
Output:
Disposing Sounds
Done Playing
In actual no sound gets play at all and completion call back isn't called as well. Any idea what could be wrong here?
I want to stop any previous sound before playing current.
AFAIK, the only supported files are .caf, .aif, or .wav:
To play your own sounds, add the sound
file to your application bundle; the
sound file must adhere to the
following requirements:
Must be .caf, .aif, or .wav files.
The audio data in the file must be in PCM or IMA/ADPCM (IMA4) format.
The file's audio duration must be less than 30 seconds.
Audio & Video Coding How-To's
What does err contain? From that you should be able to infer the problem.
You can use AudioToolBox framework for this:
CFBundleRef mainbundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef = CFBundleCopyResourceURL(mainbundle, CFSTR("tap"), CFSTR("aif"), NULL);
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundFileObject);
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
AudioServicesPlaySystemSound(1100);