I'm writing an alarm clock app and I need to have the user select the chime to wake up to. I have a UITableView with a list of my sounds and when the user taps a sound I want it to play.
However, my sounds do not play unless I step over the call to AudioServicesPlaySystemSound (or if I put a breakpoint on the call to AudioServicesDisposeSystemSoundID which triggers after I hear the sound).
I had a theory that this was a main thread issue so I used performSelectorOnMainThread:
[self performSelectorOnMainThread:#selector(playWakeUpSound)
withObject:nil
waitUntilDone:true];
but that didn't help. (The value of waitUntilDone doesn't seem to matter.)
This question was promising but I checked and I'm only calling my method once.
This is only a guess, since I don't know what is in playWakeUpSound. If you are using AVAudioPlayer within that call and ARC, it might be because it is getting released. Store the instance of AVAudioPlayer as a member of the class and it should play.
#interface MyClass
{
AVAudioPlayer *player;
}
#end
Then in the implementation:
#implementation MyClass
- (void)playWakeUpSound {
// Assuming name and extension is set somewhere.
NSURL* musicFile = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:name ofType:extension]];
NSError* error = nil;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:&error];
if (error) {
NSLog(#"%#", error.localizedDescription);
} else {
[player play];
}
}
#end
Related
I have a series of sound files ranging from 0.3s to 3.0s.
When trying to play with AVAudioPlayer, I get nothing, unless I add a sleep(4) call after to ensure the sound can play. Really weird.
Apart from that, no errors with the error param that gets passed in, and the [player play] returns YES every time. The delegate methods are not called, though, oddly enough.
Here's my code.
(.m file)
#interface MyClass ()
#property (nonatomic, strong) AVAudioPlayer *soundPlayer;
#end
#implementation SLInspireKit
//view did load here
- (void)playRandomSound {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayback error:nil];
NSError *error;
NSString *musicFileName = [NSString stringWithFormat:#"my-sound-%d.aiff", arc4random_uniform(8)];
NSString *path = [NSString stringWithFormat:#"%#/%#", [[NSBundle mainBundle] resourcePath], musicFileName];
NSURL *soundUrl = [NSURL fileURLWithPath:path];
self.soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:&error];
self.soundPlayer.delegate = self;
[self.soundPlayer setVolume:1.0];
[self.soundPlayer prepareToPlay];
if ([self.soundPlayer play]){
NSLog(#"Played fine");
} else {
NSLog(#"Did not play fine");
}
sleep(1); //uncomment this out, and nothing. Set to 1, first second of each sound, 2, 2 seconds and so on.
}
Any ideas? I don't think it's an ARC thing, but it could be :/ I have to initiate each time because the sound changes each time.
Quick summary of the comments:
Turns out the sleep(n) was needed since the entire class containing the AVAudioPlayer was deallocated.
Retaining the class fixes the issue.
Cheers!
Okay, so I'm working on a game using SpriteKit, and there is background music throughout the app. Initially, I had just used a new AVAudioPlayer in each scene with the appropriate audio file, but now I am finding that I'd like to have uninterrupted audio playback when moving between menus and such. So I am attempting use a separate class to handle all of the background music. I'm not sure if this is technically possible, if not, I'd like to see if there's another way to solve my problem.
So onto the code... here's the header file for my audio class:
#import <AVFoundation/AVFoundation.h>
typedef NS_ENUM(int, songTitle) {
Menu_Music,
Level_1,
Level_2,
Game_Over,
};
#interface NWAudioPlayer : AVAudioPlayer
#property (nonatomic, assign) songTitle songName;
#property (nonatomic) AVAudioPlayer* bgPlayer;
-(void)createAllMusicWithAudio: (songTitle)audio;
#end
and here's the implementation:
#import "NWAudioPlayer.h"
#implementation NWAudioPlayer
-(void)createAllMusicWithAudio: (songTitle)audio {
if ([[GameState sharedGameData] audioWillPlay] == YES) {
switch (audio) {
case Menu_Music:
[self playMusicWithString:#"menuMusic"];
break;
case Level_1:
[self playMusicWithString:#"Level-1-Music"];
break;
case Level_2:
[self playMusicWithString:#"Level-2-Music"];
break;
case Game_Over:
break;
default:
break;
}
}
}
-(void)playMusicWithString: (NSString *)file {
NSString *soundFile = [[NSBundle mainBundle] pathForResource:#"menuMusic" ofType:#"m4a"];
NSURL *soundFileUrl = [NSURL fileURLWithPath:soundFile];
NSError *Error = nil;
_bgPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileUrl error:&Error];
_bgPlayer.numberOfLoops = -1;
[_bgPlayer prepareToPlay];
NSLog(soundFile);
}
#end
And then here's what I have in my scene's implementation:
-(void)createAudio
{
[[NWAudioPlayer alloc] createAllMusicWithAudio:Menu_Music];
[[NWAudioPlayer alloc].bgPlayer play];
}
and obviously I'll call [self createAudio] in the init for that scene.
The frustrating part is that I'm not getting a single error, and my NSLog is showing that the method is calling the correct audio file, but nothing is playing. Clearly I'm missing something... I'm stumped. Any help is appreciated! Thanks so much!
Separate the code that manages an AVAudioPlayer from the scene code. You could put it in your application delegate, for example, or create your own music player class with a singleton instance.
I'm developing an app that plays sounds using AVAudioPlayer, and I want to know when a sound has finished playing. I want to change an image when the sound stops playing
Here's the code I use to create the player:
NSURL* url = [[NSBundle mainBundle] URLForResource:#"LOLManFace1" withExtension:#"mp3"];
NSAssert(url, #"URL is valid.");
NSError* error = nil;
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&url];error;
if(!self.player)
{
NSLog(#"Error creating player: %#", error);
}
And here's the code I use to play the sound:
[self.player play];
How can I tell that playback has finished?
Thank you so much in advance!
Look at the AVAudioPlayerDelegate protocol which has a method to tell the delegate when audio playback has finished among other things, specifically what you are looking for is - (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag, here is a reference
Hope it helps
Daniel
Long time ago and I am pretty sure you figured out at one point. If someone reads this post:
1) Your class should implement the AVAudioPlayerDelegate. (https://developer.apple.com/documentation/avfoundation/avaudioplayerdelegate)
One of the delegate methods is:
audioPlayerDidFinishPlaying:successfully:
2) Set delegate:
self.player.delegate = self
Mucho tiempo wasted on this - I have fairly straightforward playback using AVAudioPLayer but when the file finishes playing I get this message in the debug window:
objc[39752]: Object 0x7304d80 of class _NSThreadPerformInfo
autoreleased with no pool in place - just leaking - break on
objc_autoreleaseNoPool() to debug
Here are my play and callback functions (I have simplified the filename creation but the error is the same):
#implementation PlaybackEngine {
int _pbIndex;
AVAudioPlayer *_player;
NSArray *fileList;
BufferStores *_BS;
}
....
-(BOOL)startPlayback {
NSURL *playURL = [[NSURL alloc] initWithString:[[DOCUMENTS_FOLDER stringByAppendingPathComponent:#"28Jan13_17:13:21.aif"] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
printf("Playback file URL = %s\n", [[playURL path] UTF8String]);
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:playURL error:nil];
_player.delegate = self;
[_player prepareToPlay];
[_player play];
self.playing = YES;
return YES;
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
// check for player stopping due to bad audio data
if (!flag) {
printf("Player finished playing due to bad audio data");
}
self.playing = NO;
_player = nil;
}
I've tried setting a breakpoint on objc_autoreleaseNoPool but it never gets hit. What blindingly obvious ARC mistake am I making?
Well I seem to have solved it. Apple Dev Support told me that this error should never happen, which made me think it must be some obscure bug in the 5.x SDK, but in the end I tidied up all my classes, redefining ivars as either properties or properties in an extension as appropriate, made sure all my property modifiers were correct etc and the problem has gone.
Sorry I can't explicitly pinpoint the culprit, but clearly a memory management issue.
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;