Changing the volume without a volume slider on an iphone - iphone

I need your help. How should I proceed to change the sound volume in my app. I don't want to use a volume slider. Instead I have an UIImageView which is a volume knob, in which I rotate clockwise to increase, and anti clockwise to decrease the sound volume. The rotation is just an animation and I've already done that part.
I need your help and advice on how to increase/decrease the volume. Thanks

I view this as a bug in Apple's code and have reported it to them both with Bug Reports and in person, but since they insist its a feature, you might as well benefit from it.
Use the following code to change your application's volume:
[[MPMusicPlayerController applicationMusicPlayer] setVolume:newVolume];
This will only work after you have established your audio session, either by playing a sound or by setting it active as such:
[[AVAudioSession sharedInstance] setActive:YES error:NULL];
Note as that you'll need MediaPlayer.framework and AVFoundation.framework and that the volume is between 0.0 and 1.0.

I would be careful calling setValue on an MPVolumeView since it probably won't do anything other than update the appearance of the slider, but not the actual device volume level. You would instead have to call _commitVolumeChange which is private API and will likely get your app rejected.
A short answer to how to control volume: It really depends on what you're trying to control the volume of.
If you want a "controls every sound within the app" sort of control then you can use an MPVolumeView but you cannot change it's value programmatically. You would then only be able to change the volume by either moving the slider with a touch or using the volume buttons on the side of the device. The best thing to do is create a global object that stores the volume level which any of your objects can read before they play their sound.
If it's an AVAudioPlayer object, you'd create the object and use [theAudioPlayerObject setVolume: someFloat]; where someFloat is a value between 0.0 and 1.0.
If it's a SystemSound object, you can't control volume.
If it's an AudioQueue you'd change it via AudioQueueSetParameter
Like I said.. it all depends on how you are playing the sound.
Update based on comment
For that particular example, you would set the volume like this:
Add to the AudioStreamer.h file
- (void)setVolume:(float)Level;
Add to the AudioStreamer.m file
- (void)setVolume:(float)Level
{
OSStatus errorMsg = AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, Level);
if (errorMsg) {
NSLog(#"AudioQueueSetParameter returned %d when setting the volume.", errorMsg);
}
}
Add to the view controller for where the volume knob will be (this goes in the .m file.. i just did this as a couple UIButtons real quick, you'll have to do your own) and set up an IBAction to change the volume for a given value (you can pass in 0.0 thru 1.0 as a float)
- (IBAction)volumeUp:(id)sender
{
[streamer setVolume:1.0];
}
- (IBAction)volumeDown:(id)sender
{
[streamer setVolume:0.0];
}

Well, take the min Rotation (R1) and max Rotation (R2). Then do rotation / (R2 - R1) to get a % like a slider does.
EDIT:
To commit the volume change, add the following:
MPVolumeView *systemVolumeSlider = [[MPVolumeView alloc] initWithFrame: self.view.bounds];
[systemVolumeSlider setHidden:YES];
[systemVolumeSlider setUserInteractionEnabled:NO];
[self.view addSubview:systemVolumeSlider];
(Make sure to release systemVolumeSlider in dealloc)
Then, when the volume is changed, use setValue to set its value. You will also need to handle what happens when your user presses volume +/- buttons on the device.

Related

How do I tie a audio track volume slider to the physical volume buttons

I would like to tie the slider action of the volume control to the volume buttons so if the buttons are used the slider adjusts as well. I thought this was possible but I am not sure.
Currently I have a working slider volume control but it is not tied to the physical buttons.
I haven't tested these, just did a bit of research and copied and pasted.
1a. get the volume from the device.
float volume = [[AVAudioSession sharedInstance] outputVolume];
self.volume = vol;
or
#import <MediaPlayer/MediaPlayer.h>
//...
float vol = [MPMusicPlayerController iPodMusicPlayer].volume;
self.volume = vol;
1b. get notified when volume changes
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(volumeChanged:)
name:#"AVSystemController_SystemVolumeDidChangeNotification"
object:nil];
-(void)volumeChanged:(NSNotification*)notification{
float vol = [[[notification userInfo]
objectForKey:#"AVSystemController_AudioVolumeNotificationParameter"]
floatValue];
self.volume = vol;
}
// Some said the Notification used is a private apple API so you should do a bit of research for using it in a app you are submitting to apple.
// There is also this notification though: MPMusicPlayerControllerVolumeDidChangeNotification
Set the slider to the volume.
self.volumeSlider.value = (double) self.volume;
Or
Use MPVolumeView from apple
Use a volume view to present the user with a slider control for setting the system audio output volume, and a button for choosing the audio output route. When first displayed, the slider’s position reflects the current system audio output volume. As the user drags the slider, the changes update the volume. If the user presses the device volume buttons while sound is playing, the slider moves to reflect the new volume.
- Apple Docs
mpVolumeViewParentView.backgroundColor = [UIColor clearColor];
MPVolumeView *myVolumeView = [[MPVolumeView alloc] initWithFrame: mpVolumeViewParentView.bounds];
[mpVolumeViewParentView addSubview: myVolumeView];
Edit:
From reading the MPMediaPlayer docs it does seem as if Apple wants everyone to use their own volume slider and customise the look of it if needed.

Hardware Volume buttons change in app volume

So in my app i would like to change the in app volume leves for an alarm by the use of the hardware buttons but whenever i use the buttons to turn up or down the volume it ONLY changes the "ringer" volume wich does NOT effect my in app volume.
Under Settings -> Sounds the "change with buttons" switch is ON and everything works fine if i turn it off but most users will want to have it on as well. So when im in my app i want the volume buttons to change the app volume not the ringer volume.
Hope it makes sense
Thanks
By default the hardware buttons will change the alarm volume unless you have an open audio session when they use the buttons. I would recommend opening an AVAudioSession to have them change it or placing a volume slider somewhere in your app to have them change the volume.
This is a difficult problem to solve perfectly because users aren't told what is wrong and many times don't look to see that the ringer volume is what is being changed.
Use MPVolumeView!
If you add an MPVolumeView to your UIWindow (you can make it hidden), the MPVolumeView will automatically take over the hardware buttons for you. The hardware buttons will now affect your app's volume levels instead of the system's.
MPVolumeView *volumeView = [[MPVolumeView alloc] initWithFrame:CGRectZero];
volumeView.showsRouteButton = NO;
volumeView.hidden = YES;
[self.window addSubview:volumeView];

Change iPhone application volume without volume changed box appearing (app for Japan)

I am making an Augmented Reality application that has picture taking functionality. It uses a custom function of mine to create a UIImage to save the screen. By law in Japan, cameras must have a shutter noise, which is why the iPhone camera always plays it. So far I have found a way to play sounds even when the iPhone is muted but it still relies on the user set volume. So I found a way using MPMusicPlayerController to control the application volume. This works, but when the volume is changed a box pops up signaling that the volume was changed.
Here is my code to play sounds even when muted:
AudioSessionInitialize (NULL, NULL, NULL, NULL);
AudioSessionSetActive(true);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory,
sizeof(sessionCategory),&sessionCategory);
I use the library Finch to play the sound (a light wrapper for openAL) and then MPMusicPlayerController to adjust the volume before play.
appMusicPlayer = [MPMusicPlayerController applicationMusicPlayer];
[appMusicPlayer setVolume:0.5f];
Anyone have experience with this or have made apps like this for Japan? I'm really at a loss. Thanks.
The MPVolumeView will, while visible, block the floating box, even if the user can't actually see it.
Some sample code…
// create/synthesize ivars for "MPVolumeView" and "UIView" (both are necessary)
// I called them "mpVolumeView" and "mpVolumeViewParentView" respectively
// the UIView containing the MPVolumeView can have a frame of (0,0,1,1)
// this way, the user never sees the slider, but it still works normally
- (void)viewDidLoad {
...
// with this, only the slider is visible
mpVolumeViewParentView.backgroundColor = [UIColor clearColor];
// initialize the volume slider (link the parent view in IB, or init here)
mpVolumeView = [[MPVolumeView alloc] initWithFrame:
mpVolumeViewParentView.bounds];
// since it's a programmatic init, the subview must be added like so
[mpVolumeViewParentView addSubview:mpVolumeView];
// allows the floating box to appear without destroying mpVolumeView
mpVolumeView.hidden = YES; // or [mpVolume setHidden:YES]; if you prefer
...
}
Before changing volume to force the camera to make sound…
mpVolumeView.hidden = NO; // view visible, box doesn't appear
And after sounds, so it doesn't look like you messed with anything…
mpVolumeView.hidden = YES; // view hidden, box appears
It might take some tweaking to get what you want, but it should be a good starting point.
This code is for iOS 5.1
I don't know what the compatibility is with older versions.

iPhone SDK: AVAudioRecorder will not record after calling [AVPlayer play]

I have a view controller that uses AVPlayer. this view controller can load a modal view controler where the user can record audio using AVAudioRecorder.
this is what happens:
if the user plays the composition in the fist controller with [AVPLayer play] the AVAudioRecorder will not record in the modal view controller. there are no errors but the current time returned by AVAudioRecorder is 0.0;
if the user dismisses the modal dialog and reloads it. AVAudioRecorder works fine.
this can be repeated over and over
AVAudioRecorder does not work the first time it is invoked after a [AVPlayer play] call
I have been fighting with this for days and just have reorganized my code related to both AVPlayer and AVAudioRecorder and it's still acting weird.
Any help or pointer is much appreciated
Thanks in advance
Jean-Pierre
I've been having the same problem too, and this is the solution I found.
When recording, write this line code after AVAudioRecord is initialized:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:NULL];
Then invoke record method.
How are you setting the AVAudioSession's category, i.e., play or record? Try something like
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
I've been having the same problem. I use AVPlayer to play compositions (previous recordings I've used AVAudioRecord for). However, I found that once I've used AVPlayer I could no longer use AVAudioRecorder. After some searching, I discovered that so long as AVPlayer is instantiated in memory and has been played at least once (which is usually what you do immediately after instantiating it) AVAudioRecorder will not record. However, once AVPlayer is dealloc'd, AVAudioRecorder is then free to record again. It appears that AVPlayer holds on to some kind of connection that AVAudioRecorder needs, and it's greedy...it won't let it go until you pry it from it's cold dead hands.
This is the solution I've found. Some people claim that instantiating AVPlayer takes too much time to keep breaking down and setting back up. However, this is not true. Instantiating AVPlayer is actually quite trivial. So also is instantiating AVPlayerItem. What isn't trivial is loading up AVAsset (or any of it's subclasses). You really only want to do that once. They key is to use this sequence:
Load up AVAsset (for example, if you're loading from a file, use AVURLAsset directly or add it to a AVMutableComposition and use that) and keep a reference to it. Don't let it go until you're done with it. Loading it is what takes all the time.
Once you're ready to play: instantiate AVPlayerItem with your asset, then AVPlayer with the AVPlayerItem and play it. Don't keep a reference to AVPlayerItem, AVPlayer will keep a reference to it and you can't reuse it with another player anyway.
Once it's done playing, immediately destroy AVPlayer...release it, set its var to nil, whatever you need to do. **
Now you can record. AVPlayer doesn't exist, so AVAudioRecorder is free to do its thing.
When you're ready to play again, re-instantiate AVPlayerItem with the asset you've already loaded & AVPlayer. Again, this is trivial. The asset has already been loaded so there shouldn't be a delay.
** Note that destroying AVPlayer may take more than just releasing it and setting its var to nil. Most likely, you've also added a periodic time observer to keep track of the play progress. When you do this, you receive back an opaque object you're supposed to hold on to. If you don't remove this item from the player AND release it/set it to nil, AVPlayer will not dealloc. It appears that Apple creates an intentional retain cycle you must break manually. So before you destroy AVPlayer you need to (example):
[_player removeTimeObserver:_playerObserver];
[_playerObserver release]; //Only if you're not using ARC
_playerObserver = nil;
As a side note, you may also have set up NSNotifications (I use one to determine when the player has completed playing)...don't forget to remove those as well.
If you need use AVPlayer and AVAudioRecorder at the same time do following:
set audio category to "play and record" (as described above or with C-based Audio Session Function)
"record" method must be invoked after invocation of "play" method with some delay. (I set 0.5 sec)
If you don't provide this, playback will start, but recording will not start.
shouldn't it be [AVAudioRecorder record]; instead of play?
I've had the same issue: AVAudioRecorder did not start recording.
My example is a bit different: I have a tabbar based app with several AVAudioPlayers in the different views. For the sake of the example, lets say that the first loaded view has 3 AVAudioPlayers, while the second view has one AVAudioPlayer and one AVAudioRecorder. They are all initiated in the viewDidLoad method.
The recorder was not working in second view. When I hit my record button, the view slightly shaked and the recording did not begin.
Strangely, the simulator worked fine and did not show the same symptoms... (anyone has an idea why?)
After some research and reading this thread I have realized that I have not set the players in the first view to nil, so I guess they were still in the memory preventing the recorder to start. Once I have set them to nil in the viewDidDisappear method, it was working alright again.
here is what helped me:
-(void)viewDidDisappear:(BOOL)animated {
firstPlayer = nil;
secondPlayer = nil;
thirdPlayer = nil;
}
ViewDidUnload did not help because the View, and therefore the variablbes in the class itself were not unloaded when switching views.
Hope this may be useful for others as well.
#Aaron Hayman explanation is accurate but the solution for me was very simple. Just implement the didFinishPlaying method of the player delegate and release the player there.
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
self.playerWord = nil;
}

Volume control using UIslider Iphone?

i am creating a application in which sound is played when a button is pressed and using a UISlider with which volume can be adjusted.Sometimes the volume of sound is too high and sometimes its too low even after increasing the volume of iphone to the full.How can i keep the volume to always high?? any possible way to integrate system volume with slider volume?? Using MPVolumview will get my app rejected i guess..
the code i am using on button touch is this
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
resourcePath = [resourcePath stringByAppendingString:#"/sound.mp3"];
NSLog(#"Path to play: %#", resourcePath);
player = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:&err];
player.delegate = self;
[player play];
player.volume=.50;
player.numberOfLoops=-10;
-(IBAction)slidervaluechanged
{ player.volume=slider.value; }
}
Hemant,
The maximum volume you can have is when player.volume is equal to 1.0 (so long as the ringer volume is all the way maximum as well).
If the ringer volume is not at maximum, you can only go as high as whatever it is at by again using the value of 1.0
However, you could implement the MPVolumeView (I'm almost positive, as Pandora does it) and then you use that slider in your app instead. Then you can just set player.volume always equal to 1.0 and let the slider change the ringer volume.
More information on if you want to use MPVolumeView: http://developer.apple.com/iphone/library/documentation/MediaPlayer/Reference/MPVolumeView_Class/Reference/Reference.html and How do you implement an MPVolumeView?
I may be wrong, but I think Apple allows MPVolumeView if you use it as-is. They say in the class reference that it now changes the device ringer volume when you move the slider (which it didn't used to and that's why people were having to access private API to do). I'm going to try and implement it on my next update here in a week, so if I get rejected, I'll come back here and update this post to let everyone know.