Creating AVAudioPlayer object much before playing the dynamic audio - ios

Whenever i play an audio file like below, it is taking few seconds to start playing the audio. I have seen from some forums and understand that, AVAudioPlayer will take few seconds to start playing if we allocate the object there itself. I am thinking to allocate this object much earlier (may be Appdelegate itself), before when i want to play, so that when i want to play it, it will play immediately.
NSURL *audioPathURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:audioName ofType:#"wav"]];
audioP = [[AVAudioPlayer alloc]initWithContentsOfURL:audioPathURL error:NULL];
[appDelegate.audioP play];
but, i am passing audio url dynamically at run time, so i would like to know how can i allocate the object earlier and then be able to pass dynamic audio url path later?
Please note, my question is different from this question Slow start for AVAudioPlayer the first time a sound is played
In the existing question, they are telling about one audio file and play it when required. But, i am having many different audio file and url path is generated randomly and at run time to play, so i do not know what the actual url path to set and play. So, the answer mentioned here->Slow start for AVAudioPlayer the first time a sound is played
won't help for my question.
I can't use prepareToPlay, because audio path url is set at run time and not just one audio is being used for all the times to play, there will more than 20 audio file randomly chosen and set to play one at a time. So, i need the right answer for it, it is not a duplicate question.
Thank you!

For a single file here the solution:
in your file.h
#import <AVFoundation/AVFoundation.h>
#interface AudioPlayer : UIViewController <AVAudioPlayerDelegate> {
}
To use with button add - (IBAction)Sound:(id)sender; in your file.h
Now in your file.m
//this is a action but you can use inside a void to start auto
#implementation AudioPlayer {
SystemSoundID soundID;
}
- (IBAction)Sound:(id)sender {
AudioServicesDisposeSystemSoundID(soundID);
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
//you can use here extension .mp3 / .wav / .aif / .ogg / .caf / and more
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"sound" ,CFSTR ("mp3") , NULL);
AudioServicesCreateSystemSoundID(soundFileURLRef, &soundID);
AudioServicesPlaySystemSound(soundID);
}
If you want use your code to play audio from URL you can use this:
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#interface AudioPlayer : UIViewController <AVAudioPlayerDelegate> {
AVPlayer *mySound;
}
#property (strong, nonatomic) AVPlayer *mySound;
#end
And file.m
- (IBAction)playPause:(id)sender {
if (mySound.rate == 0){
NSURL *url = [NSURL URLWithString:#"http://link.mp3"];
mySound = [[AVPlayer alloc] initWithURL:url];
[mySound setAllowsExternalPlayback:YES];
[mySound setUsesExternalPlaybackWhileExternalScreenIsActive: YES];
[mySound play];
} else {
[mySound pause];
}
}
Hope this can help you!

One thing you can do to reduce the latency of the first load+play is to "spin up" the underlying audio system before you need it by loading and playing a dummy file. For example, in application:didFinishLaunchingWithOptions: add some code like this:
// this won't effect the problem you're seeing, but it's good practice to explicitly
// set your app's audio session category
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
NSURL *audioURL = [[NSBundle mainBundle] URLForResource:#"dummy.wav" withExtension:nil];
AVAudioPlayer *dplayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioURL error:nil];
// we don't want to hear it (alternatively, the audiofile you use for this purpose can be silence.)
dplayer.volume = 0.f;
[dplayer prepareToPlay];
[dplayer play];
[dplayer stop];
Once this code completes, subsequent instantiations and playback of AVAudioPlayer instances should have pretty low latency (certainly well under 1 second)

Related

AVAudioPlayer crashes my app with EXC_BAD_ACCESS(code=1) when running multiple sounds at once

Hello my sounds play but if I keep hitting multiple sounds rapidly I get a crash EXC_BAD_ACCESS(code=1). Been trying for a long time to figure this one out. I tried using properties as well but no luck. Anybody ever seen this happen when playing multiple simultaneous sounds?
#interface SoundBoardScene : SKScene<AVAudioPlayerDelegate>
AVAudioPlayer *Player1;
AVAudioPlayer *Player2;
AVAudioPlayer *Player3;
AVAudioPlayer *Player4;
AVAudioPlayer *Player5;
AVAudioPlayer *Player6;
AVAudioPlayer *Player7;
AVAudioPlayer *Player8;
-(AVAudioPlayer*) PlayAudioFile:(AVAudioPlayer*)player withAudioFileName:(NSString*)audioFileName withExtensionType: (NSString*)audioFileExtension withLoopCount:(uint)NumberOfLoops
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:audioFileName
ofType:audioFileExtension]];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
player.numberOfLoops = NumberOfLoops;
return player;
}
-(void) initSounds
Player1 = [animalObject PlayAudioFile:Player1 withAudioFileName:#"Sound1" withExtensionType:#"mp3" withLoopCount:0];
Player1.delegate=self;
Player2 = [animalObject PlayAudioFile:Player2 withAudioFileName:#"Sound2" withExtensionType:#"mp3" withLoopCount:0];
Player2.delegate=self;
etc
etc
}
if([node.name isEqualToString:#"Sound1"])
{
[Player1 play];
}
if([node.name isEqualToString:#"Sound2"])
{
[Player2 play];
}
there is no need to use avplayer for play sound file.
try this code
[self runAction:[SKAction repeatAction:[SKAction playSoundFileNamed:#"pew1.wav" waitForCompletion:NO] count:count]];

AVfoundation AudioPlayer not working

I am trying to play a sound on button click using the AVFoundation framework. Previously, i used audio toolbox however when i use the audioplayer nothing comes out. What is weird is that when i test it on my phone and i press the button, the volume controls aren't the normal ringer volume controls. This suggests that it should be playing but i can't hear anything, on my phone or the simulator. I have used different files, created new project changed the code, nothing has helped.
Here's the code in .h:
#interface MainViewController : UIViewController <AVAudioPlayerDelegate>
- (IBAction)test;
Here's the code in .m:
- (IBAction)test {
NSString *path = [[NSBundle mainBundle] pathForResource:#"sound" ofType:#"mp3"];
AVAudioPlayer* theAudio = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
[theAudio play];
}
can you do this before you play the sound i think you might have the wrong category:
AudioSessionInitialize (NULL,NULL,NULL,NULL);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory,sizeof (sessionCategory),&sessionCategory);
AudioSessionSetActive(true);

AVAudioPlayer audioPlayerDidFinishPlaying called when the file is played second time

I am using AVAudioPlayer class to play audio but the problem is that when I play the audio I do not receive the callback in audioPlayerDidFinishPlaying when the file is finish playing and the file starts again even when the number of loops is set to 1
Here is the code.
.h
#interface AudioController : NSObject <AVAudioPlayerDelegate>
-(void) playBigGameTheme{
-(void) playMusic:(NSString*) fileName isLooping: (BOOL) isLooping;
#end
.m
#implementation AudioController{
AVAudioPlayer *audioPlayer;
}
-(void) playMusic:(NSString*) fileName isLooping: (BOOL) isLooping{
int loop = 1;
if(isLooping){
loop = -1;
}
[audioPlayer stop];
NSURL* soundUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:fileName ofType:#"mp3"]];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
audioPlayer.numberOfLoops = loop;
[audioPlayer setDelegate:self];
NSLog(#"===Number of loops : %d", audioPlayer.numberOfLoops);
[audioPlayer play];
}
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"HERE");
}
-(void) playBigGameTheme{
[self playMusic:BGAME_MAIN_MUSIC_FILE_NAME isLooping:NO];
}
#end
when I call playBigGameTheme the audio starts playing correctly. but it restarts when the sound is finished and when the audio finishes the second time then only the audioPlayerDidFinishPlaying method is called.
I am stuck and do not know whats going wrong..
even when the number of loops is set to 1
That's exactly why. The name and the behavior of this property is a bit counter-intuitive, but if you had read the documentation, it would have been clear that it sets the number of repetitions, and not the number of playbacks. Quote from the docs:
The value of 0, which is the default, means to play the sound once.
So set it to 0 (or don't set it, zero is the default value) and it will play the audio file only once:
audioPlayer.numberOfLoops = 0;

How to loop audio on a specific View Controller

-(void)viewDidAppear:(BOOL)animated
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundFileURLRef;
soundFileURLRef = CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"Intro",
CFSTR ("wav"), NULL);
UInt32 soundID;
AudioServicesCreateSystemSoundID (soundFileURLRef, &soundID);
AudioServicesPlaySystemSound (soundID);
}
I'm a beginner with this. I'm trying to play a wav file in the background of the view controller. I originally made a 30 second wav file, it wouldn't play, I reduced the length of the audio file to 10 seconds and it plays fine on the view controller. My question is, how to loop this audio file and/or play a longer audio file. Also, will the audio stop when I leave the view controller.
I am using xCode 4.3
I am running the app on a iPhone4 & 4s (iOS 5.1)
Thanks in advance...
Apple Documentation states
http://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/SystemSoundServicesReference/Reference/reference.html
You can use System Sound Services to play short (30 seconds or shorter) sounds. The interface does not provide level, positioning, looping, or timing control, and does not support simultaneous playback: You can play only one sound at a time. You can use System Sound Services to provide audible alerts. On some iOS devices, alerts can include vibration.
So no loop is available when using system sounds.
You can check out this tutorial
http://mobileorchard.com/easy-audio-playback-with-avaudioplayer/
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/audiofile.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = -1;
if (audioPlayer == nil)
NSLog([error description]);
else
[audioPlayer play];
}
It states that if you set the numberOfLoops property to a negative number causes the audioPlayer to loop indefinitely
And dont forget to add the AVFoundation framework to your project

iPhone AVAudioPlayer app freeze on first play

I'm using AVAudioPlayer from iOS SDK for playing short sounds on each click in tableView rows.
I have manually made #selector on button in each row that fires up method playSound:(id)receiver {}. From receiver I get sound url so I can play it up.
This method looks like that:
- (void)playSound:(id)sender {
[audioPlayer prepareToPlay];
UIButton *audioButton = (UIButton *)sender;
[audioButton setImage:[UIImage imageNamed:#"sound_preview.png"] forState:UIControlStateNormal];
NSString *soundUrl = [[listOfItems objectForKey:[NSString stringWithFormat:#"%i",currentPlayingIndex]] objectForKey:#"sound_url"];
//here I get mp3 file from http url via NSRequest in NSData
NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithData:soundData error:&error];
audioPlayer.numberOfLoops = 0;
if (error) {
NSLog(#"Error: %#",[error description]);
}
else {
audioPlayer.delegate = self;
[audioPlayer play];
}
}
Everything works fine except for the first play of some sound. The application freezes for about 2 seconds and than sound is played. Second and every other sound play works just right after click on sound button.
I wonder why there is that about 2 seconds freeze on first play when application starts?
From your code snippet, audioPlayer must be an ivar, right?
At the top of the method, you invoke -prepareToPlay on the existing audioPlayer instance (which could be nil, at least on the first pass).
Later in the method, you replace this existing audio player with a brand new AVAudioPlayer instance. The previous -prepareToPlay is wasted. And you're leaking memory with each new AVAudioPlayer.
Instead of caching sound data or URLs, I would try creating a cache of AVAudioPlayer objects, one for each sound. In your -playSound: method, get a reference to the appropriate audio player for the table row, and -play.
You can use -tableView:cellForRowAtIndexPath: as the appropriate point to get the AVAudioPlayer instance for that row, maybe lazily creating the instance and caching it there as well.
You can try -tableView:willDisplayCell:forRowAtIndexPath: as the point where you would invoke -prepareToPlay on the row's AVAudioPlayer instance.
Or you could just do the prepare in -tableView:cellForRowAtIndexPath:. Experiment and see which works best.
Check whether you are getting data asynchronously in function..
NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];
If you are getting asynchronously the execution will be blocked until it will get data.
If your audio is less than 30 seconds long in length and is in linear PCM or IMA4 format, and is packaged as a .caf, .wav, or .aiff you can use system sounds:
Import the AudioToolbox Framework
In your .h file create this variable:
SystemSoundID mySound;
In your .m file implement it in your init method:
-(id)init{
if (self) {
//Get path of VICTORY.WAV <-- the sound file in your bundle
NSString* soundPath = [[NSBundle mainBundle] pathForResource:#"VICTORY" ofType:#"WAV"];
//If the file is in the bundle
if (soundPath) {
//Create a file URL with this path
NSURL* soundURL = [NSURL fileURLWithPath:soundPath];
//Register sound file located at that URL as a system sound
OSStatus err = AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &mySound);
if (err != kAudioServicesNoError) {
NSLog(#"Could not load %#, error code: %ld", soundURL, err);
}
}
}
return self;
}
In your IBAction method you call the sound with this:
AudioServicesPlaySystemSound(mySound);
This works for me, plays the sound pretty damn close to when the button is pressed. Hope this helps you.
This sometimes happens in the simulator for me too. Everything seems to work fine on the device. Have you tested on actual hardware?

Resources