What is the simplest way to play a looping sound in an iPhone app?
Probably the easiest solution would be to use an AVAudioPlayer with the numberOfLoops: set to a negative integer.
// *** In your interface... ***
#import <AVFoundation/AVFoundation.h>
...
AVAudioPlayer *testAudioPlayer;
// *** Implementation... ***
// Load the audio data
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"sample_name" ofType:#"wav"];
NSData *sampleData = [[NSData alloc] initWithContentsOfFile:soundFilePath];
NSError *audioError = nil;
// Set up the audio player
testAudioPlayer = [[AVAudioPlayer alloc] initWithData:sampleData error:&audioError];
[sampleData release];
if(audioError != nil) {
NSLog(#"An audio error occurred: \"%#\"", audioError);
}
else {
[testAudioPlayer setNumberOfLoops: -1];
[testAudioPlayer play];
}
// *** In your dealloc... ***
[testAudioPlayer release];
You should also remember to set the appropriate audio category. (See the AVAudioSession setCategory:error: method.)
Finally, you'll need to add the AVFoundation library to your project. To do this, alt click on your project's target in the Groups & Files column in Xcode, and select "Get Info". Then select the General tab, click the + in the bottom "Linked Libraries" pane and select "AVFoundation.framework".
The easiest way would be to use an AVAudioPlayer set to an infinite number of loops (or finite if that's what you need).
Something like:
NSString *path = [[NSBundle mainBundle] pathForResource:#"yourAudioFileName" ofType:#"mp3"];
NSURL *file = [[NSURL alloc] initFileURLWithPath:path];
AVAudioPlayer *_player = [[AVAudioPlayer alloc] initWithContentsOfURL:file error:nil];
[file release];
_player.numberOfLoops = -1;
[_player prepareToPlay];
[_player play];
This will simply loop whatever audio file you have specified indefinitely.
Set the number of loops to any positive integer if you have a finite number of times you want the audio file to loop.
Hope this helps.
Cheers.
Related
I'm working on a game, that at first seem too run fine, but.......
after it runs a long time the sound first stops to work,
[[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error]; returns a null and a little while later when I try to set a view equal to a new image, by view.image=, I get a error in my console that says to many files open,
ImageIO: CGImageRead_mapData 'open' failed '/Users/deno/Library/Application Support/iPhone Simulator/5.1/Applications/5A0C777C-70D1-4AA0-8D7B-04A1F734C932/cPetCakes.app/twinkles04fin32.png'
error = 24 (Too many open files)
right now I creat a new I'm creating a new AVAudioPlayer object each time a sound is played. Should I be re-using the object or some how freeing it??? Since I was not using a alloc, I was thinking that the object would be freed automatcly???
Code for sound
-(void) PlaySound: (NSString *) name
{
NSURL *url;
if ( [name compare:#"chasdog"]==0)
url = [[NSBundle mainBundle]
URLForResource:name
withExtension:#"wav"];
else
url = [[NSBundle mainBundle]
URLForResource:name
withExtension:#"mp3"];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.volume=1.0;
audioPlayer.numberOfLoops= 0;
[audioPlayer play];
audioPlayer.delegate = self;
}
code that no longer loads in new images
self.image=gPetCakeB[gGame->mPetCakeId];
In the player delegate it's needed to release the player once it's stopped playing:
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
[player release];
}
Hi I want to play a mp3 file via my server e.g. http://test.com/hi.mp3
At the moment the code plays the file if its in the directory of the code.
The code also enables only 1 sound at a time.
- (IBAction)oneSound:(id)sender; {
NSString *path = [[NSBundle mainBundle] pathForResource:#"1" ofType:#"mp3"];
if (theAudio) [theAudio release];
NSError *error = nil;
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
if (error)
NSLog(#"%#",[error localizedDescription]);
theAudio.delegate = self;
[theAudio play];
}
However this code here enables me to play the sound through the http server, but I can play multiple sounds at once, I need the sound in sessions so only 1 sound can play at once. I have 10 sounds.
- (IBAction)oneSound:(id)sender; {
AVPlayer *player = [[AVPlayer playerWithURL:[NSURL URLWithString:#"http://www.mysite.com/hi.mp3"]] retain];
[player play];
}
My suggestion would be to move the pointer for your player from being declared in that method so that it is declared at a module level (in the .h file) - either just defined in the interface, or defined as a #property. Then you can access this player in another method later.
Then when you wish to switch to a new sound in another method you could try:
[player pause]; // stop the player from playing
[player release]; // free the reference count
// start a new plaer
player = [[AVPlayer playerWithURL:
[NSURL URLWithString:#"http://www.mysite.com/nextsound.mp3"]] retain];
You should be careful with the 'retain' call here. The playerWithURL will be passing back an autoreleased object, so depending on what you are doing elsewhere with autorelease pools, and depending on whether you are using a property including (retain) in its definition, you may not need to call retain here.
I am using AVAudioPLayer to do the audio for my game. I have the following code in a method in a seperate class, which is called every time I want the 'boing' sound to play. Problem is, it leaks huge amounts of memory, to the point where the game becomes unplayable. Now i'm not releasing any of the pointers in the method, because at the time I only had one. But now I have 10 of these methods.
What is the best way to release the pointers in tis case? (Yes, I have tried releasing straight after [boing play];, this solves the leak (obviously) but the sound dosen't play so theres no point.
-(void)playBoing {
int x = (arc4random()%3)+1;
NSString *path = [NSString stringWithFormat:#"/boing_0%i.aif", x];
NSString* resourcePath = [[NSBundle mainBundle] resourcePath];
resourcePath = [resourcePath stringByAppendingString:path];
AVAudioPlayer *boing = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:nil];
boing.delegate = self;
boing.volume = 1;
[boing play];
}
It's possible there's a better solution, but I've had similar problems where releasing right away killed my process. This is the solution I took. Again, there may be better out there, but for a quick fix, it should do the trick.
In your header file, create the following:
AVAudioPlayer *boing;
Then in -(void)playBoing, do as you did, but change
AVAudioPlayer *boing = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:nil];
to
if (boing != nil) {
boing = nil;
[boing release];
}
boing = [[AVAudioPlayer alloc] initWithContentsOfURL:
[NSURL fileURLWithPath:resourcePath] error:nil];
This should ensure that there's only one instance of boing allocated at a time
I am new in Iphone SDK programming
I need to create audio player but without array. Melodies have names "sound1, sound2, sound3..."
My code here:
NSUInteger x;
for (x=1; x<=tuneNums; x++)
{
path = [[NSBundle mainBundle] pathForResource:#"sound%d" ofType:#"wav"];
if([[NSFileManager defaultManager] fileExistsAtPath:path])
{
aSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
[aSound setNumberOfLoops:0];
aSound.delegate = self;
[aSound prepareToPlay];
}
}
All button I have and work great, but only with one mellody:( But how release 4-5 melodies i dont know :(
P.S. Forgive me for my english I'm just out of Siberia
You are allocating an AVAudioPlayer on each cycle, and you get only one 'melody' because that's the one allocated in the last cycle.
As 7KV7 pointed out, you should allocate one AVAudioPlayer when the user presses a button, play the corresponding audio file, and then release/recreate it when another button is pressed.
Something like:
- (void)button1Pressed:(id)sender {
// release the current player
if(aSound) {
[aSound release], aSound = nil;
}
// create a new player with the first file
aSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
[aSound setNumberOfLoops:0];
aSound.delegate = self;
[aSound prepareToPlay];
}
How to determine which audio file to play when a button is pressed is up to you. If you are going to have only four audio files, the simplest way is to have a fixed number of buttonPressed selectors (e.g. button1Pressed, button2Pressed) and associate them to your four buttons.
if i got you right you should add the method-
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
[player release];
}
and don't forget to set your view controller as the player delegate.
aSound.delegate=self;
Audio Player works fine if I initiate it for 20 to 30 times but after that it shows the following error
Error Domain=NSOSStatusErrorDomain Code=-43 "The operation couldn’t be completed. (OSStatus error -43.)"
The code currently I am using is as follows
- (void)playShortSound:(NSString *)fileName type:(NSString *)type
{
if (!isSound) {
return;
}
NSString *soundFilePath =[[NSBundle mainBundle] pathForResource: fileName ofType: type];
NSURL *fileURL = [[[NSURL alloc] initFileURLWithPath: soundFilePath] autorelease];
NSError *error;
AVAudioPlayer *newPlayer =[[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: &error];
//newPlayer.numberOfLoops = 0;
newPlayer.volume=1.0;
if (newPlayer == nil){
NSLog(#"%#",[error description]);
//[self playShortSound:fileName type:type];
//return;
}
else{
[newPlayer play];
newPlayer.delegate=self;
}
}
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player
successfully: (BOOL) completed {
if (completed == YES) {
NSLog(#"Sound Playing complete");
}else {
NSLog(#"error in completion of %#",player.url);
}
}
I was having the same problem and instead of init the player from url i did it from NSData.
NSString *soundFilePath =[[NSBundle mainBundle] pathForResource: fileName ofType: type];
NSData *soundData = [[NSData alloc] initWithContentsOfFile:soundFilePath];
NSError *error;
AVAudioPlayer *newPlayer =[[AVAudioPlayer alloc] initWithData: soundData
error: &error];
It seem to solved my problem hope it does yours.
By calling the method many times you are allocating the instance of AVAudioPlayer newPlayer many times. This will certainly cause you problems because of the memory allocated. Just make sure you release the objects that you own once you are done using them.
Did you set AudioSessionCategory?
{NSError *setCategoryError = nil;
BOOL success = [[AVAudioSession sharedInstance] setCategory: avaudiosessioncategoryplayandrecorderror: &setCategoryError];
}
I was trying to play files in my app by picking them from the iPod library when I got this error code. I found that in my iPod library on the device, I had a duplicate entry for the same song. Music player that comes with iOS was able to play one and not the other. That means one entry was not valid anyway. Probably removed from the device but iTunes/iOS did not do proper housekeeping.
It seems that the reference to the non-playable entry existed within the "Recently Added" playlist which is automatically created by the system. I had picked the same file from "Recently Added" playlist to play in the app.
Side Note: I had removed the file from the manually created playlist in which it existed (removed from the iOS device itself).
In short it seems that the file that I loaded into the AVAudioPlayer instance was not available on the device anymore or its referencing URL did not give back a proper file to play. So common reason for this error would be when the player does not get the file to load based on the URL given to it.