AudioServicesAddSystemSoundCompletion callback not getting called in silent mode - iphone

If I put device into silent mode using switch, AudioServicesAddSystemSoundCompletion callback method doesn't get called. If the switch is on, I mean if device is NOT in silent mode, method gets called perfectly.
Has anyone experienced something like this. Is this a bug?

I had the same problem. It is not a bug. If the sound is not played, due to the device being muted the call back never gets called.
A work around is to use an NSTimer that is the same length as the sound to be played. If the sound doesn't play the timer call back gets called. Which can perform the same code as your callback.

Here is how you can use the NSTimer for soundDidFinishPlaying callback even in Silent Mode.
- (IBAction)playSelectedSound:(id)sender {
if (!self.isPlaying)
{
// playing the sound
NSString *fileName = [soundsFileNames objectAtIndex:self.selectedIndex];
SystemSoundID topClick;
NSBundle *bundle = [NSBundle mainBundle];
NSString *topClikFile = [bundle pathForResource:fileName ofType:#"aiff"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL URLWithString:topClikFile], &topClick);
AudioServicesPlaySystemSound(topClick);
// getting the file duration
AudioFileID audioFileID;
AudioFileOpenURL((__bridge CFURLRef)[NSURL URLWithString:topClikFile], kAudioFileReadPermission, 0, &audioFileID);
NSTimeInterval seconds;
UInt32 propertySize = sizeof(seconds);
OSStatus st = AudioFileGetProperty(audioFileID, kAudioFilePropertyEstimatedDuration, &propertySize, &seconds);
// fire the timer
if (st == 0)
{
[NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:#selector(soundDidFinishPlaying) userInfo:nil repeats:NO];
}
self.isPlaying = YES;
}
}
- (void)soundDidFinishPlaying {
self.isPlaying = NO;
}

The source code for Sound Switch (with demo project) indicates that the callback does in fact occur also if the device is in silent mode.
I've just tried this with iOS 8.1 on an iPhone 5 and silent mode turned off/on with the button on the device.

Related

AudioServicesPlaySystemSound sound not audible when called from IBAction method

I'm having a problem with AudioServicesPlaySystemSound in my iOS app.
I have a method defined to play a system sound. When I call the method from viewDidLoad, I can hear the sound play, but when I call it from a button handler, I do not hear the sound play.
Here's the code from my view controller:
SystemSoundID startSound = 0;
-(void)playStartSound
{
AudioServicesPlaySystemSound(startSound);
}
-(void)viewDidLoad
{
NSURL *urlStart = [[NSBundle mainBundle] URLForResource:#"Beep" withExtension:#"wav"];
AudioServicesCreateSystemSoundID((CFURLRef)urlStart, &startSound);
[self playStartSound];
[super viewDidLoad];
}
- (IBAction)start:(id)sender
{
[self playStartSound];
}
The IBAction method is associated with a button. When I select the button, the playStartSound method gets called, but the sound is not audible.
The system sound is not disposed of with AudioServicesDisposeSystemSoundID until the dealloc method is called.
I've tried calling AudioServicesCreateSystemSoundID from within playStartSound, but it didn't make any difference.
The app also uses AV Foundation, Media Player, Core Media, Core Video, and Core Location. I'm wondering if one of these might be interfering with AudioToolbox?
UPDATE: I just put together a simple app that just contains the above code and a button and the sound plays just fine when I select the button.
So then I removed all the video capture code from my controller and now the audio plays fine.
This was not working because I had an active video capture session in my application.
AudioServicesPlaySystemSound will not play when there is an active video capture session.
NSString *soundPath = [[NSBundle mainBundle] pathForResource:#"7777" ofType:#"mp3"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)([NSURL fileURLWithPath: soundPath]), &soundID);
AudioServicesPlaySystemSound (soundID);
Hey try like that it will play

kAudioSessionOverrideAudioRoute_Speaker issue

i need to trigger the iphone audio route back and forth from speaker to normal mode very quickly. I've created an audio Session similar to this site:
http://atastypixel.com/blog/using-remoteio-audio-unit/
Im creating a app that can do VOIP.
So let me explain the simple issue i have. I have a button that toggles from speaker to normal mode like this:
if(speakerState){
value = kAudioSessionOverrideAudioRoute_None;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerState = false;
}
else {
value = kAudioSessionOverrideAudioRoute_Speaker;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerState = true;
}
... you get the idea just toggling the speaker on and off.
Here is the issue: When the end user RAPIDLY hits the button to toggle the speaker after about 10 presses the application freezes for a while as if its releasing something and after 30 seconds comes back. I need the end user to be able to constantly tap this button on/off as much as they want.
update: I tried calling the speakers in the background thread but still if i press the speakers too many times it freezes or i loose audio here is the code:
-(void) manageSpeakerState{
OSStatus error;
UInt32 value;
if(speakerState){
value = kAudioSessionOverrideAudioRoute_None;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerBtn.selected = false;
speakerState = false;
}
else {
value = kAudioSessionOverrideAudioRoute_Speaker;
error = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(value), &value);
speakerBtn.selected = true;
speakerState = true;
}
}
// this gets called whenever the speaker button is pressed ..it toggles the speakers on/off
-(IBAction) speaker
{
[self performSelectorInBackground:#selector(manageSpeakerState) withObject:nil ];
}
UPDATE: My audio session is running on a seperate thread using pthread and its in a .c file. It could be a concurrency issue as it seems when the main thread gets tied up this issue occurs. How can i update the audio route on the thread that the audio session is running on ?
UPDATE: i have the same issue as this: http://lists.apple.com/archives/coreaudio-api/2012/Jul/msg00129.html
Whenever i change routes (ie. speaker or headphone) my audioUnits are paused so im reading empty stuff into the buffers. Its almost like a hardware manufacturer issue. How can i get around this ? the call backs are still being called but theres no audio unit while switching routes.
On button click just change button's background image and tag.Change audio session property in background thread.
In your application delegate just add this piece of code..I hope it work fine.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];

AVAudioPlayer - Muting button

I have searched for this answer and have not found it.
I have music that plays in the background when my iPhone app is launched. But I want a button so that users can mute the music. There are sound effects within the app also so sliding the mute button on the side of the device won't cut it.
This is the current code I have for the AVAudioPlayer.
- (void)viewDidLoad{
#if TARGET_IPHONE_SIMULATOR
//here code for use when execute in simulator
#else
//in real iphone
NSString *path = [[NSBundle mainBundle] pathForResource:#"FUNKYMUSIC" ofType:#"mp3"];
AVAudioPlayer *TheAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
TheAudio.delegate = self;
[TheAudio play];
TheAudio.numberOfLoops = -1;
#endif
}
Can anyone help me out the code needed for a simple button to simply stop the music and start it again.
Thanks in advanced.
Put this code in the viewcontroller.h file:
-(IBAction) btnStop:(id)sender;
Put this code in the viewcontroller.m file:
-(IBAction) btnStop:(id)sender {
[TheAudio stop];
//Whatever else you want to do when the audio is stopped
}
In the Interface builder, connect a button to this action, so when it is clicked this action will be called.
That should make the music stop.
Its easier to display code in an answer:
-(IBAction) playerPlay:(id)sender {
if([player isPlaying]) {
[player stop];
}
if(![player isPlaying]) {
[player play];
}
}
I'll explain:
The [player isPlaying] method checks to see if the audio is playing. If the audio is playing, everything in the brackets is executed (in this situation, the audio stops playing).
Because of the "!" in ![player isPlaying], the method is made the opposite of what it usually is. This means that if the player is NOT playing, everything in the brackets is executed (in this situation, the audio starts playing).
All of this is enclosed in the IBAction, so that it is executed when the button is clicked.
For future reference, the correct format for an If statement in Objective-C is:
if(thing to check for) {
things that happen if the thing that is check for is correct;
}
The word "then" is never actually used, but it is the same thing and whatever is in the brackets.
Hope this helps!

Play sound with screen turned off / don't let iPhone go to sleep

So I have an application and I want to keep it working even if the screen is turned off.
Previously when I wanted to do that I used this hack/trick - I play a silent/empty sound in a loop in the background (AudioServicesPlaySystemSound) so if user presses the on/off button the application still works in the background - so it never allow iPhone to go to sleep mode - it just turned off the screen and maybe things like wifi or bluetooth (and on the iPod Touch accelerometers as far as I remember). And it worked. I wanted to use the same trick in my new application but when I was testing it now it seems it doesn't work anymore. The sound in the background plays (when I replace "empty" audio file with some sound I can hear it play) even with screen turned off but the sound it should play (using AVAudioPlayer) doesn't play (even when I turn the screen on again).
I don't know at which point it stopped working (it worked on 3.x OS for sure). Am I doing something wrong? Did Apple changed/fixed the "hack" that allowed your app to work even with screen turned off? Is there another way to allow the device to go to sleep (and drain the battery less) but continue to work?
This is the code I use to play background/silent sound:
-(void) playSilentSound
{
CFBundleRef mainBundle = CFBundleGetMainBundle ();
CFURLRef silentUrl = CFBundleCopyResourceURL (mainBundle, CFSTR ("silence"), CFSTR ("aiff"), NULL);
AudioServicesCreateSystemSoundID (silentUrl, &silentSound);
silentTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 target: self selector:#selector(playSilence) userInfo: nil repeats: YES];
}
-(void) playSilence
{
AudioServicesPlaySystemSound (silentSound);
}
And this is how I play the sound that should play even if the screen is turned off:
-(BOOL) playSound: (NSString *) path withLoops: (BOOL) loops stopAfter: (int) seconds
{
NSError *error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath: path] error: &error];
player.delegate = self;
player.numberOfLoops = 0;
player.volume = volume;
secondsPlayed = 0;
loop = loops;
BOOL played = [player play];
if(played && seconds > 0)
{
timer = [[NSTimer scheduledTimerWithTimeInterval: 0.5 target: self selector: #selector(stopPlaying:) userInfo: [NSNumber numberWithInt: seconds] repeats: YES] retain];
secondsLimit = seconds;
} else {
secondsLimit = -1;
}
}
-(void) stopPlaying:(NSTimer*)theTimer
{
if(secondsLimit > 0 && (secondsPlayed + [player currentTime]) >= secondsLimit)
{
[player stop];
[timer release];
timer = nil;
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)sender successfully:(BOOL)flag
{
if(sender == player)
{
if(flag) { secondsPlayed += sender.duration; }
if(loop) { [player play]; }
}
}
The code is a bit complicated maybe - it could be just the first few lines - it's made that way so you can play only X seconds of sound (if sound is shorter than X it will play few loops until the total time is >= X). And of course everything is working fine when the screen is left on.
Also - if you find the code useful in your projects (like playSound:withLoops:stopAfter:) - feel free to use it (but it would be cool if you send me a message so I would know that I helped :)).
So the answer is to set session category. Some categories just turn off sound when the device is being locked. For me the best option was AVAudioSessionCategoryPlayback
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
I don't know about Apple fixing stuff, but in iOS 4 there are now official ways to continue doing things in the background which you should take advantage of.

iphone, how to refresh the gui?

i'm coding a small app for the iphone (just for fun)
what i want:
if i press the "update" button:
send something to the server
parse the answer of the server
update the content of some labels on the screen
show the answer
play a system sound //using the audio toolbox
sleep some secods, just enough to finish the previos system sound call
do other stuff
end
actually, everything works but... for some reason, the label content is updated at the end of the method / callback / function that is called when pressed the "update" button.
what am i doing wrong?
thanks!
some 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);
}
- (IBAction)update: (id) sender{
BOOL error=[self sendContent];
if ( error == NO ){
result.text=[self parseContent];
[self playSound:#"ready"];
sleep(4);
....
}
// here is updated the gui
}
The GUI is displayed by the runloop. The loop will not reach its next iteration until your method is done executing. Therefore, your method blocks the view from updating. Either use a background thread or a timer.
You don't want to use sleep there. Sleep stops the whole process from continuing that's why your label gets updated only at the end of the function. I'd recommend either using NSTimer or performSelector:withObject:afterDelay: (see here).
This is because you have blocked the main thread when you sleep. UI can only be drawn on the main thread.
A solution is to use [NSTimer scheduledTimerWithTimeInterval:X_SEC target:self selector:#selector(doOtherStuff) userInfo:nil repeats:NO] in place of sleep and do other stuff.
What about performing the [self sendContent] call in a separate thread?