I am playing a wav file to give a little audio feedback when a button in my UI is pressed. My question is when you first press the button there is a delay (about 1.5secs) whilst the sound file "sound.wav" is loaded and cached. Is there a way to pre-cache this file (maybe in my viewDidLoad)? I guess I could do it by just playing it a viewDidLoad, but would really need to disable the audio so it does not "beeb" each time the app starts.
many thanks for and help.
gary
EDIT_001:
Looks like my question is a duplicate of this post unless anyone has any new info? Maybe a way to turn the play volume down temporarily, unless the audio is cleared each time through the run loop.
EDIT_002:
I am currently using SystemSoundID / AudioServices:
-(void)playButtonSound {
NSBundle *bundle = [NSBundle mainBundle];
NSString *soundPath = [bundle pathForResource:#"buttonClick_002" ofType:#"wav"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:soundPath], &soundID);
AudioServicesPlaySystemSound(soundID);
}
Use performSelectorInBackground:#selector(<A SELECTOR>) when the view loads to load the wav file into memory. Then play the sound when needed ;)
Hope this helps!
In the end I actually found that using AVAudioPlayer allowed my to do the preload that I required using (see below) in viewDidLoad to do the preload:
[audioPlayer prepareToPlay];
and then using (see below) in the button methods where I needed the sound to play on demand.
[audioPlayer play];
gary
Related
I'm looking to play a sound on my iPhone (iOS 6.1) depending on pressed buttons.
After converting to aif, caf, wav and compressed formats, I could not get sound on the device. Sound volumes have been checked, device has been rebooted, no uppercase letters are included in filenames.
I read many topics about this problem and could not find a proper solution, which means my problem is either more complex or plain stupid - surely plain stupid.
Here is my code to play the sound:
// Plays sound of given filename
- (void) playSound:(NSString*)name {
SystemSoundID sound = [self createSoundID:name];
AudioServicesPlaySystemSound(sound);
NSLog(#"file played: %#",name);
}
// Creates a playable sound out of filename
-(SystemSoundID) createSoundID:(NSString*) name
{
NSString *path = [NSString stringWithFormat: #"%#/%#",
[[NSBundle mainBundle] resourcePath], name];
NSURL* filePath = [NSURL fileURLWithPath: path isDirectory: NO];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)filePath, &soundID);
return soundID;
}
If the sound works fine on simulator but not in real device that means you should turn on your device's Ringer and Alert Settings. you should also check the mute button, if its' on or not.
The problem was that the method calling playSound was not called with an NSTimer. It seems that for the sounds to play, they must be called by the main thread of the application (which seems legit...).
So I modified the app so that the main thread would call those sounds and voila, it worked on the device as well. The code above was correct.
I'm using AVAudioPlayer to play a little shot sound when a user clicks a button. The sounds lasts about 3 seconds and I want that, if a user hit a button multiple times, the shot should sound multiple times. If the user clicks twice in 2 seconds, then the second sound should overlap the first shot.
My problem is that the shot only sounds every 3 seconds (if the user clicks rapidly) instead of every hit of the button.
Inside ViewDidLoad
NSString *path = [[NSBundle mainBundle] pathForResource:#"shot" ofType:#"caf"];
urlShotCaf = [NSURL fileURLWithPath:path];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:urlShotCaf error:nil] ;
[player prepareToPlay];
And when a person clicks the shot button
- (IBAction)tap:(id)sender {
clicks++;
[player play];
}
Can I do this with AVAudioPlayer? Should I use another framework?
As stated in reference here:
Play multiple sounds simultaneously, one sound per audio player, with precise synchronization
I guess you need a AVAudioPlayer for every sound you want to play simultaneously. Otherwise you could use any simple library like CocosDenshion that is really simple, easy to embed and powerful (it resides on OpenAL).
Just do
[SimpleAudioEngine sharedEngine] playEffect:#"yoursound.wav"];
and you are done.
Please try this. It works for me.
- (IBAction) tap:(id)sender {
if ([player isPlaying]) {
[player stop];
[player setCurrentTime:0.0];
}
[player play];
}
If high level frameworks fail, then you can dip down to AUSampler - an AudioUnit sample player (typically used for playback of sampled instruments, drum sounds, and so on). This should have very fast response times and support multiple active notes. Configure the sample's playback as one-shot. When the button is pressed, simulate a note on event. You could also map different samples (audio recordings) to different notes and velocity ranges.
i've created a drum app. As of now, the app's kinda satisfying but not great.
The problem is a strange delay or "lag" that sometimes happens when i press my sound buttons. When i spam a button it begins to freeze, when unfreezed again, it plays the same sound stacked on itself about 4 times :(
What am i doing wrong?
Are my samples bad?
Should i use an imageView and touchesBegan to simulate the buttons?
This is the code that i use to play a sound when a button is pushed:
- (IBAction) HHC {
NSString *path = [[NSBundle mainBundle] pathForResource:#"HHClosed"ofType:#"wav"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((CFURLRef) [NSURL fileURLWithPath:path], &soundID);
AudioServicesPlaySystemSound (soundID);
}
Please give me some good advice :)
Thanks in advance
-DD
Try doing this and see if it makes your performance better.
In your .h file declare this:
SystemSoundID soundID;
In your .m file in the -viewDidLoad method add this code:
-(void) viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"HHClosed"ofType:#"wav"];
AudioServicesCreateSystemSoundID((CFURLRef) [NSURL fileURLWithPath:path], &soundID);
}
And in the method you showed just keep this one line of code:
- (IBAction) HHC {
AudioServicesPlaySystemSound (soundID);
}
Let me know if that makes the freezing going away. If it doesn't try Core Audio
Every time you hit the button you are loading the sound file from the filesystem, bringing it into memory, and then playing it.
This is very inefficient. Your audio samples should already be loaded in memory at launch, so they don't need to be read in from the filesystem. Also, AudioServicesPlaySystemSound isn't really designed for this kind of usage - its main purpose is playing back short alert sounds (ie, a system alert). If you need low-latency audio playback you should be looking at the Core Audio framework instead.
For low latency sound appropriate for a drum app, you will want to use the RemoteIO Audio Unit API, which is not simple. You will need to know how to handle, buffer and mix PCM sound samples. Start by reading a book on digital audio technology.
I initialize an AVAudioPlayer instance:
NSString* path = [[NSBundle mainBundle] pathForResource:#"foo" ofType:#"wav"];
AVAudioPlayer* player =[[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:nil]; //Returned object not nil, indicating that the file has loaded successfully
BOOL b = [player prepareToPlay]; //returns TRUE
BOOL b2 = [player play];
//returns TRUE, goes immediately to the next line since asynchronous
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]; //wait 1 sec
printf("%10.4f\n",player.duration); //duration is 2s
printf("%10.4f\n",player.currentTime); //position remains 0
/* There is no sound output, player is also stuck since position==0 */
Does anyone know why the AVAudioPlayer is not responding? Is there something that I am overlooking while initializing, playing the audioplayer? Boilerplate code maybe?
I am running this on the iPhone simulator. Also, AudioServicesPlaySystemSound() works for the same file, which is puzzling, but this indicates that sound playback might work with AVAudioPlayer as well.
I have 2 quick suggestions:
Check to see if that path actually gives you data. Alloc a NSData object using [[NSData alloc] initWithContentsOfFile:path] and inspect it in the debugger to see if you're actually loading that wav file.
I wrote a sample project that uses AVAudioPlayer here: AVAudioPlayer Example. As the code is pretty much the same as yours, the only thing I can imagine is that there is a problem with your data.
Check those out and see if it gets you anywhere!
I have just had a similar problem. I am not sure if it is the same solution, but my application records audio through the iphone and then plays it back to the user.
I thought I was doing the correct thing by telling my audio session that it was going to record data, so I did the following:
[audioSession setCategory:AVAudioSessionCategoryRecord error:nil];
Using this setting playback worked on the Simulator but not on the device.... the didFinishPlaying event never got raised.
I removed this line after realising I didn't need it an audio playback started working correctly. Now I just need to work out why it is so quiet :)
Paul
When I use my app (on the device), the actual volume works OK for a while, but after a few days it seems to get 'stuck' at a low level.
Adjusting the volume rocker has no effect - and it shows 'Ringer' text. I've noticed that other people's apps are similarly affected if they show the 'Ringer' text when adjusting the volume. But apps which don't show 'Ringer' text are not affected by this.
How would I remove the 'Ringer' text and get my app to respond properly to different volumes?
I found some code on Apple's forums which fixes it. You have to use the AVFoundation.framework and put a bit of code into your app delegate. And put an audio file called 'blank.aif' into your project. Basically, it's a hack which prepares a file to play, but then never plays it. This fools the system into thinking that sound is being played all the time, which allows the user to use the main volume control.
#import <AVFoundation/AVFoundation.h>
//...
-(void)applicationDidFinishLaunching:(UIApplication *)application {
//volume control hack
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"blanksound" ofType:#"aif"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:soundFilePath];
AVAudioPlayer *volumeHack = [[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil]retain];
[fileURL release];
[volumeHack prepareToPlay];
// other applicationDidFinishLaunching stuff
}