I recently updated my project with Automatic Reference Counting (ARC) and it broke my playAudio methods (worked perfect before). I am new to ARC but am pretty certain it is due to the retain count or the system releasing the object immediately so you never hear the sound play at all. I create a iVar of the AudioPlayer and try to keep my own personal retain count as found in question linked below. I call out the playSystemSound: from another class, by first alloc]init; then [class playSystemSound:1002]; If anyone can point out what it is I am doing wrong that would be greatly appreciated.
AVAudioPlayer not playing any sound
#property (strong, nonatomic) AVAudioPlayer *soundToPlay;
-(void)playsystemSound:(long long)eventType{
NSString *path = [NSString stringWithFormat:#"/User/Library/Preferences/com.idd.iconomaticprefs.plist"];
NSMutableDictionary* savedDict = [NSMutableDictionary dictionary];
[savedDict addEntriesFromDictionary:[NSDictionary dictionaryWithContentsOfFile:path]];
NSMutableArray *activeThemesArray = [savedDict valueForKey:#"ActiveThemes"];
if ([activeThemesArray count]) {
NSString *soundURL;
for (NSString *name in activeThemesArray) {
soundURL = [NSString stringWithFormat:#"/Library/Themes/%#/UISounds",name];
if ([[NSFileManager defaultManager]fileExistsAtPath:soundURL]) {
//UISounds avilable in theme determine event type
if (eventType == 1002) {
//respring?
soundURL = [NSString stringWithFormat:#"/Library/Themes/%#/UISounds/lock.caf",name];
}
if (eventType == 1003) {
soundURL = [NSString stringWithFormat:#"/Library/Themes/%#/UISounds/Anticipate.caf",name];
}
if (eventType == 1004) {
soundURL = [NSString stringWithFormat:#"/Library/Themes/%#/UISounds/key_press_click.caf",name];
}
if (eventType == 1008) {
soundURL = [NSString stringWithFormat:#"/Library/Themes/%#/UISounds/photoShutter.caf",name];
}
// setup session correctly
AVAudioSession *audiosession = [AVAudioSession sharedInstance];
[audiosession setCategory:AVAudioSessionCategoryAmbient error:nil];
[audiosession setActive:YES error:nil];
// play sound
NSURL *url = [NSURL fileURLWithPath:soundURL];
self.soundToPlay = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[self.soundToPlay prepareToPlay];
[self.soundToPlay play];
break;
}
}
}
}
If there is no strong reference keeping hold of it, your AudioSession will go away as soon as the method returns.
Related
Should AVAudioPlayer be used to play sound in background or main thread? Is it a better practice to use background thread to play sound to not block the UI?
play your sound on a separate thread and I suspect your blocking issues will go away.
The easiest way to make this happen is to do:
- (void)playingAudioOnSeparateThread: (NSString *) path
{
if(_audioPlayer)
{
_audioPlayer = nil; // doing this while a sound is playing might crash...
}
NSLog(#"start playing audio at path %#", path);
NSError *error = nil;
NSData *audioData = [NSData dataWithContentsOfFile:path];
_audioPlayer = [[AVAudioPlayer alloc] initWithData:audioData error:&error];
if (error == nil)
{
[_audioPlayer play];
}
}
- (void)playAudio:(NSString *)path
{
[NSThread detachNewThreadSelector: #selector(playingAudioOnSeparateThread:) toTarget: self withObject: path];
}
I am currently using AVAudioPlayer to play an MP3 file when the method is triggered.
This works well however I would now like to add in the ability to select which audio file is played when the method is triggered from a list, this is not going so well.
I have created a UISegmentedControl and attempted to change the AVAudioPlayer method to reflect what has been selected however I am now receiving the following error when the app is launched:
'NSInvalidArgumentException', reason: '*** -[NSURL
initFileURLWithPath:]: nil string parameter'
From what I understand, I am recieving the above error because when the AVAudioPlayer is attempting to initWithContentsOfUrl:, the string being referenced does not contain any data yet, I thought it may have got this from the default segment in the UISegmentedController, however this is not the case.
I have searched for a solution for this but could not find anything which matched what i was attempting to achieve, the closest I could find was to initiate a new session of AVAudioPlayer for every audio file I wanted to play however this seemed very long winded and ultimatly may still not work.
Below is my code so far, if you could suggest how I could make this work, I would appreciate it.
for the UISegmentedController:
- (void) selectTone:(id)sender
{
if (self.alarmToneType == 0) {
self.filePath = [[NSBundle mainBundle] pathForResource: #"alarm" ofType: #"mp3"];
NSLog(#"Tone Selected 0 %#", [HRASettingsViewController alarmToneDescription:(int) self.alarmToneType]);
}
if (self.alarmToneType == 1) {
self.filePath = [[NSBundle mainBundle] pathForResource: #"redalert" ofType: #"mp3"];
NSLog(#"Tone Selected 1 %#", [HRASettingsViewController alarmToneDescription:(int) self.alarmToneType]);
}
if (self.alarmToneType == 2) {
self.filePath = [[NSBundle mainBundle] pathForResource: #"greenalaert" ofType: #"mp3"];
NSLog(#"Tone Selected 2 %#", [HRASettingsViewController alarmToneDescription:(int) self.alarmToneType]);
}
}
I have created the string as a #property above but didn't think you would need that.
for the AVAudioPlayer:
- (void) setupAppAudio {
/////////////////////////////////////////////////////////////////////////////////////
// SOUNDS
// Registers this class as the delegate of the audio session.
[[AVAudioSession sharedInstance] setActive:YES error:nil];
// Use this code instead to allow the app sound to continue to play when the screen is locked.
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: &sessionError];
if (sessionError) {
NSLog(#"AVAudioSession ERROR %#", sessionError);
} else {
NSLog(#"AVAudioSession SUCCESSFUL");
}
// Tell other music to be quiet when we are talking!
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OtherMixableAudioShouldDuck,
sizeof (doSetProperty),
&doSetProperty);
// Registers the audio route change listener callback function
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
(__bridge void *)(self));
NSError *activationError = nil;
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
// setup
//NSString *filePath;
AVAudioPlayer *newPlayer;
newPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: [NSURL fileURLWithPath:self.filePath] error: nil];
self.beepPlayer = newPlayer;
// "Preparing to play" attaches to the audio hardware and ensures that playback
// starts quickly when the user taps Play
[self.beepPlayer prepareToPlay];
[self.beepPlayer setVolume: 1.0];
[self.beepPlayer setDelegate: self];
}
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!
I am trying to make a background music in my game with AVAudioPlayer, so I set this code at the initWithSize method of my SKScene:
//SETTING BACKGROUND MUSIC//
NSError * err = nil;
NSURL * musicFile = [[NSBundle mainBundle] URLForResource:#"Cosmic Rush" withExtension:#"m4a"];
music = [[AVAudioPlayer alloc] initWithContentsOfURL:musicFile error:&err];
if(err)
{
NSLog(#"%#", [err userInfo]);
return;
}
[music prepareToPlay];
music.numberOfLoops = -1;
[music setVolume:0.5];
//SETTING BACKGROUND MUSIC//
Then, in another method: [music play]; However, the sound never plays, and I have tried all sort of extensions: .mp3, .caf, .mp4 etc. I have also checked people who had the same problem, but none of the solutions that solved their problems worked with mine.
there is a easier way to add background music to your scene. i use the following steps and works just as good as any other methods and it is easier.
import the #import AVFoundation; at the top of your scene.
add it as a property right after your #implementation.
AVAudioPlayer *_backgroundMusicPlayer;
then declare a method for the music
- (void)playBackgroundMusic:(NSString *)filename
{
NSError *error;
NSURL *backgroundMusicURL =
[[NSBundle mainBundle] URLForResource:filename
withExtension:nil];
_backgroundMusicPlayer =
[[AVAudioPlayer alloc]
initWithContentsOfURL:backgroundMusicURL error:&error];
_backgroundMusicPlayer.numberOfLoops = -1;
[_backgroundMusicPlayer prepareToPlay];
[_backgroundMusicPlayer play];
}
after that create a reference to that method in your -(id)initWithSize:(CGSize)size like the following
[self playBackgroundMusic:#"bgMusic.mp3"];
that should take care of the background music for you.
I want to loop my background music with an SKAction but the music stops after one row when I switch to an other scene. Is there a way to start the loop and keep playing it over different scenes?
Right now the code is placed in the init method of MyScene - is that the correct place? Maybe didFinishLaunchingWithOptions?
Here is what I've tried:
if (delegate.musicOn == YES && delegate.musicIsPlaying == NO) {
SKAction *playMusic = [SKAction playSoundFileNamed:#"loop.wav" waitForCompletion:YES];
SKAction *loopMusic = [SKAction repeatActionForever:playMusic];
[self runAction:loopMusic];
delegate.musicIsPlaying = YES;
}
You can't play background music over the scenes with using SKActions. Use AVAudioPlayer instead:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"music" ofType:#"mp3"];
NSError *error;
AVAudioPlayer *gameSceneLoop = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:filePath] error:&error];
if (error) {
NSLog(#"Error in audioPlayer: %#", [error localizedDescription]);
} else {
gameSceneLoop.numberOfLoops = -1;
[gameSceneLoop prepareToPlay];
[gameSceneLoop play];
}
In iOS 9 use SKAudioNode for:
let backgroundMusic = SKAudioNode(fileNamed: "music.wav")
self.addChild(backgroundMusic)