AVAudioPlayer can't play .caf file in /Documents when the app restart - ios

I am using AVAudioPlayer to play audio of .caf file store in the sandbox of the simulator. When I run the program and play the audio of the file that I created just now, it works, but I can't play the audio of the files created when the application ran last time.
My code for playing the audio:
if (self.audioURL != nil) {
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:self.audioURL error:nil];
if (!_player) {
} else {
[_player setNumberOfLoops:0];
[_player setDelegate:self];
[_player play];
}
}
My .caf files are stored in the sandbox:
/Users/kiki/Library/Developer/CoreSimulator/Devices/2AF173A9-8C49-4F2E-9547-079AEDC0B020/data/Containers/Data/Application/EF2931D8-B498-4239-84AD-00525CA62F48/Documents
And the player didn't respond any error message, in other words, the methods of "audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error" and "audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag" not called.
How can I solve this?

Related

AVAudioPlayer doesn't play sound when MPMusicPlayerController is playing on iOS

I have an app that plays various musical sounds using AVAudioPlayer. It lets people select a song from their library and plays it with MPMusicPlayerController. It used to work that they could jam with it, but now when MPMusicPlayerController is playing it doesn't play the sound using AVAudioPlayer. It even doesn't play the sound once MPMusicPlayerController has stopped. I've tested it on 13.4.1 and 13.6. What changed to make it stop working and what can I do to fix it?
It plays the sound like this:
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&err];
[player prepareToPlay];
[player setVolume: 1.0];
[player play];
For the music, it gets a MPMediaItemCollection using MPMediaPickerController, then plays it like this:
- (void) playMusic: (MPMediaItemCollection *) collection {
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryAmbient error: nil];
if (musicPlayer == nil) {
musicPlayer = [MPMusicPlayerController applicationMusicPlayer];
[musicPlayer setShuffleMode: MPMusicShuffleModeOff];
[musicPlayer setRepeatMode: MPMusicRepeatModeNone];
}
[musicPlayer setQueueWithItemCollection:collection];
[musicPlayer play];
}
Feeling stupid. The phones were set to silent mode. For some reason, it can play the sounds or the music on silent, but it can't play them both together.

Pausing Audio file to play different Audio file

I'm using the systemMusicPlayer to play songs inside an app.
If a user pushes noiseButton on the Now Playing view, it will pause the systemMusicPlayer song audio file for 10 seconds to play the noiseButton audio file, and then pick back up on the systemMusicPlayer song audio file.
I'm able to get the systemMusicPlayer song audio file to pause and then pick back up after 10 seconds, but I am not able to get the noiseButton to play its sound in the 10 seconds.
I know the noiseButton audio file works
I'm NSLogging before and after the method that plays the noiseButton file
I'm not sure what I'm missing?
Here is my code:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
musicPlayer = [MPMusicPlayerController systemMusicPlayer];
[self registerMediaPlayerNotifications];
// Construct URL to sound file
NSString *path = [NSString stringWithFormat:#"%#/background-music-aac.caf", [[NSBundle mainBundle] resourcePath]];
NSURL *soundUrl = [NSURL fileURLWithPath:path];
// Create audio player object and initialize with URL to sound
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
}
- (void)willPlayClip {
[NSTimer scheduledTimerWithTimeInterval:10.0
target:self
selector:#selector(willPlayMusicInTenSeconds:)
userInfo:nil
repeats:NO];
}
- (void)willPlayMusicInTenSeconds:(NSTimer *)timer {
NSLog(#"Get ready to Play noise sound");
[_audioPlayer play];
NSLog(#"Play noise sound");
[musicPlayer play];
NSLog(#"Start playing again");
}
And the code inside the noiseButton for when it gets pressed:
if ([musicPlayer playbackState] == MPMusicPlaybackStatePlaying) {
[musicPlayer pause];
NSLog(#"Paused!");
[self willPlayClip];
} else {
NSLog(#"Already paused");
}
What AudioSession category are you setting? If you're not, refer to this documentation:
Audio Session Categories
I believe that you should get the desired behaviour if you set your session to AVAudioSessionCategoryAmbient:
The category for an app in which sound playback is nonprimary—that is, your app can be used successfully with the sound turned off.
This category is also appropriate for “play along” style apps, such as a virtual piano that a user plays while the Music app is playing. When you use this category, audio from other apps mixes with your audio. Your audio is silenced by screen locking and by the Silent switch (called the Ring/Silent switch on iPhone).
Give that a try if you're not already.

Sprite Kit stop playing music file and start from the beginning

Whenever I present my scene, I want to start playing a music file and stop the music playing from the last time my scene was presented. I'm trying to do it like this.
[self removeActionForKey:#"Music"];
[self runAction:[SKAction repeatActionForever:[SKAction playSoundFileNamed:#"Music.wav" waitForCompletion:YES]] withKey:#"Music"];
Should I use AVAudioPlayer or [SKAction playSoundFileNamed:]?
Also I would like to know if there is a way to play music file forever in all scenes without starting from the beginning.
To play background music in all scenes I will recommend AVAudioPlayer. You can write the code somewhere in your AppDelegate to access from all the scenes. Here is a sample code to achieve this using AVAudioPlayer
// AVAudioPlayer *bgMusicLoop;
- (void)playBackroundMusic:(NSString *)fileName withExtenstion:(NSString *)fileExtension
{
NSString *filePath = [[NSBundle mainBundle] pathForResource:fileName ofType:fileExtension];
NSError *error;
if(bgMusicLoop)
[bgMusicLoop stop];
bgMusicLoop = nil;
bgMusicLoop = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:&error];
if (error) {
NSLog(#"Error in audioPlayer: %#", [error localizedDescription]);
} else {
bgMusicLoop.numberOfLoops = -1;
[bgMusicLoop prepareToPlay];
[bgMusicLoop play];
}
}
- (void)stopBackgroundMusic
{
if(bgMusicLoop)
[bgMusicLoop stop];
bgMusicLoop = nil;
}
- (void)pauseBackgroundMusic
{
if(bgMusicLoop && bgMusicLoop.isPlaying)
[bgMusicLoop pause];
}
- (void)resumeBackgroundMusic
{
if(bgMusicLoop && !bgMusicLoop.isPlaying)
[bgMusicLoop play];
}
For music you should use AVAudioPlayer as using SKAction to play music will block any other actions you might want to run on the scene and its a bit more cumbersome to handle especially if you want to play more sounds along with the music.
Use SKAction to play short sound files (like player jump/fire etc...)
With AVAudioPlayer you can easily set your file to play repeatedly until stopped

iOS objective C low-latency audio playback like SoundPool

Hey on my Android apps I can preload my sounds in a SoundPool and then play them with almost no latency at all. Now I am looking for the same thing on iOS/obj-c, but I just can't find anything similar.
I followed a couple of tutorials but eventually there was a bigger lag than I expected and most of the tutorials are advising you to convert your audio to an uncompressed format like wav or caf but my MP3's are already at 14 mb and converting them to lossless audio leads to 81 mb of data which is way too much for me.
The most promising thing I tried was preloading the file (just like I did in Android's SoundPool) like shown in this OAL example:
- (bool) preloadUrl:(NSURL*) url seekTime:(NSTimeInterval)seekTime
{
if(nil == url)
{
OAL_LOG_ERROR(#"%#: Cannot open NULL file / url", self);
return NO;
}
OPTIONALLY_SYNCHRONIZED(self)
{
// Bug: No longer re-using AVAudioPlayer because of bugs when using multiple players.
// Playing two tracks, then stopping one and starting it again will cause prepareToPlay to fail.
bool wasPlaying = playing;
[self stopActions];
if(playing || paused)
{
[player stop];
}
as_release(player);
if(wasPlaying)
{
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:#selector(postNotification:) withObject:[NSNotification notificationWithName:OALAudioTrackStoppedPlayingNotification object:self] waitUntilDone:NO];
}
NSError* error;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if(nil == player)
{
OAL_LOG_ERROR(#"%#: Could not load URL %#: %#", self, url, [error localizedDescription]);
return NO;
}
player.volume = muted ? 0 : gain;
player.numberOfLoops = numberOfLoops;
player.meteringEnabled = meteringEnabled;
player.delegate = self;
player.pan = pan;
as_release(currentlyLoadedUrl);
currentlyLoadedUrl = as_retain(url);
self.currentTime = seekTime;
playing = NO;
paused = NO;
BOOL allOK = [player prepareToPlay];
if(!allOK)
{
OAL_LOG_ERROR(#"%#: Failed to prepareToPlay: %#", self, url);
}
else
{
[[NSNotificationCenter defaultCenter] performSelectorOnMainThread:#selector(postNotification:) withObject:[NSNotification notificationWithName:OALAudioTrackSourceChangedNotification object:self] waitUntilDone:NO];
}
preloaded = allOK;
return allOK;
}
}
But this still makes a quite considerable delay of about ~60ms which is way too much for an audio app like mine. My audio files don't have any delay in the beginning so it must have to do something with the code.
I tried all that stuff on an iPhone 5c.
You should be able to create several AVAudioPlayers and call prepareToPlay on them, but personally I like to use AVAssetReader to keep a buffer of LPCM audio ready to play at a moment's notice.

How to know when AVAudioRecorder stop by saving audio to file successfully?

Record something to the specified file by
[[ AVAudioRecorder alloc] initWithURL:_fileURL settings:settings error:&_error];
Need to play the recorded file immediately once stop the recorder and save file successfully.
Question is how to when is okay to read the file and playback it. Which delegate api could be used ?
set the delegate of your AVAudioRecorder instance
AVAudioRecorder* test = [[AVAudioRecorder alloc] initWithURL:_fileURL settings:settings error:&_error];
then implement
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
// play the recorded audio here
if (flag)
{
NSError* error;
AVAudioPlayer* player = [[[AVAudioPlayer alloc] initWithContentsOfURL:[recorder url] error:&error] autorelease];
if (!error)
{
[player play];
}
}
}
Have you tried using
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
// Play the audio
}

Resources