SystemSoundID Stopping Sounds - ios

In my application, I have buttons setup as a keyboard. Each buttons acts similar to a piano key, where you touch the button and the sound plays when you hold but stops when you release. For this method, I am using two IBActions. One for Touch Down (Sound Play) and Touch Up Inside (Sound Stop). I am using SystemSoundID for playing the sounds, as AVAudioPlayer has a delayed play. The only problem is, when I hold down the button the sound plays fine, but once I release the touch my app crashes. Below is my Code:
.h File:
SystemSoundID *soundID;
.m File:
- (IBAction)ASoundStart:(id)sender {
NSString *soundFile = [[NSBundle mainBundle] pathForResource:#"P1:4t" ofType:#"mp3"];
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:soundFile], &soundID);
AudioServicesPlaySystemSound(soundID);
[soundFile release];
}
- (IBAction)ASoundStop:(id)sender {
AudioServicesDisposeSystemSoundID(soundID);
}

I believe you're treating SystemSoundID incorrectly as an object. It's simply an integer.
If you take a look at the declaration of AudioServicesPlaySystemSound, notice that it's argument is SystemSoundID and not SystemSoundID *
void AudioServicesPlaySystemSound (
SystemSoundID inSystemSoundID
);
So if you just change SystemSoundID *soundID in your header to SystemSoundID soundID I believe it should work.
Happy coding.

Related

Audio overlap issue

I'm developing a iOS app using objective-c. When the application is launched a background music is played. The background music should continue playing when the user clicks help button. Also when the user goes back to the main screen from the help screen the background music should be continuously playing.
For me a new background music is getting played along with the old background music when I navigate from help to main menu. So, I am hearing two background music now.
Could anyone help me in solving this issue?
Regards,
Bharathi.
Your problem could be solved if you retained a reference to your audio player in your UIApplicationDelegate (or some other singleton that's kept around).
//in the .h
#property (nonatomic, strong) AVAudioPlayer *player;
//in the .m
- (void) playMusic
{
if (self.player == nil) {
NSString *path = [NSString stringWithFormat:#"%#/music.mp3", [[NSBundle mainBundle] resourcePath]];
NSURL *soundUrl = [NSURL fileURLWithPath:path];
self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
}
if (!self.player.isPlaying) {
[self.player play];
}
}
That way you can call it wherever you need with a:
[(MyAppDelegate*)[UIApplication sharedApplication].delegate playMusic];
Though it might be to your advantage to keep around a SoundsManager class as a singleton in order to handle all the sounds that you'll need to track if you're going to need more than just this one.

AudioToolBox not working since xcode update

I have an app that uses audiotoolbox framework to play sounds. It used to work fine but since i updated my xcode to 6.1.1 the sounds don't play.
My code is fairly simple:
.h
#import <AudioToolbox/AudioToolbox.h>
#interface ViewController : UIViewController < SKProductsRequestDelegate, SKPaymentTransactionObserver, ADBannerViewDelegate > {
SystemSoundID playSound1;
}
-(IBAction)playSound1:(id)sender;
.m
-(void)viewDidLoad{
NSURL *SoundURL1 = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"airhorn1" ofType:#"wav"]];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)SoundURL1, &playSound1);
}
-(IBAction)playSound1:(id)sender{
AudioServicesPlaySystemSound(playSound1);
NSLog(#"play this sound");
}
I then link the action to the button in the storyboard and when i launch the simulator and tap the button i see the "Play this sound" log but the sound doesn't play. Any idea on how this fix this?
AudioServicesCreateSystemSoundID returns a result code. You should verify that the result of that call is kAudioServicesNoError. Depending on the result, you may be able to debug this further.
Documentation on the result codes is available here.

How can I make audio play when the button is pressed and immediately stop when I take my finger off the same button?

My current code for the button is.
-(IBAction)playsnare;
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundfileURLRef;
soundfileURLRef =CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"snare", CFSTR ("wav"), NULL);
UInt32 SoundID;
AudioServicesCreateSystemSoundID(soundfileURLRef, &SoundID);
AudioServicesPlaySystemSound(SoundID);
}
What can I add to this to make the button play when pressed and stop when not pressed.
Your implementation for playing audio samples does not allow for this. Because you are using AudioServicesPlaySystemSound from the AudioToolbox framework, there is no way to stop playback once the audio sample is triggered. You can only get a callback once it has finished playing.
This way of playing sounds has other limitations also (taken from the docs)
In addition, when you use the AudioServicesPlaySystemSound function:
Sounds play at the current system audio volume, with no programmatic
volume control available
Sounds play immediately
Looping and stereo positioning are unavailable
Simultaneous playback is unavailable: You can play only one sound at a
time
The sound is played locally on the device speakers; it does not use
audio routing.
To have control over stopping the sound once triggered, you'll need to use another API. For a high level API try AVAudioPlayer class from AVFoundation framework. You will then be able to hook up events from a button such as touch down to start playing the sound, and also events such as touch up inside and touch drag outside to stop the sound playing.
You may or may not encounter performance issues depending on what else you're doing. For better performance (and a lot more writing of code) AudioUnits would be the preferred choice.
A short example using AVAudioPlayer class would be something like this-
The following code assumes a property of type AVAudioPlayer named
loopPlayer that is initialised with a file called some_audio_loop_file.wav in a UIViewController sub-class.
yourViewController.h
#property (nonatomic, strong) AVAudioPlayer *loopPlayer;
yourViewController.m
// set up loopPlayer property in for example viewDidLoad
NSURL* audioFileURL = [[NSBundle mainBundle] URLForResource:#"some_audio_loop_file" withExtension:#"wav"];
self.loopPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:audioFileURL error:nil];
// called from a UIButton touch down event
-(IBAction)startAudioSample:(id)sender {
[self.loopPlayer play];
}
// called from a UIButton touch up inside and touch drag outside events
-(IBAction)stopAudioSample:(id)sender {
if (self.loopPlayer.isPlaying) {
[self.loopPlayer stop];
self.loopPlayer.currentTime = 0;
self.loopPlayer.numberOfLoops = -1;
[self.loopPlayer prepareToPlay];
}
Try:
-(IBAction)longPress:(UILongPressGestureRecognizer*)playsnare;
{
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef soundfileURLRef;
soundfileURLRef =CFBundleCopyResourceURL(mainBundle, (CFStringRef) #"snare", CFSTR ("wav"), NULL);
UInt32 SoundID;
AudioServicesCreateSystemSoundID(soundfileURLRef, &SoundID);
AudioServicesPlaySystemSound(SoundID);
}
UILongPressGestureRecognizer is a continuous event recognizer. You have to look at the state to see if this is the start, middle or end of the event and act accordingly. i.e. you can through away all events after the start, or only look at movement as you need. From the Class Reference:
Long-press gestures are continuous. The gesture begins (UIGestureRecognizerStateBegan) when the number of allowable fingers (numberOfTouchesRequired) have been pressed for the specified period (minimumPressDuration) and the touches do not move beyond the allowable range of movement (allowableMovement). The gesture recognizer transitions to the Change state whenever a finger moves, and it ends (UIGestureRecognizerStateEnded) when any of the fingers are lifted.
Now You Can Track The State Like This
- (void)handleLongPress:(UILongPressGestureRecognizer*)sender {
if (sender.state == UIGestureRecognizerStateEnded) {
NSLog(#"UIGestureRecognizerStateEnded");
//Do Whatever You want on End of Gesture
}
else if (sender.state == UIGestureRecognizerStateBegan){
NSLog(#"UIGestureRecognizerStateBegan.");
//Do Whatever You want on Began of Gesture
}
}
detail from: https://stackoverflow.com/a/3320351/1255945
Create a property
bool isPlaying
and then as in your IBAction function
if(isPlaying){
stop();
}else{
play();
}

Playing the sent email swoosh sound

I would like to close a view controller and get a swoosh sound similar to sending an email. Any idea how I could approach this?
You can use either the System Sound or the AVPlayer Classes for your sound. The UIViewController Class has a viewWillDisappear: and a viewDidDisappear:. You can insert your code there.
Use the Audio Toolbox:
#include <AudioToolbox/AudioToolbox.h>
Load your audio resource like this:
NSURL* soundUrl = [[NSBundle mainBundle] URLForResource: #"emailsent" withExtension: #"aif"];
AudioServicesCreateSystemSoundID ((__bridge CFURLRef)(soundUrl), &emailSentSoundId);
Where emailSentSoundId should be declared as:
SystemSoundID emailSentSoundId;
Play the sound like this:
AudioServicesPlaySystemSound(emailSentSoundId);

ios SystemSound will not respond to volume buttons

Ive used SystemSound in my app in order to play simple sound effects. In addition to this I play a musicvideo through the MPMoviePlayerController - now when I turn the volume up/down the music from the video responds as intended (lowering the volume up/down).
But the system sounds that are played does not respond to the volume. Im playing the system sounds when the user taps certain areas in the app. Heres a snippet of my code for that:
- (void)handleTap:(UITapGestureRecognizer *)recognizer {
SystemSoundID completeSound = nil;
//yellow folder in xcode doesnt need subdirectory param
//blue folder (true folder) will need to use subdirectory:#"dirname"
NSURL *sound_path = [[NSBundle mainBundle] URLForResource: target_sound_filename withExtension: #"wav"];
AudioServicesCreateSystemSoundID((__bridge CFURLRef)sound_path, &completeSound);
AudioServicesPlaySystemSound(completeSound);
}
PS. I doublechecked that my "Settings->Sounds->Ringer and Alerts->Change With Buttons" is set to ON (as I read on some other SO answers that leaving this option OFF will cause systemsound to not respond to the volume buttons)
Further the reason for using systemsound is that it gave the most accurate and responsive results when playing multiple sounds (like in a game).
Id prefer to not use OpenAL if possible (even through 3rd party sound libraries like Finch or CocosDenshion)
Any ideas?
Use the AVAudioPlayer class for playing sounds that are controlled by the user's volume settings (non-system sounds).
You can retain instances of AVAudioPlayer for each sound file that you use regularly and simply call the play method. Use prepareToPlay to preload the buffers.
Cheers to Marcus for suggesting that i could retain instances of AVAudioPlayer for each sound file and use prepareToPlay to preload the sounds. It might be to help for others looking for the same solution so here is how I did it (feel free to comment if anyone have suggestions for improvements)
//top of viewcontroller.m
#property (nonatomic, strong) NSMutableDictionary *audioPlayers;
#synthesize audioPlayers = _audioPlayers;
//on viewDidLoad
self.audioPlayers = [NSMutableDictionary new];
//creating the instances and adding them to the nsmutabledictonary in order to retain them
//soundFile is just a NSString containing the name of the wav file
NSString *soundFile = [[NSBundle mainBundle] pathForResource:s ofType:#"wav"];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:soundFile] error:nil];
//audioPlayer.numberOfLoops = -1;
[audioPlayer prepareToPlay];
//add to dictonary with filename (omit extension) as key
[self.audioPlayers setObject:audioPlayer forKey:s];
//then i use the following to play the sound later on (i have it on a tap event)
//get pointer reference to the correct AVAudioPlayer instance for this sound, and play it
AVAudioPlayer *foo = [self.audioPlayers objectForKey:target_sound_filename];
[foo play];
//also im not sure how ARC will treat the strong property, im setting it to nil in dealloc atm.
-(void)dealloc {
self.audioPlayers = nil;
}

Resources