I'm simply trying to do something when an audio file has finished playing, but it doesn't seem to work.
In my .h file I've got this:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
#interface soundViewController : UIViewController
<AVAudioPlayerDelegate>
{
//various outlets
}
#property (strong, nonatomic) AVAudioPlayer *audioPlayer;
#end
And in the .m file I've got:
#import "soundViewController.h"
#interface soundViewController ()
#end
#implementation soundViewController
#synthesize audioPlayer;
And the method's:
- (void) playWord{
Word *currentWord = [[Word alloc]init];
currentWord = [testWords objectAtIndex:wordNum-1];
NSString *item = [currentWord word];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/%#.mp3", [[NSBundle mainBundle] resourcePath], item]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 0;
if (audioPlayer == nil){
NSLog(#"error in audioPlayer");
}
else{
[audioPlayer play];
NSLog(#"Playing word");
}
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
if (flag==YES){
NSLog(#"Finished!");
}
}
So... it all works swimmingly (apart from the fact that the sound file won't work if it starts with a capital letter), but why am I not seeing the log message "Finished" when the sound finishes?
Thanks for your help,
Morten
You actually need to set the delegate of the player after you instantiate it, like:
audioPlayer.delegate = self;
Hope this helps!
Related
I have two AVAudioPlayer to determine what sound has finished to do something after that. the first sound plays just fine but the second sound (twoPlayer) plays the sound with echo. any of you knows why or how this happend?
I have this on my .h file:
#interface myClass : UIViewController<AVAudioPlayerDelegate>
{
AVAudioPlayer *onePlayer;
AVAudioPlayer *twoPlayer;
}
#property (retain, nonatomic) AVAudioPlayer *onePlayer;
#property (retain, nonatomic) AVAudioPlayer *twoPlayer;
.m file:
NSString *urlAddress = [[NSBundle mainBundle] pathForResource:#"sound" ofType:#"m4a"];
NSURL *url = [NSURL fileURLWithPath:urlAddress];
NSError *error;
twoPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
twoPlayer.delegate=self;
twoPlayer.volume=0.5;
if (twoPlayer == nil)
NSLog(#"[error description]");
else
[twoPlayer play];
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
if (player==onePlayer) {
[onePlayer release];
}
if (player==twoPlayer) {
[numberPlayer release];
}
}
I found the error I was setting a UIButton with "forControlEvents:UIControlEventAllTouchEvents" and the actionw was been trigger more then once. I replace the UIButton with this forControlEvents:UIControlEventTouchDown and works just fine.
I created a custom class to handle audio playback.
AudioPlayback.h
#import <Foundation/Foundation.h>
#import "AVFoundation/AVAudioPlayer.h"
#import <AVFoundation/AVFoundation.h>
#interface AudioPlayback : NSObject <AVAudioPlayerDelegate> {
AVAudioPlayer *player;
BOOL playing;
NSTimeInterval duration;
}
#property (nonatomic, assign) AVAudioPlayer *player;
#property (readonly) BOOL playing;
#property (readonly) NSTimeInterval duration;
-(NSTimeInterval)GetSoundFileDuration:(NSString *) sound_file_name;
-(void)PlaySoundFile:(NSString *) sound_file_name;
-(void)play;
-(void)stop;
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
#end
Then, in AudioPlayback.h
#import "AudioPlayback.h"
#implementation AudioPlayback
#synthesize player;
-(id)init
{
self = [super init];
if (self != nil)
{
player = [[AVAudioPlayer alloc] init];
[player initWithContentsOfURL:nil error:nil];
player.delegate = self;
}
return self;
}
-(void)PlaySoundFile:(NSString *) sound_file_name
{
NSURL *sound_file = [[NSURL alloc] initFileURLWithPath: [[NSBundle mainBundle] pathForResource:sound_file_name ofType:#"mp3"]];
[player initWithContentsOfURL:sound_file error:nil];
[player prepareToPlay];
[player play];
[sound_file release];
}
...
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(#"audioPlayerDidFinishPlaying");
}
The callback never triggers. Am I missing anything obvious?
I think "player.delegate = self" is call too early. You "initWithContentsOfURL" again in your "PlaySoundFile", which I think that cause the problem.
Try to put "player.delegate = self" before the "[player prepareToPlay]" in your "PlaySoundFile" method
I added a sound file in my Xcode resources folder.
I want to play the sound file by AVAudioPlayer
I can use the image by [UIImage imageNamed:#"xxxx.png"];
But I don't know how to attach the file to the AVAudioPlayer.
Thank you for helping me.
In the .h file
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#interface RootViewController : UIViewController {
AVAudioPlayer *audioPlayer;
}
#property(nonatomic, retain) AVAudioPlayer *audioPlayer;
& also synthesize this in your .m file
Now in your .m file
NSString *mp3Path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"test.mp3"];
NSURL *mp3Url = [NSURL fileURLWithPath:mp3Path];
NSError *err;
AVAudioPlayer *audPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:mp3Url error:&err];
[self setAudioPlayer:audPlayer];
if(audioPlayer)
[audioPlayer play];
[audPlayer release];
Also add the follwing frameworks
AudioToolbox.framework & AVFoundation.framework
Hope it helps
Im doing an application that needs to play alot of short sounds (mp3-files). Im using AvAudioPlayer and the sounds are playing just fine, BUT the leaks are building up until my app crashes.
I have a seperate class for the player
AVSnd.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#interface AVSoundPlayer : NSObject <AVAudioPlayerDelegate> {
AVAudioPlayer *msoundPlayer;
}
#property (nonatomic, retain) AVAudioPlayer *msoundPlayer;
-(id)initWithMp3File: (NSString *)inString;
-(void) playNum:(int)num;
#end
AVSND.m
#implementation AVSoundPlayer
#synthesize msoundPlayer;
-(id)initWithMp3File: (NSString *)fileName{
if (self = [super init]){
NSBundle *mainBundle = [NSBundle mainBundle];
NSError *error;
NSURL *sURL = [NSURL fileURLWithPath:[mainBundle
pathForResource:fileName ofType:#"mp3"]];
self.msoundPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:sURL error:&error];
if (!self.msoundPlayer) {
NSLog(#"Sound player problem: %#", [error localizedDescription]);
}
}
return self;
}
-(void) playNum:(int)num{
self.msoundPlayer.numberOfLoops = num;
[self.msoundPlayer prepareToPlay];
AVAudioPlayer *tmpPlayer = self.msoundPlayer;
[tmpPlayer play];
}
- (void)dealloc {
[self.msoundPlayer release];
[super dealloc];
}
#end
Then I make an instance of this object in the views i want sound.
in .h files I add following lines:
#class AVSnd;
AVSnd *mPlayer;
#property (nonatomic, retain) AVSnd *mPlayer;
and in .m files i use:
#synthezise mPlayer;
[self.mPlayer initWithMp3File:#"soundFileName"];
[self.mPlayer playNum:1];
[self.mPlayer release];
But why do I get memory-leaks every time i play a sound? Should i implement the player in another way?
Thank you so much for any help!
I had the same problem and solved it with autorelease:
self.audioPlayer = [[[AVAudioPlayer alloc]
initWithContentsOfURL:url error:&error] autorelease];
self.msoundPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:sURL error:&error];
Here you are retaining the object twice, in self. (because of the property), and when alloc. It could be the reason.
I am using the AVAudioPlayer framework, and I have several sounds that play one at a time. When a sound is finished playing, I want the application to do something. I tried to use audioPlayerDidFinishPlaying to do the action at the end of the first sound, but I couldn't use that for the second sound because I got a redefinition error. Can I use NSTimeInterval to get the duration of a sound?
// ViewController.h
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AVFoundation/AVAudioPlayer.h>
#interface ViewController : UIViewController {
UIButton *begin;
UIWindow *firstTestWindow;
UIWindow *secondTestWindow;
AVAudioPlayer *player;
NSTimeInterval duration;
}
#property (nonatomic, retain) IBOutlet UIButton *begin;
#property (nonatomic, retain) IBOutlet UIWindow *firstTestWindow;
#property (nonatomic, retain) IBOutlet UIWindow *secondTestWindow;
#property (nonatomic, retain) AVAudioPlayer *player;
#property (readonly) NSTimeInterval duration;
- (IBAction)begin:(id)sender;
- (IBAction)doTapScreen:(id)sender;
#end
//ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize begin, player, firstTestWindow, secondTestWindow;
//first sound
- (IBAction)begin:(id)sender; {
[firstTestWindow makeKeyAndVisible];
NSString *soundFilePath =
[[NSBundle mainBundle] pathForResource: #"Tone1"
ofType: #"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer *newPlayer =
[[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: nil];
[fileURL release];
self.player = newPlayer;
[newPlayer release];
[player prepareToPlay];
[self.player play];
}
//If user does not do anything by the end of the sound go to secondWindow
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player
successfully: (BOOL) flag {
if (flag==YES) {
[secondWindow makeKeyAndVisible];
}
}
//second sound
- (IBAction)doTapScreen:(id)sender {
//When user touches the screen, either play the sound or go to the next window
if (self.player.playing) {
[self.player stop];
[thirdWindow makeKeyAndVisible];
}
else {
NSString *soundFilePath =
[[NSBundle mainBundle] pathForResource: #"Tone2"
ofType: #"mp3"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer *newPlayer =
[[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: nil];
[fileURL release];
self.player = newPlayer;
[newPlayer release];
[player prepareToPlay];
[self.player play];
}
}
When a sound is finished playing, I want the application to do something.
Then set yourself as the delegate and respond to audioPlayerDidFinishPlaying:successfully:.
In the code snippet you showed, you've done step 2 but not step 1: You're not setting yourself as the delegate.
I tried to use applicationDidFinishLaunching to do the action at the end of the first sound …
Brain fart? That method has nothing that I can see to do with playing sounds.
…, but I couldn't use that for the second sound because I got a redefinition error.
Huh? Did you implement the method twice or something instead of just comparing the player object in the method body?
It would help if you showed the exact error message you got.
Swift 3:
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
print("sound file finished")
}