AVAudioPlayer not playing long sounds? - ios

I have a series of sound files ranging from 0.3s to 3.0s.
When trying to play with AVAudioPlayer, I get nothing, unless I add a sleep(4) call after to ensure the sound can play. Really weird.
Apart from that, no errors with the error param that gets passed in, and the [player play] returns YES every time. The delegate methods are not called, though, oddly enough.
Here's my code.
(.m file)
#interface MyClass ()
#property (nonatomic, strong) AVAudioPlayer *soundPlayer;
#end
#implementation SLInspireKit
//view did load here
- (void)playRandomSound {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
NSError *error;
NSString *musicFileName = [NSString stringWithFormat:#"my-sound-%d.aiff", arc4random_uniform(8)];
NSString *path = [NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], musicFileName];
NSURL *soundUrl = [NSURL fileURLWithPath:path];
self.soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:&error];
self.soundPlayer.delegate = self;
[self.soundPlayer setVolume:1.0];
[self.soundPlayer prepareToPlay];
if ([self.soundPlayer play]){
NSLog(#"Played fine");
} else {
NSLog(#"Did not play fine");
}
sleep(1); //uncomment this out, and nothing. Set to 1, first second of each sound, 2, 2 seconds and so on.
}
Any ideas? I don't think it's an ARC thing, but it could be :/ I have to initiate each time because the sound changes each time.

Quick summary of the comments:
Turns out the sleep(n) was needed since the entire class containing the AVAudioPlayer was deallocated.
Retaining the class fixes the issue.
Cheers!

Related

AVAudioPlayer not playing sound from temp file path

I have saved a recording to my Temp directory and can verify this using iExplorer
The file path structure is TempFolder/UserID/SoundName001.wav
The bit I save to the database is UserID/SoundName001.wav
Here is the code I use to play the file
#property (nonatomic, strong) AVAudioPlayer *player;
-(void) playSound: (NSIndexPath*)recordingIndex {
NSArray *recording = [recordingArray objectAtIndex:recordingIndex.item-1];
NSString *filePath = [NSString stringWithFormat:#"%#%#",NSTemporaryDirectory(),[recording objectAtIndex:2]];
NSURL *recorderURL = [[NSURL alloc] initFileURLWithPath: filePath];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:recorderURL error:nil];
[self.player prepareToPlay];
[self.player setMeteringEnabled:YES];
[self.player setDelegate:self];
[self.player play];
}
I set a breakpoint and recorderURL returns the following file:///private/var/mobile/Containers/Data/Application/0883D0DC-B1FD-4D06-A7BB-EBD9EEF5D607/tmp/1/Dog001.wav
When I call this method, nothing happens, no crash, no sound...
Am I missing something obvious here?
Did you check the silent switch on your device? I think AVPlayer doesn't play sound if the silent switch is turned on. Try putting this line of code:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
in your viewDidLoad or before [self.player play]; and see if the audio plays.

AudioServicesPlaySystemSound not playing except when I step over

I'm writing an alarm clock app and I need to have the user select the chime to wake up to. I have a UITableView with a list of my sounds and when the user taps a sound I want it to play.
However, my sounds do not play unless I step over the call to AudioServicesPlaySystemSound (or if I put a breakpoint on the call to AudioServicesDisposeSystemSoundID which triggers after I hear the sound).
I had a theory that this was a main thread issue so I used performSelectorOnMainThread:
[self performSelectorOnMainThread:#selector(playWakeUpSound)
withObject:nil
waitUntilDone:true];
but that didn't help. (The value of waitUntilDone doesn't seem to matter.)
This question was promising but I checked and I'm only calling my method once.
This is only a guess, since I don't know what is in playWakeUpSound. If you are using AVAudioPlayer within that call and ARC, it might be because it is getting released. Store the instance of AVAudioPlayer as a member of the class and it should play.
#interface MyClass
{
AVAudioPlayer *player;
}
#end
Then in the implementation:
#implementation MyClass
- (void)playWakeUpSound {
// Assuming name and extension is set somewhere.
NSURL* musicFile = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:name ofType:extension]];
NSError* error = nil;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:&error];
if (error) {
NSLog(#"%#", error.localizedDescription);
} else {
[player play];
}
}
#end

AVAudioPlayer: Simple sound not playing

I'm trying to figure out why sounds aren't playing in my app, so I created what I think is as simple an implementation as possible:
NSError *error;
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"canary-trills" ofType:#"wav"];
NSLog(#"string=%#", soundFilePath);
NSURL *url = [[NSURL alloc] initFileURLWithPath:soundFilePath];
NSLog(#"URL=%#", url);
AVAudioPlayer *avPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url error:&error];
[avPlayer prepareToPlay];
BOOL success = [avPlayer play];
NSLog(#"The sound %# play", success ? #"did" : #"didn't");
Per the console, it looks like it finds the resource, creates the URL correctly. Even the play call indicates success. However, no sound play in the simulator nor the device.
AVAudioPlayer wasn't being retained & was going out of scope.
Adding:
#property (strong, nonatomic) AVAudioPlayer *audioPlayer;
to the class & using that member solved the issue.

AVFoundation not playing sound

I have always used audio toolbox to play my sounds, but users have saying there is no sound, this is because when you are on silent it doesnt play. I have tried many times to swap instead to av foundation but it never works for me. this is the code i am attempting to use now:
- (IBAction)soundbutton:(id)sender {
NSString *path = [[NSBundle mainBundle] pathForResource:#"EASPORTS" ofType:#"mp3"];
AVAudioPlayer * theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[theAudio play];}
Reason could be that in ARC your audio player is being released by ARC, therefore you need to make a strong reference of the AVAudioPlayer, you can do that by making the AVAudioPlayer as class level property.
here is how you do it, in your .h file like this
#property (strong, nonatomic) AVAudioPlayer *theAudio;
and in .m file synthesize it like this
#synthesize theAudio;
and finally your code would look like this
- (IBAction)soundbutton:(id)sender {
NSString *path = [[NSBundle mainBundle] pathForResource:#"EASPORTS" ofType:#"mp3"];
self.theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:nil];
[self.theAudio play];
}
check also if your delegate methods are responding something
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error;

How to play multiple audio files in a row with AVAudioPlayer?

I have 5 songs in my app that I would like to play one after the other with AVAudioPlayer.
Are there any examples of this? How can I accomplish this?
Any example code would be greatly appreciated!
Thanks!
AVQueuePlayer work for this situation.
AVQueuePlayer is a subclass of AVPlayer you use to play a number of items in sequence.
Instead of AVAudioPlayer you can use AVQueuePlayer which suits this use case better as suggested by Ken.
Here is a bit of code you can use:
#interface AVSound : NSObject
#property (nonatomic, retain) AVQueuePlayer* queuePlayer;
- (void)addToPlaylist:(NSString*)pathForResource ofType:(NSString*)ofType;
- (void)playQueue;
#end
#implementation AVSound
- (void)addToPlaylist:(NSString*)pathForResource ofType:(NSString*)ofType
{
// Path to the audio file
NSString *path = [[NSBundle mainBundle] pathForResource:pathForResource ofType:ofType];
// If we can access the file...
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
{
AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL fileURLWithPath:path]];
if (_queuePlayer == nil) {
_queuePlayer = [[AVQueuePlayer alloc] initWithPlayerItem:item];
}else{
[_queuePlayer insertItem:item afterItem:nil];
}
}
}
- (void)playQueue
{
[_queuePlayer play];
}
#end
Then to use it:
In your interface file:
#property (strong, nonatomic) AVSound *pageSound;
In your implementation file:
- (void)addAudio:(Book*)book pageNum:(int)pageNum
{
NSString *soundFileEven = [NSString stringWithFormat:#"%02d", pageNum-1];
NSString *soundPathEven = [NSString stringWithFormat:#"%#_%#", book.productId, soundFileEven];
NSString *soundFileOdd = [NSString stringWithFormat:#"%02d", pageNum];
NSString *soundPathOdd = [NSString stringWithFormat:#"%#_%#", book.productId, soundFileOdd];
if (_pageSound == nil) {
_pageSound = [[AVSound alloc]init];
_pageSound.player.volume = 0.5;
}
[_pageSound clearQueue];
[_pageSound addToPlaylist:soundPathEven ofType:#"mp3"];
[_pageSound addToPlaylist:soundPathOdd ofType:#"mp3"];
[_pageSound playQueue];
}
HTH
For every song you want to make make a single AVPlayer.
NSURL *url = [NSURL URLWithString:pathToYourFile];
AVPlayer *audioPlayer = [[AVPlayer alloc] initWithURL:url];
[audioPlayer play];
You can get a Notification when the player ends. Check AVPlayerItemDidPlayToEndTimeNotification when setting up the player:
audioPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(playerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:[audioPlayer currentItem]];
this will prevent the player to pause at the end.
in the notification:
- (void)playerItemDidReachEnd:(NSNotification *)notification
{
// start your next song here
}
You can start your next song as soon as you get a notification that the current playing song is done. Maintain some counter which is persistent across selector calls. That way using counter % [songs count] will give you an infinite looping playlist :)
Don't forget un unregister the notification when releasing the player.
Unfortunately, AVAudioPlayer can only play one file. To play two files, you have to kill the first instance of the AVAudioPlayer and recreate it a second time (it can be initiated using - (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError). The problem with this approach is that there is a slight delay between when the first file finishes playing and when the second file starts playing. If you want to get rid of this delay you have to dig into Core Audio and come up with a much more complex solution.

Resources