I am working on an iPad app that connects with an accessory that plays sound. When the iPad is connected to the accessory, I would like to mute all system sounds but allow other sounds (iPod).
Part of the reason for this is that the accessory is such that it is intended to be used during a live performance. Clearly it would be annoying to have e-mail, alert, or any other system sound running through and amplified (crazy loud).
I have looked at using AVAudioSession (read Audio Sessions to learn more) and tried all of the AudioSessionCategories. None of these categories will mute the system sound, instead it will only allow you to mute application sounds (iPod) - not useful for my purposes.
I also found docs on "System Sound Services", but this only allows you to play system sounds. There is no api here to disable system sounds while your app is running.
A final note, we have made it easy to adjust the iPad level (volume) by including the MPVolumeView, but we expect the user to want to play iPod music. If while playing iPod music (or music from another app) and an e-mail comes through, you'd be amazed how LOUD / ANNOYING that e-mail suddenly becomes when going through our accessory. It's even possible it could damage equipment. :D
It is possible to change the system sounds, which turns out to be the ringer btw, using the AVSystemController. However, AVSystemController exists in the private Celestial framework. Since this framework is referenced by UIKit, it is still possible to use this class without directly referencing it.
Apple prohibits using private API's, so that alone makes this a bad idea. Given my circumstance, I think they may make an exception, BUT I will likely abandon this course since after taking it I realized that it didn't fix my problem. It does indeed mute the sounds, but as soon as I plug in to my accessory, the system sounds come out at max volume even though the ringer volume is set to 0. This leads me to believe the answer to solving my problem is in the MFI documentation.
Anyhow, here is how to change the ringer using private framework / api (which will get your app rejected without some kind of special permission).
short answer:
[[AVSystemController sharedAVSystemController] setVolumeTo:0 forCategory:#"Ringtone"];
answer without having to directly reference Celestial frameork / AVSystemController.h:
- (void) setSystemVolumeLevelTo:(float)newVolumeLevel
{
Class avSystemControllerClass = NSClassFromString(#"AVSystemController");
id avSystemControllerInstance = [avSystemControllerClass performSelector:#selector(sharedAVSystemController)];
NSString *soundCategory = #"Ringtone";
NSInvocation *volumeInvocation = [NSInvocation invocationWithMethodSignature:
[avSystemControllerClass instanceMethodSignatureForSelector:
#selector(setVolumeTo:forCategory:)]];
[volumeInvocation setTarget:avSystemControllerInstance];
[volumeInvocation setSelector:#selector(setVolumeTo:forCategory:)];
[volumeInvocation setArgument:&newVolumeLevel atIndex:2];
[volumeInvocation setArgument:&soundCategory atIndex:3];
[volumeInvocation invoke];
}
Using MediaPlayer framework, we can set the level of SYSTEM sound
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0];
Best you can do is encourage your users to go into airplane mode.
Related
Is there a way to determine whether VoiceOver is currently announcing and when it stops. I've tried UIAccessibilityVoiceOverStatusChanged but my understanding is that this is only if you switch VoiceOver on or off. Any help would be greatly appreciated. thanks.
We use otherAudioIsPlaying, the problem is some app's running in the background like some pedometer monitors etc. turn on the audio it seems and never release it so even though nothing is actually being spoken or played otherAudioIsPlaying always returns 1 until you remove the other application from the background. So now not only can you not play music but you have no idea that another application in the background will mess up this test. Apple really needs to put in an API to determine if Voice Over is currently speaking or not.
These are all the Accessibility booleans that I found in the documentation:
UIAccessibilityPostNotification
UIAccessibilityIsVoiceOverRunning
UIAccessibilityIsMonoAudioEnabled
UIAccessibilityIsClosedCaptioningEnabled
UIAccessibilityRegisterGestureConflictWithZoom
I don't think that there are any booleans to do what you are talking about.
You could use the audio session's "OtherAudioIsPlaying" property to check if another system process is using the audio hardware at the moment. It should be "true" if VoiceOver is speaking and "false" if not.
Actually this might not work properly if the user is playing music in the background. But most users running VoiceOver will usually not have any other audio enabled permanently, since it makes it harder to understand what VoiceOver is saying.
Here is an example for usage:
UInt32 otherAudioIsPlaying;
UInt32 propertySize = sizeof(otherAudioIsPlaying);
AudioSessionGetProperty(kAudioSessionProperty_OtherAudioIsPlaying,&propertySize, &otherAudioIsPlaying);
if(otherAudioIsPlaying) {
// other application is generating sound output (including VoiceOver)
// but might also be any other app (like iPod App)
}
I'm developing an interactive storybook type application for the iPhone and I've recently encountered a frustrating bug concerning audio mixing on the device.
Firstly, I setup an audio session. I set the category to AVAudioSessionCategoryAmbient and then init and play my AVAudioPlayer instance. Now, in the background whilst the audio is playing I'm pre-loading a video to play using an MPMoviePlayerController followed by a call to prepareToPlay. The reason I pre-load the video this way is because I need it to play instantly later on cue with fairly strict timing.
In this configuration, the audio/movie works fine and they mix and do not interrupt each other. However, this particular audio session category does not permit audio to continue playing while the device is locked, a feature I really need. As a result I'm forced to consider a different category: AVAudioSessionCategoryPlayback.
By default this category does not permit mixing with other audio, according to the Apple docs. To enable mixing with other audio I am overriding the relevant category:
OSStatus propertySetError = 0;
UInt32 setProperty = 1;
propertySetError = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(setProperty), &setProperty);
assert(propertySetError == 0);
Unfortunately, this solves my playing whilst locked issue but introduces another issue: the AVAudioPlayer audio is interrupted briefly as the video loads with a minor stutter. The stutter is small, perhaps less than a second but is enough to disrupt the user experience. I've read this related post which enabled me to pre-load the video with the AVAudioSessionCategoryAmbient, but unfortunately the same approach doesn't seem to work with the new category.
The audio session category is applied successfully, according to the return code. Does anyone know why enabling audio mixing with this category is not the same as the mixing facility provided by ambient category?
The best way I've found working a similar problem is to use the newer AVPlayer (+1 #adam) and set your app to enable background audio and receive remote control notifications. I was tipped off to this by #MarquelV following How can you play music from the iPod app while still receiving remote control events in your app?
If you can get backgrounding working properly, that should enable you to continue playing while the device is locked. Oh, and don't forget to add keys to info.plist, its easy to do and then have no idea why it isn't working.
Is it possible to programmatically power off an iPhone or does Apple disallow this?
If Apple disallows this, is it possible to programmatically mute the sound on an iPhone?
The iPhone applications you create with the official SDK are sandboxes in and of themselves. Walled off sandboxes with barbed wire.
You won't be able to turn off the power. And muting sounds other than your own applications' sounds amounts to being able to turn off the iPod playback.
I don't have any evidence for that, but this would involve modifying the "UserExperience" - which is something that Apple never would allow (and why still many people jailbreak their phones).
And this involves "power off" as well as "mute sound" - because both could destroy the UX (you wait for an important call, but application X broke the sound).
Is it possible to programmatically power off apple iPhone or does apple dissalow this. If apple disallow this is it possible to programmatically mute the sound on iPhone?
Apple prevents you from affecting the functionality of other apps and the core phone functions. When in doubt, if you want to do something phone-wide, you can't.
Plus, to mute the phone, you'd also have to figure out some way of making the physical mute switch on the side of the phone match the phone's mute setting. That's not going to happen with software!
I'm not sure how powering down the device and muting the device are reasonable alternatives in your app, but the bottom line is that you can't power down the device. However, you can mute the sound of your own app or the iPod app using the MPMusicPlayerController class.
The code looks like this for your app:
MPMusicPlayerController *player = [MPMusicPlayerController applicationMusicPlayer];
player.volume = 0.0f;
And, this for the iPod:
MPMusicPlayerController *player = [MPMusicPlayerController iPodMusicPlayer];
player.volume = 0.0f;
Anything you do that affects anything external to your application wont make it through the approval process (besides push notifications). You can certainly mute the sound in your app by simply pausing, stopping, or setting the volume to zero for all sounds you are playing. If you mean make the phone be mute globally, no.
You can't turn the device off through software. You can set the music playback volume with the MPMusicPlayerController class, the docs suggest you can't change the volume of the
iPod player though.
I'm obviously not trying to play full spectrum audio, but is there some way to customize the iPhone/iTouch system alert sounds to play a little melody on the piezo speaker? The functionality is clearly present, so I guess the question is has Apple made it available for developer use...
Thanks,
-S
There are quite a few predefined sounds you can use in the 0-2000 range. For example, the keyboard click can be called like such:
AudioServicesPlayAlertSound(1104);
There is also a way to play a custom melody on the first-generation iPod touch, but it's not pretty.
It isn't. Sorry.
Only thing you can do from an application is this (you can use any SoundID for this, really):
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
Discussion
Depending on the particular iPhone OS device, this function plays a short sound and may invoke vibration. Calling this function does the following on various iPhone OS devices:
iPhone—plays the specified sound and, if the user has configured the Settings application for vibration on ring, invokes vibration.
iPod touch, original—plays a short alert melody.
iPod touch, 2nd generation—plays the specified sound.
We have a game that is very close to being released, but we have one extremely annoying problem. One on of our Beta testers phones, he can't hear any of the in game sound effects. He can, however, hear the background music and the title screen music just fine.
The background and title music are both being played via AVAudioPlayer (they are longer, we need looping and volume control, etc). The sound effects are simply being played with AudioServicesPlaySystemSound (they are very short, we don't need precise control or to know when they end, etc). This works on most iPhones, but not on this one. All of this is being played with an audio session of AVAudioSessionCategorySoloAmbient.
So I have two questions:
- First, is this an acceptable implementation? i.e. is there something I missed that says you can't mix these two frameworks, or a reason why its a bad idea to mix them?
- Second, has anyone seen something like this before? If so, did you find a way around it?
Additional background note: I can pretty conclusively say that on his phone, it is the mixing of the two frameworks. He was able to hear sounds until roughly the same build where we added the title screen music. Also, if I change one of the sounds to work through an AVAudioPlayer, he's able to hear it. Unfortunately, I can't simply move the sounds into AVAudioPlayers because it just doesn't perform well at all, and I need better synchronization.
EDIT 2:
I've determined the cause of the error. The two different audio frameworks play on two different volume settings. There is the main volume (that's what shows up once you are in the app, and has no title on it) and the ringer volume (that's what shows up when you're on the home screen without any app loaded). The AVAudioPlayer calls play with the main volume setting. As I have set the category to AVAudioSessionCategorySoloAmbient (see code below), this is the volume control that will be adjusted if you use the volume rocker inside the app. The SystemSounds, however, are played at the Ringer Volume. This will NOT come up inside the app, and as such does not allow the user to adjust their settings during the game.
It's easy to see the possible confusion for the user: Let's say they have their ringer set to low, or even off. They start playing the game. They hear the title screen music (AVAudioPlayer) and are able to turn the volume up or down and it responds normally. Then they start the game and the sound effects play (SystemSounds), and they hear nothing because the ringer volume is low/off. In an effort to hear the sound effects, they bump up the volume and the background music responds accordingly. So from this point of view, it definitely looks like the sound effects just aren't playing.
If you have this similar situation, have your user make sure that their ringer volume is up before playing the game and their mute switch is on (i.e. not muted). You can also verify all of this by doing the test yourself - drop your ringer volume and bump your in-app volume up. Your SystemSounds should drop out of the mix.
Original answer:
In re-digging through all of the docs, I found the answer. I wasn't properly activating the audio session. Previously I only had this code to set the category:
NSError *setCategoryError = nil;
BOOL categoryWasSet = [[AVAudioSession sharedInstance]
setCategory: AVAudioSessionCategorySoloAmbient
error: &setCategoryError];
But I needed to also add the following code to explicitly activate the audio session:
NSError *activationError = nil;
BOOL activationSuccess = [[AVAudioSession sharedInstance]
setActive: YES
error: &activationError];
I've re-tested with this user, and it has completely fixed the issue. Hope that helps anyone who might have a similar problem.
EDIT: This doesn't seem to have resolved the issue after all. I got a false positive from my tester, and when examined more in detail it seems that he didn't actually hear the SystemSound generated sounds.
I have two apps on the app store that mix those two frameworks. AVAudioPlayer for sounds that require start and stop and volume control and AudioServices toolbox for short little beeps and clicks. The only think I can think of, off the top of my head, is that the volume of AVAudioPlayer seems to be controlled separately from the AudioServices sounds. So check to make sure that the tester doesn't have sounds turned off (with the vibrate mode switch) or the volume turned way down.
If this doesn't help, tell me more about your sounds/code and I can add some code to my app to see if I can get the same behavior. Of course, sounds like most your testers don't have this problem, and I probably won't see it either.