I want to play multiple audio files (.WAV) using IBAction and AVAudioPlayer. Unfortunatelly the sound plays but if I play the sound many times, my application crashes. Can you help me?
Here's my code.
ViewController.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
NSString *Path;
}
- (IBAction)Sound1;
- (IBAction)Sound2;
- (IBAction)Sound3;
- (IBAction)Sound4;
#end
ViewController.m
#import <AVFoundation/AVAudioPlayer.h>
#import "ViewController.h"
#implementation ViewController
AVAudioPlayer *Media;
- (IBAction)Sound1
{
Path = [[NSBundle mainBundle] pathForResource:#"Sound1" ofType:#"wav"];
Media = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:Path] error:NULL];
[Media setDelegate:self];
[Media play];
}
- (IBAction)Sound2
{
Path = [[NSBundle mainBundle] pathForResource:#"Sound2" ofType:#"wav"];
Media = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:Path] error:NULL];
[Media setDelegate:self];
[Media play];
}
- (IBAction)Sound3
{
Path = [[NSBundle mainBundle] pathForResource:#"Sound3" ofType:#"wav"];
Media = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:Path] error:NULL];
[Media setDelegate:self];
[Media play];
}
- (IBAction)Sound4
{
Path = [[NSBundle mainBundle] pathForResource:#"Sound4" ofType:#"wav"];
Media = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:Path] error:NULL];
[Media setDelegate:self];
[Media play];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[player release];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (void)dealloc
{
[Media Release];
[super dealloc];
}
#end
There are a couple things that look wrong in your code:
(1). There are no method Release, [Media Release] should be [Media release];
(2). If you play Sound2 while Sound1 is still playing, you leak Media instance:
Media = [[AVAudioPlayer alloc] initWithContentsOfURL:...
This allocates new player and overwrites old one without releasing it first;
(3). It is usually bad idea to release calling object in delegate;
(4). I'd also suggest to rename Media to media and Path to path.
So playing action should look like this:
- (IBAction)playSound1
{
path = [[NSBundle mainBundle] pathForResource:#"Sound1" ofType:#"wav"];
media = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[media play];
[media release];
}
Related
I have an audio player and it won't play the sound, has someone any idea why?
And i have imported AVFoundationFramework.h
This is the code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURL* musicFile = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"begin" ofType:#"mp3"]];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:nil];
[audioPlayer play];
audioPlayer.numberOfLoops=-1;
}
Add a delegate:
[audioPlayer setDelegate:self];
and make sure your volume is correctly set.
NSString *musicPath = [[NSBundle mainBundle] pathForResource:#"ding" ofType:#"mp3"];
if (musicPath)
{
if(TapSoud)
{
[TapSoud release];
TapSoud = nil;
}
TapSoud = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:musicPath] error:nil];
}
[TapSoud setDelegate:self];
[TapSoud prepareToPlay];
[TapSoud play];
NSString *musicPath1 = [[NSBundle mainBundle] pathForResource:#"roadrunner" ofType:#"mp3"];
if (musicPath1)
{
if(MatchSoud)
{
[MatchSoud release];
MatchSoud = nil;
}
MatchSoud = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:musicPath1] error:nil];
}
[MatchSoud setDelegate:self];
[MatchSoud prepareToPlay];
NSString *musicPath2 = [[NSBundle mainBundle] pathForResource:#"Wrong Answer" ofType:#"wav"];
if (musicPath2)
{
if(WrongMatchSoud)
{
[WrongMatchSoud release];
WrongMatchSoud = nil;
}
WrongMatchSoud = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:musicPath2] error:nil];
}
[WrongMatchSoud setDelegate:self];
[WrongMatchSoud prepareToPlay];
This code works properly in the simulator, but generates no sound on the device. What is the problem here?
The most likely problem is that the audio files are missing either in the project or at resource copying stage. The simulator stores previously copied resources even if removed from the project until the application is removed completely from the simulator.
Make sure musicPath, musicPath1 and musicPath2 are not empty and valid.
When I press a button, then press another one, the sounds overlap. How can I fix that so the first sound stops when another one is pressed?
- (void)playOnce:(NSString *)aSound {
NSString *path = [[NSBundle mainBundle] pathForResource:aSound ofType:#"caf"];
AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[theAudio setDelegate: self];
[theAudio setNumberOfLoops:0];
[theAudio setVolume:1.0];
[theAudio play];
}
- (void)playLooped:(NSString *)aSound {
NSString *path = [[NSBundle mainBundle] pathForResource:aSound ofType:#"caf"];
AVAudioPlayer* theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[theAudio setDelegate: self];
// loop indefinitely
[theAudio setNumberOfLoops:-1];
[theAudio setVolume:1.0];
[theAudio play];
[theAudio release];
}
Declare your AVAudioPlayer in the header the viewController ( don't alloc a new one each time you play a sound). That way you will have a pointer you can use in a StopAudio method.
#interface myViewController : UIViewController <AVAudioPlayerDelegate> {
AVAudioPlayer *theAudio;
}
#property (nonatomic, retain) AVAudioPlayer *theAudio;
#end
#implementation myViewController
#synthesize theAudio;
- (void)dealloc {
[theAudio release];
}
#end
- (void)playOnce:(NSString *)aSound {
NSString *path = [[NSBundle mainBundle] pathForResource:aSound ofType:#"caf"];
if(!theAudio){
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath:path] error:NULL];
}
[theAudio setDelegate: self];
[theAudio setNumberOfLoops:0];
[theAudio setVolume:1.0];
[theAudio play];
}
- (void)playLooped:(NSString *)aSound {
NSString *path = [[NSBundle mainBundle] pathForResource:aSound ofType:#"caf"];
if(!theAudio){
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath:path] error:NULL];
}
[theAudio setDelegate: self];
// loop indefinitely
[theAudio setNumberOfLoops:-1];
[theAudio setVolume:1.0];
[theAudio play];
}
- (void)stopAudio {
[theAudio stop];
[theAudio setCurrentTime:0];
}
also be sure to read the Apple Docs
The code you've posted will only play one sound, the first sound sent to the method.
If you want to only play one changeable sound, after stoping the player, release theAudio and then set the new sound.
Add a check to see if the player is already playing at the beginning of each method:
if (theAudio.playing == YES) {
[theAudio stop];
}
AVAudioPlayer Class Reference
In your playOnce method the 'path' variable is unused -- remove that to get rid of your warning message. Your playOnce does not set anything up to play so I'm not sure how that is supposed to work -- unless you call playLooped first? You also need to be calling prepareToPlay after the initWithContentsOfUrl.
- (void)playOnce:(NSString *)aSound {
NSString *path = [[NSBundle mainBundle] pathForResource:aSound ofType:#"caf"];
if (theAudio && [theAudio isPlaying]) {
[theAudio stop]
} else {
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath: path] error: NULL];
[theAudio prepareToPlay];
[theAudio setDelegate: self];
[theAudio setNumberOfLoops: 1];
[theAudio setVolume: 1.0];
[theAudio play];
}
}
You will need to utizilize a BOOL value to get this to work properly.
in your .m file BEFORE #implementation put this:
static BOOL soundIsPlaying = NO;
Then your IBAction needs to look something like this:
- (IBAction)play {
if (soundIsPlaying == YES) {
[theAudio release];
soundIsPlaying = NO;
}
else if (soundIsPlaying == NO) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"SOUNDFILENAME" ofType:#"wav"];
theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
theAudio.volume = 1.0;
theAudio.numberOfLoops = 0;
[theAudio play];
soundIsPlaying = YES;
}
}
thats honestly about it. It will stop sounds when another button is pressed.
You can probably do something like the following:
(void) playLooped: (NSString * ) aSound {
NSString * path = [[NSBundle mainBundle] pathForResource: aSound ofType: #"caf"];
//stop audio player from playing so the sound don't overlap
if([theAudio isPlaying])
{
[theAudio stop]; //try this instead of stopAudio
}
if (!theAudio) {
theAudio = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath: path] error: NULL];
}
[theAudio setDelegate: self];
// loop indefinitely
[theAudio setNumberOfLoops: -1];
[theAudio setVolume: 1.0];
[theAudio play];
}
I am trying to make an audio controller, but the stop button doesn't work. But I don't know why. How is this caused and how can I solve it?
.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface myprojectViewController : UIViewController {
AVAudioPlayer* theAudio;
}
- (IBAction)start:(id)sender;
- (IBAction)stop:(id)sender;
#end
.m
- (IBAction)start:(id)sender{
NSString *path = [[NSBundle mainBundle] pathForResource:#"LE" ofType:#"wav"];
AVAudioPlayer* theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate = self;
[theAudio play];
}
- (IBAction)stop:(id)sender{
[theAudio stop];
}
'theAudio' is a local variable in your start() method. You've declared it as a local variable, so it is out of scope in your stop() method, and you have an invalid reference. You need to use the class variable 'theAudio. Change :
AVAudioPlayer* theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
to
self.theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
I am currently loading an audio file inside of a button. I am assuming there is a more efficient way to load this file instead of every time the button is pressed. Where should this be loaded? Globally, inside viewdidload. I'm loading it like the code below.
NSString *path = [[NSBundle mainBundle] pathForResource:#"click" ofType:#"mp3"];
AVAudioPlayer* theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate=self;
viewDidLoad is a good idea -- will let you free up the memory in viewDidUnload too in case you are short on memory. You could also implement getters and setters in the code below by synthesizing theAudio and using self.theAudio
#interface myClass {
AVAudioPlayer *theAudio;
}
#implementation myClass {
- (void) viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"click" ofType:#"mp3"];
theAudio=[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
theAudio.delegate=self;
}
- (void) viewDidUnload {
[theAudio release];
}
}