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.
Related
I am working on this iOS app that plays music from URLs. Currently it has no issues playing through a playlist while the app is foregrounded, backgrounded, or even when the screen is locked ONLY IF the app was in the foreground prior to locking.
If the app is backgrounded THEN has the screen locked, the next song will load, but it will not play.
My question is: How can I get the AVPlayer to play the next song in a list while the app is in the background and the screen is locked?
Below is the code I am using to load and play the songs...
- (void)loadNextSong {
if (self.playlistIndex < self.playlist.count-1) { //check to see if the next track is in bounds of the playlist array
self.playlistIndex++;
[self.player pause];
[self serviceDidFetchSong:self.playlist[self.playlistIndex]];
[self.player play];
} else { //play the first song if we reach the end of the playlist
self.playlistIndex = 0;
[self.player pause];
[self serviceDidFetchSong:self.playlist[self.playlistIndex]];
[self.player play];
}
}
- (void)serviceDidFetchSong:(Song *)song {
if (song.audioFileURL != nil) {
if (![self.song.audioFileURL isEqual:song.audioFileURL]) {
if (self.timeIntervalObserver) {
[self.player removeTimeObserver:self.timeIntervalObserver];
}
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:song.audioFileURL options:0];
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:asset];
self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
//this is just for a visual of the song progress
__block __weak SongPlayerViewController *blockSelf = self;
self.timeIntervalObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds((1.0f/5.0f), NSEC_PER_SEC)
queue:NULL
usingBlock:^(CMTime time){
[blockSelf updateProgressBar];
}];
}
self.song = song;
}
}
After trying to debug with some console logs and break points, I have verified that [self.player play] is definitely being called and that the correct URL is loaded each time.
My info.plist file has audio and fetch enabled for the required background modes.
The next song just doesn't seem to play at all when the app has been backgrounded and the screen is locked.
Any insight or suggestions would be much appreciated. Thank you.
EDIT
This seems to be an intermittent issue which appears much less frequently since the release of iOS 9.3 which leads me to believe this may have been related to the OS...?
It works when you play it twice.
player?.play()
player?.play()
My ios app plays few audio (.m4a) files . It works well and i hear the audio on ios simulator running on ios 7, but when i run the app on my iphone 5, ios 7, it does not play the audio. At least i don't hear anything.
I ran the app on iPhone 4s , os 6.3 and 7.0 and it worked well. Looks like the issue is something related to iPhone 5, maybe it is some setting in my phone ?
I did a similar post here but its not really helping me.
Audio file doesn't play in iPhone 5
In the play audio method, i have the following statements
I have audioPlayer loaded and ready to play on a swipe gesture
self.audioPlayer = [self getAudioPlayer:imgAudFileName extension:#".m4a"];
[self.audioPlayer prepareToPlay];
When the use clicks on play button, this is the action that gets executed
- (IBAction)playOrPauseSound:(UIButton *)sender {
[self.audioPlayer play];
BOOL isAudioPlaying = [self.audioPlayer isPlaying];
NSLog(#" Audio Playing? %d", isAudioPlaying);
[self.audioPlayer setDelegate:self];
}
which returns 1 for both Simulator and iPhone5
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player
successfully:(BOOL)flag {
NSLog(#" Did Finish Playing %d", flag);
}
delegate method is also returning 1 on iPhone 5..
Any help on this is appreciated.
Thanks,
Update : Nov-12
I tried using a different method to initialize and use AudioPlayer but the issue remains the same
- (IBAction)testAction:(id)sender {
NSURL *url = [[NSBundle mainBundle] URLForResource:#"hello" withExtension:#"m4a"];
_somePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL];
_somePlayer.delegate = self;
[_somePlayer play];
}
I deployed the app on my friends iPhone 5, ios 7 and it worked there without any issues. I dont have a proper explanation for why it's not working on my phone. I guess I have to get it tested on more iphone 5.
Thanks,
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 starting to get convinced that iOS is just pure magic. I looked through numerous posts on AVAudioPlayer for solutions, but none worked.
When the application starts I have a movie starting off from MPMoviePlayerController and I have AVAudioPlayer kicking off with a sound as follows:
-(void)createAndPlayMovieFromURL:(NSURL *)movieURL sourceType:(MPMovieSourceType)sourceType{
/* Create a new movie player object. */
player = [[MPMoviePlayerController alloc] initWithContentURL:movieURL];
player.controlStyle = MPMovieControlModeDefault;
if (player)
{
/* Save the movie object. */
[self setMoviePlayerController:player];
/* Specify the URL that points to the movie file. */
[player setContentURL:movieURL];
/* If you specify the movie type before playing the movie it can result
in faster load times. */
[player setMovieSourceType:sourceType];
CGRect frame = CGRectMake(0, 0, 480, 320);
/* Inset the movie frame in the parent view frame. */
[[player view] setFrame:frame];
[player view].backgroundColor = [UIColor blackColor];
/* To present a movie in your application, incorporate the view contained
in a movie player’s view property into your application’s view hierarchy.
Be sure to size the frame correctly. */
[self.view addSubview: [player view]];
}
[[self moviePlayerController] play];
}
and for sound:
- (void)setupAudioPlayBack:(NSString *) path : (NSString *) extension{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:path
ofType:extension]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else {
audioPlayer.delegate = self;
[audioPlayer prepareToPlay];
}
}
And now lets talk magic. When the first movie plays and the sound, the sound is not responding to either the volume controls on the phone nor to Silent Mode On switch.
But then immediately a second movie plays with its own sound and everything works fine - there is no sound with Silent Mode On, if Silent Mode Off it responds to Volume Controls. 3rd movie - plays perfectly too.
I tried using the player.useApplicationAudioSession = NO for MPMoviePlayerController - fixed the problem with the first time sound not responding to Silent Mode or Volume Controls, but now when the second movie starts to play it freezes instantly and the sound played is cut from the original size to something around 2 seconds.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategorySoloAmbient error:nil]; didn't help either wherever I set it.
So my question is - how to make the MPMoviePlayerController interact with AVAudioPlayer without any problems...
Ok I actually found a solution (once again, magic).
When you start your ViewController you set it up as follows:
- (void)viewDidLoad
{
[self setupAudioPlayBack:#"sound1" :#"caf"];
[self playMovieFile:[self localMovieURL:#"mov1" :#"mov"]];
audioTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(playAudio) userInfo:nil repeats:NO];
[super viewDidLoad];
}
Where the playAudio method is simply:
- (void)playAudio{
[audioPlayer play];
}
Just 0.1 millisecond timer is enough to make everything work (0.0 doesn't work) and make sure a new video doesn't start before the audio finishes or it will destroy the settings so you will have to invalidate the timer and redo the timer again.
I have a piano application. It's working fine, with a little error. If I play several keys at the same time very fast, the sounds disappears for a couple of seconds, and receive the following message in the console
AudioQueueStart posting message to kill mediaserverd
Here is the relevant code:
-(IBAction)playNoteFromKeyTouch:(id) sender{
[NSThread detachNewThreadSelector:#selector(playNote:) toTarget:self withObject:[NSString stringWithFormat:#"Piano.mf.%#",[sender currentTitle]]];
}
-(void)playNote:(NSString *) note{
NSError *err;
NSString *path = [[NSBundle mainBundle] pathForResource:note ofType:#"aiff"];
AVAudioPlayer *p = [[AVAudioPlayer alloc ] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&err];
p.delegate = self;
if (err) {
NSLog(#"%#", err);
}else{
[p prepareToPlay];
[p play];
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[player release];
}
I have tested with Instruments and I don't have any memory leak. If somebody could have an idea to avoid this error it would be appreciated.
I suffer from a similar issue.
I spent ages trying to solve the issue, and I think my particular issue takes place when:
I'm in an AudioCategory that doesn't allow sound to play while the mute switch is on.
I start to play a sound (I actually don't do this in the app, but this is how I can reproduce reliably).
With the sound still playing, I switch to another AudioCategory that doesn't allow sound to play while the mute switch is on.
From this point onwards, I get 'posting message to kill mediaserverd' from what looks like various points in calls in the AudioSession API. The app hangs, the device hangs, and I struggle to get the device back to a normal running state.
According to this message it's the device's mute switch.
It turns out that having the iPad muted via the device's physical switch was
causing the problem with my app. As long as the button is not
switched on the problem does not occur.
Sheesh. How to programmatically override?
I "solved" the issue using SoundBankPlayer instead of AVAudioPlayer. SoundBanker info.