Play audio file multiple times after a very short time? - ios

I have an audio file that is 2 seconds long. Im trying to play that many times before it is done playing. But when I try it with AVAudioPlayer by creating a new instance of it, it sounds like it stops the audio that was played before and plays the new one instead of playing them at the same time. Should I use another way to play sounds or is this not achievable?
Code:
NSURL *url = [[NSBundle mainBundle] URLForResource:#"PistolShootSound" withExtension:#"mp3"];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[audioPlayer play];
audioPlayer is declared in the header file.

Have you used AVAudioPlayer's property numberOfLoops to play audio file repeatedly?
UPDATE: I didn't understand your goal before. If you want to play short files many times you could use AudioServices. Here is a good example of how to use it.

Related

Multiple AVPlayers ducking?

I have a relatively simple setup involving 1 AVPlayer looping some ambient audio in the background and a second player playing a shorter sound at certain points.
What I've observed is that when the short sound is played, I hear the ambient clip cut out for about a second while there is a staticy pop. It then proceeds to continue playing while the short sound is played at the same time. This only happens on device - it's not noticeable on the sim, which seems to point to a potential performance issue.
I can't quite figure out why the first AVPlayer has this blip. Here is the code for the ambient player:
NSString *path = [[NSBundle mainBundle] pathForResource:kAmbientTrack ofType:#"mp3"];
_ambientPlayer = [AVPlayer playerWithURL:[NSURL fileURLWithPath:path]];
[self.ambientPlayer play];
It's slightly more complex, as I also listen for a notification when it ends and the restart it, but this issue happens even during the initial play before any looping occurs.
So while that is going in the background, I play another clip like so:
self.announcementPlayer = [[AVPlayer alloc] initWithURL:[myObject audioPathUrl]];
[self.announcementPlayer play];
So nothing too unique there - just two AVPlayers playing.
The only other piece of interest is how I set up the audio session when the app launches.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
I'm doing this to allow the user to play music from other apps in the background.
I think my code is pretty straight forward, and really have no idea why I would be getting this blip.
I fixed this somewhat by changing the audio players to be AVAudioPlayers. That resolved the popping in most cases. However, I still get the ducking. Specifically, if I play an AVAudioPlayer and, while it's still playing, I start to load an AVPlayer, it cuts off the AVAudioPlayer for about a second. Here's what I'm talking about:
[myAVAudioPlayer play];
AVPlayer *player = [AVPlayer playerWithURL:[self.currentExercise videoPathUrl]];
The addition of that second line causes the first second or so of myAVAudioPlayer to be silent.

Streaming multiple music with AVPlayer

I have an app that I want to make which requires streaming audio files from web server. I use AVPlayer as the player. The problem is, some responses that I am receiving from the server has two audio files on it. And this makes the streaming hard. My audio player UI by the way is like this:
I have a slider for the streamed time ranges (the black one) and another slider for the AVPlayer.currentTime. I have two audio music streamed and their music durations are added together which is now 8:46. My first music has 6 minutes duration and my second music has 1:46. As you can see in the above photo, my streamed time ranges slider indicates that AVAsset has completely streamed the first music. My problem is, I can't continue streaming and playing the next music when the first one has reached it's end. It just stop and the slider value gets back to 0.
What I want to accomplish is that when the first item has reached its end, AVPlayer would load another player item and that would be the second music. Will continue to play and slider will continue to move.
Is this possible? What are your suggestions? Thanks experts.
To load audio files one after the other, you can use AVQueuePlayer.
NSURL *song1 = [NSURL URLWithString:#"audio url1"];
NSURL *song2 = [NSURL URLWithString:#"audio url2"];
AVPlayerItem *playerItem1 = [[AVPlayerItem alloc] initWithURL:song1];
AVPlayerItem *playerItem2 = [[AVPlayerItem alloc] initWithURL:song2];
NSArray *songs = #[playerItem1, playerItem2];
self.queuePlayer = [[AVQueuePlayer alloc] initWithItems:songs];
[self.queuePlayer play];
Hope this might help you.

iOS: how to conveniently store and play many mp3 files

I have a program with about 2000 short mp3 files. I am now storing all those file into folder Supporting Files and when I want to play I call this function:
-(void)playSound:(NSString *)mySoundFileName{
NSString *filePath = [[NSBundle mainBundle] pathForResource:mySoundFileName ofType:#"mp3"];
if ([NSData dataWithContentsOfFile:filePath]) {
url = [NSURL fileURLWithPath:filePath];
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:nil];
[audioPlayer play];
}
}
However, the first time I play the sound, it always takes long time to search/load the file. More specifically, after pressing "play sound" button to play sound, I have to wait for at least 5 seconds until it plays. It is OK to play other sound after that, i.e, it play almost immediately when I press "play sound" button. Do you have any suggestion to store and play those many files more efficiently? Thank you very much
It can sometimes take an undesirable amount of time for AVAudioPlayer to start playing initially. A good way to solve this is to make the initial alloc/init before you call play. This way the player is ready to play before the user presses the play button. Additionally, calling [player prepareToPlay]; before play will help improve performance slightly.

IOS: AVAudioPlayer

In my app I have 52 mp3 and when I load my view controller I alloc all 52 mp3 in this way:
NSString *pathFrase1 = [NSString stringWithFormat:#"%#/%#",[[NSBundle mainBundle] resourcePath],[NSString stringWithFormat:#"a%d_1a.mp3",set]];
NSURL *filePath1 = [NSURL fileURLWithPath:pathFrase1 isDirectory:NO];
f1 = [[AVAudioPlayer alloc] initWithContentsOfURL:filePath1 error:nil];
[f1 prepareToPlay];
but it's very slow when I open viewcontroller, then is there a way to alloc mp3 when I use it? and release its AVAudioPlayer??
It's a little more complicated to handle, but instead of using AVAudioPlayer, use AVPlayer. AVPlayer is designed to play AVAssets, which can be preloaded files. Specifically, you'll want to use a subclass of AVAsset called AVURLAssets, which can load up your URL: Loading AVAsset. You can then use AVAsset to create an AVPlayerItem. An AVPlayerItem is a lightweight wrapper that AVPlayer uses to keep track of play state for an AVAsset. The nice thing about using AVPlayer is that it can play an AVMutableComposition, which itself can contain multiple AVAssets. AVPlayer can also play a queue of AVAssets and provide you with information on when it is beginning to play a new AVAsset, and which one. If you load your MP3's into a bunch of AVURlAssets you can load them and keep them around, creating AVPlayerItem & AVPlayer only when you want to play one (or more) of the MP3's.
AVAudioPlayer is designed to play single files, but it uses AVAssets (and probably AVPlayer) behind the scenes. It's nice for simple situations, but anything more complex and you really want to use AVPlayer.
I should also point out that AVPlayerItem & AVPlayer are light weight objects. They don't take long at all to instantiate. It's loading the AVAsset that takes all the time. So you can feel free to create and destroy AVPlayerItem & AVPlayer objects as you need.
Finally, AVAsset and AVPlayer sometimes rely on blocks for notifications. So, for example, you may need to use c-blocks when loading up AVURLAsset to get notification on when an Asset if fully loaded. Just be aware that those blocks aren't called on the main thread. So if you try to update any UI elements or do any animations from that block it won't work right. You need to dispatch another block on to the main thread to do this, for example: dispatch_async(dispatch_get_main_queue(),^{....update UI element code....});. For more information about about dispatching blocks see Apple's Concurrency Programming Guide and Block Programming Guide.
Sure, but there will be a delay as the AVAudioPlayer is allocated and prepared for playing. Can you predict what will play and when? If so, maybe you can load a couple of seconds before you need a particular mp3 to play.
An alternative, which may not work depending on your timing requirements, is the following:
- (void)prepareAudioPlayer {
static int index = -1;
index = index + 1;
if (index < [[self FileNames] count]) {
NSError *err = nil;
NSString *audioFilePath = #""; // Figure out how to get each file name here
NSURL *audioFileURL = [NSURL fileURLWithPath:audioFilePath];
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:&err];
if (err) {
NSLog(#"Audio Player Error: %#", [err description]);
}
[player prepareToPlay];
// Add this player to the "AudioPlayers" array
[[self AudioPlayers] addObject:player];
// Recurse until all players are loaded
[self prepareAudioPlayer];
}
}
This solution requires properties of FileNames and AudioPlayers.
Once this is set up, you could do something like the following (probably in viewDidLoad):
// Make the file name array
[self setFileNames:[NSMutableArray array]];
// Initiate the audio player loading
[self performSelectorInBackground:#selector(prepareAudioPlayer) withObject:nil];
Later, when you need to play a particular file, you can find the index of the file name in the FileNames array and the call play on the AVAudioPlayer for that index in the AudioPlayers array.
This seems like maybe not the best way to do things, but it might work if you require it this way.
Here is the using method , If the sound is playing, current Time is the offset of the current playback position, measured in seconds from the start of the sound. If the sound is not playing, current Time is the offset of where playing starts upon calling the play method, measured in seconds from the start of the sound.
By setting this property you can seek to a specific point in a sound file or implement audio fast-forward and rewind functions.
The value of this property increases monotonically while an audio player is playing or paused.
If more than one audio player is connected to the audio output device, device time continues incrementing as long as at least one of the players is playing or paused.
If the audio output device has no connected audio players that are either playing or paused, device time reverts to 0.
Use this property to indicate “now” when calling the play AtTime: instance method. By configuring multiple audio players to play at a specified offset from deviceCurrent Time, you can perform precise synchronization—as described in the discussion for that method.To learn more visit..enter link description here

Sounds playing over each other using AVAudioPlayer

Right, sorry if i seem like a noob, but i'm new to xcode. I'm currently making a soundboard, and i've got all the buttons to work and play sounds from them.
However, if one sound is playing, when i press another button, rather than stopping the original sound, it just plays over it, so i was wandering if anyone knew how to fix this?
Here's the code i'm using for the buttons:
- (IBAction)playsound {
NSString *path = [[NSBundle mainBundle] pathForResource:#"Gold" ofType:#"mp3"];
AVAudioPlayer* myAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
myAudio.delegate = self;
myAudio.volume = 2.0;
myAudio.numberOfLoops = 0;
[myAudio play];
}
I'd be really grateful if someone could help me out.
Thanks.
If the other sound is still playing, you need to tell it to stop. You probably want to look into using the calling the stop method on the other AVAudioPlayer. So, something like where you have:
[myAudio play];
...insert a method call just before that to tell the other sound to stop.
[myOtherAudioThatsPlaying stop];
[myAudio play];
It might make sense to also browse the AVAudioPlayer documentation.
While bobtiki's answer is formally correct, it only makes sense to call the stop method if you want to do something useful with the sound after it has stopped, for example start playing it again. Since you just want to get rid of it, I recommend have the player just get deallocated, which automatically makes the sound stop playing.
So, instead of
AVAudioPlayer* myAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
add an instance variable to your class:
AVAudioPlayer* myAudio;
then just use
myAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
Everytime a new sound is played, myAudio gets assigned a new pointer and the old AVAudioPlayer just gets deallocated.

Resources