Does anyone know the best way to mute game sounds on my app.
I am currently using SystemSoundID which I know cannot have the volume adjusted and need to move to AVAudioPlayer.
Ideally I need a settings page where the volume can be changed and the volume level saved so when coming back out or leaving and re-opening the app it remembers to volume level?
I have tried it various ways using AVAudioPlayer but had no success at present and am receiving some bad reviews for my app for people saying they hate that they cant control the game volume... HELP!!
for AVAudioPlayer you can use this code to mute volume
in .h file
#interface ViewController : UIViewController
{
BOOL muted;
}
and in .m file
- (void)viewDidLoad
{
[super viewDidLoad];
muted = NO;
}
- (IBAction)speakerOnOff:(id)sender
{
if (muted) {
muted = NO;
[player setVolume:1.0];
} else {
muted = YES;
[player setVolume:0.0];
}
}
this code is from this great answer.
Related
My app uses Audio Queue Services to play sounds. On app start, I set audio session category to solo ambient:
`[[AVAudioSession sharedInstance] setCategory:AVAudioSEssionCategorySoloAmbient error:&error];`
When my app delegate gets applicationWillResignActive notification, I call AudioQueuePause(queue); for all playing sounds. And when the app delegate gets applicationDidBecomeActive, I call
OSStatus status = AudioQueueStart(queue, 0);
Sometimes (and it's hard to reproduce) status equals to 561015905. This value does not belong to Audio Queue Result Codes. It is AVAudioSessionErrorCodeCannotStartPlaying, and docs say
"The app is not allowed to start recording and/or playing, usually because of a lack of audio key in its Info.plist. This could also
happen if the app has this key but uses a category that can't record
and/or play in the background (AVAudioSessionCategoryAmbient,
AVAudioSessionCategorySoloAmbient, etc.)."
I definitely do not want to play the sound in background (that's why I am pausing audio queues when app resigns active). So, what am I doing wrong?
I had a similar problem, but I made a little in a different way...There was musical app in which background music was necessary, but under certain conditions she has to howled to play in background mode.
For me, i use this code:
#import "ViewController.h"
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVAudioPlayer.h>
#import <AVFoundation/AVAudioSession.h>
#interface ViewController ()
{
AVAudioPlayer *audioPlayer;
}
#property (assign) SystemSoundID pewPewSound;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSURL * pewPewURL = [[NSBundle mainBundle]URLForResource:#"Calypso" withExtension:#"caf"];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:pewPewURL error:nil];
if (audioPlayer)
{
[audioPlayer setNumberOfLoops:100];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[audioPlayer prepareToPlay];
[audioPlayer play];
}
}
#end
If u don't touch any Xcode setting, this code play looping melody, only in foreground, and if u go to background, player is stopped.
if u do this:
melody will continue to play in background, and in my case i can stop and play melody in background and foreground when i want. And I add system sound to call then u stop player, system sound stopped then U continue play music.
-(void)stopPlaying
{
[audioPlayer pause];
NSURL * pewPewURL;
pewPewURL = [[NSBundle mainBundle]URLForResource:#"Calypso" withExtension:#"caf"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)pewPewURL, &_pewPewSound);
AudioServicesPlaySystemSound(self.pewPewSound);
}
-(void)continueToPlay
{
if ([audioPlayer prepareToPlay])
{
[audioPlayer play];
AudioServicesRemoveSystemSoundCompletion(self.pewPewSound);
AudioServicesDisposeSystemSoundID(self.pewPewSound);
}
}
I hope it will help to solve yours problems, If there are questions, set I will answer everything.
In my app, I have multiple tabs and on each tab I have an instance of AVPlayer. When I activate AirPlay, however, "the first player wins". That means that the player on the currently active tab connects to AirPlay and when I switch to a different tab and press play, nothing happens. So only the first instance of AVPlayer that connects to AirPlay can actually play through AirPlay and no players on the other tabs work. What to do?
The solution is quite easy: When your view controller that contains a player appears, you set allowsExternalPlayback on the AVPlayer instance to YES, when in disappears you set it to NO.
Example:
- (void)viewWillAppear:(BOOL)animated
{
// _player is an instance of AVPlayer
if ([_player respondsToSelector:#selector(setAllowsExternalPlayback:)]) {
// iOS 6+
_player.allowsExternalPlayback = YES;
} else {
// iOS 5
_player.allowsAirPlayVideo = YES;
}
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
// _player is an instance of AVPlayer
if ([_player respondsToSelector:#selector(setAllowsExternalPlayback:)]) {
// iOS 6+
_player.allowsExternalPlayback = NO;
} else {
// iOS 5
_player.allowsAirPlayVideo = NO;
}
[super viewWillDisappear:animated];
}
Enjoy.
I've created my own custom controls for use with the MPMoviePlayerController. So far everything works except the mute button control.
I've configured the AVAudioSession using the following code before I create my instance of the MPMoviePlayerController.
NSError *modeError = nil;
[self.audioSession setMode:AVAudioSessionModeMoviePlayback error:&modeError];
if (modeError != nil) {
NSLog(#"Error setting mode for AVAudioSession: %#", modeError);
}
NSError *categoryError = nil;
[self.audioSession setCategory:AVAudioSessionCategoryPlayback error:&categoryError];
if (categoryError != nil) {
NSLog(#"Error setting category for AVAudioSession: %#", categoryError);
}
Then in my mute button callback method I have the following code:
NSError *activeError = nil;
[self.audioSession setActive:NO error:&activeError];
if (activeError != nil) {
NSLog(#"Error setting inactive state for AVAudioSession: %#", activeError);
}
When clicking the Mute button I get the following unuseful error:
Error Domain=NSOSStatusErrorDomain Code=560030580 "The operation couldn’t be completed. (OSStatus error 560030580.)"
I am linking to the AVFoundation framework.
This is really starting to bug me as I can't for the life of me work out a way to reduce or mute the playback audio of my application.
I don't want to change the system global volume just the application level volume as defined by the AVAudioSession AVAudioSessionCategoryPlayback category.
It seems that you can set the volume of the AVAudioPlayer but not the MPMoviePlayerController. I've seen other posts here on SO that say just create an instance of AVAudioPlayer and set the volume but this just causes my app to crash and I expect it has something to do with the fact I'm not using the initWithContentsOfURL:error: or initWithData:error: and instead using `init'.
Any help would be appreciated.
After speaking to an Apple technician it turns out that it's not possible to control or mute the audio using MPMoviePlayerController.
Instead you have to create your own controller using AVFoundations AVPlayer class.
Once you're using that it's a matter of creating a custom audio mix and setting the volume level. It actually works very well.
Sample code:
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:[self localMovieURL] options:nil];
NSArray *audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
// Mute all the audio tracks
NSMutableArray * allAudioParams = [NSMutableArray array];
for (AVAssetTrack *track in audioTracks) {
AVMutableAudioMixInputParameters *audioInputParams =[AVMutableAudioMixInputParameters audioMixInputParameters];
[audioInputParams setVolume:0.0 atTime:kCMTimeZero ];
[audioInputParams setTrackID:[track trackID]];
[allAudioParams addObject:audioInputParams];
}
AVMutableAudioMix * audioZeroMix = [AVMutableAudioMix audioMix];
[audioZeroMix setInputParameters:allAudioParams];
// Create a player item
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem setAudioMix:audioZeroMix]; // Mute the player item
// Create a new Player, and set the player to use the player item
// with the muted audio mix
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
self.mPlayer = player;
[mPlayer play];
I've written an MPMoviePlayerController replacement class that adds support for volume level. I will upload the to github shortly and add the link in this post.
I know this is an old post, but I managed to find a way to successfully control the volume of the MPMoviePlayerController control in iOS6 & iOS7, using an MPVolumeView. One gotcha is that it does NOT work in the simulator, only on the physical device. For just controlling the volume, adding a hidden MPVolumeView will work fine. However if you use a hidden one, the native OS volume display that appears when you change the volume using the physical device volume buttons will still appear centre screen. If you want to prevent this, make sure your MPVolumeView is not hidden. Instead, you can give it a very low alpha transparency and place it behind other views, so the user can't see it.
Here's the code i've used:
MPVolumeView *volumeView = [[MPVolumeView alloc]initWithFrame:CGRectZero];
[volumeView setShowsVolumeSlider:YES];
[volumeView setShowsRouteButton:NO];
// control must be VISIBLE if you want to prevent default OS volume display
// from appearing when you change the volume level
[volumeView setHidden:NO];
volumeView.alpha = 0.1f;
volumeView.userInteractionEnabled = NO;
// to hide from view just insert behind all other views
[self.view insertSubview:volumeView atIndex:0];
This allows you to control the volume by calling:
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0];
But I was still getting the native OS volume display appearing the first time I would try to change the volume - on subsequent loads it did not show this display, so figuring it was something to do with the stage in the viewcontroller life cycle, I moved it from my viewDidLoad method to the viewDidAppear method - it worked - the volume muted and the native volume display did not appear, but I now was able to hear a split second of audio before the video started playing. So I hooked into the playback state did change delegate of the MPMoviePlayerController. In viewDidload I added:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(videoPlaybackStateDidChange:)
name:MPMoviePlayerPlaybackStateDidChangeNotification object:nil];
And the delegate callback method:
-(void)videoPlaybackStateDidChange:(NSNotification *)aNotification
{
// Note, this doesn't work in simulator (even in iOS7), only on actual device!
if ([moviePlayerController playbackState] == MPMoviePlaybackStatePlaying)
{
[[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0];
}
}
This muted the audio of the video before it started playing, but after the viewDidLoad in the life cycle, so no native OS volume muted display.
In my app, I retrieved and stored the current volume level before muting (using [MPMusicPlayerController applicationMusicPlayer].volume property), and then restored the volume to this level when the view controller was closed, meaning the user would be unaware that their device volume level was modified and reverted.
Also, if your MPMoviePlayerController is using a non-standard audio route in iOS7, calling [[MPMusicPlayerController applicationMusicPlayer] setVolume:0.0] may not work for you - in this case you can loop through the subviews of your MPVolumeView control until you find a view which subclasses UISlider. You can then call [sliderView setValue:0 animated:NO] which should work for you. This method isn't using any private Apple APIs so shouldn't get your app rejected - after all there are so many legitimate reasons why you would offer this functionality, and it was possible in iOS6 without having to go to these lengths! In fact, I was bamboozled to discover that Apple had removed the functionality to set the volume on MPMoviePlayerController in iOS7 in the first place.. enforced migration to AVPlayer?
Update: My iPad app has now been approved using this method and is live on the app store.
I am developing an application for iOS which works in two different modes, one of them plays music from the iPod library, and the other one synthesizes sounds (eg: pure tones).
In order to synthesize sounds I use an audio unit, so through its callback I can pass the desired signal. As far as sounds must be played at an specific volume, I set the device volume using iPodMusicPlayer.
The issue I see is that the first time the application is run on an iPod Touch 5g after turning it on, once the iPodMusicPlayer is used to set the volume of the device, applicationMusicPlayer will not respond to stop.
I came up with the following code. Runing it on an iPod Touch 5g after turning it on, music will not stop after touching stop.
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
MPMusicPlayerController *mp = [MPMusicPlayerController iPodMusicPlayer];
mp.volume = 0.3;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)playAction:(id)sender {
MPMusicPlayerController *mp = [MPMusicPlayerController applicationMusicPlayer];
MPMediaQuery *query = [MPMediaQuery songsQuery];
[mp setQueueWithQuery:query];
[mp play];
}
- (IBAction)stopAction:(id)sender {
MPMusicPlayerController *mp = [MPMusicPlayerController applicationMusicPlayer];
[mp stop];
}
#end
PS: It is also possible to reproduce the issue by entering the music app, the quitting it (double press on home button and quit music from the multitasking bar), and finally run the code above.
In the other devices I have tested so far (iPad 2nd and 3rd generation, iPod Touch 4 generation) the application works well.
Thanks in advance.
In my application I am using an AVAudioRecorder object to allow the user to record a sound file, and when the user is done recording they can play the sound via the AVAudioPlayer. The issue I'm having is when the user tries to play the audio file it only plays for one second. What is even more odd is that this code worked perfect on iOS 5, but since upgrading to iOS 6 this issue has started to happen.
I have checked the file that is being created via iTunes file sharing, and I'm able to play the entire sound file on my desktop.
Below I have pasted the code from my manipulation file that loads the audio player. From what I can tell the
- (void)playRecordingBtnClk:(id)sender
{
NSLog(#"Play btn clk");
if (!isRecording) {
// disable all buttons
[_recordButton disableButton];
[_stopButton disableButton];
[_playButton disableButton];
[_saveButton disableButton];
// create the player and play the file from the beginning
if (audioPlayer) {
[audioPlayer release];
audioPlayer = nil;
}
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioRecorder.url error:&error];
audioPlayer.delegate = self;
audioPlayer.currentTime = 0.0;
[audioPlayer prepareToPlay];
if (error) {
NSLog(#"Error Playing Audio File: %#", [error debugDescription]);
return;
}
[audioPlayer play];
[self startTicker];
}
}
It appears that the
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
method is being called after one second which is why the audio player stops. Anyone have any ideas what is going on here?
For iOS 6.0, add two more lines:
soundPlayer.currentTime = soundPlayer.duration + soundPlayer.duration;
soundPlayer.numberOfLoops = 1;
Not a perfect solution. We may need to wait for iOS 6.0.1/6.1 update.
For more, please visit this, someone commented there, saying it might be a problem of CDAudioManager.