views:

5552

answers:

8

Is there a way to programmatically invoke the keypad "click" sound? My app has a custom keypad (built out of UIButtons) and I'd like to provide some audio feedback when the user taps on the keys. I tried creating my own sounds in Garageband, but wasn't happy with any of my creations. If there isn't a standard way to invoke the key click, can anyone point me to a library of sounds that might have such a gem?

+3  A: 

The simplest way I've found is to extract Tock.aiff (the keyboard sound) from the iPhone Simulator and package it with your app, then play it using AudioServicesPlaySystemSound() at the appropriate time. On my machine, simply typing Tock.aiff into Spotlight turns up the file, but if you have to go looking for it, it's in the simulator version of UIKit.framework.

Brent Royal-Gordon
I had actually done this with "Tock.caf" from the Metronome example for testing purposes, but was reluctant to include it in the finished product for fear of copyright issues. Is this "allowed"?
Caffeine Coma
From my reading of the iPhone developer license agreement (disclaimer: I'm not a lawyer), you can't use Tock.aiff unless you can find it as part of sample code or an open source component (section 2.6).
outis
It's not clear that it's technically legal, but I've done it in two apps now and not gotten any complaints. Perhaps Apple is actually willing to be reasonable about something for once. If not, there may be a way to use the copy already on the iPhone, but that seems susceptible to breaking if Apple removes the sound.
Brent Royal-Gordon
It is definitely available in sample code- check out the Metronome example. It includes both "tick" and "tock" soundfiles, though I am not sure that these are the same sound that "the system" actually uses.
Caffeine Coma
Keep in mind that this will ignore the users preference to disable the keyboard sound.
Jason Harwig
+2  A: 

From what I can tell, the click sound isn't available to apps. I haven't seen anything in audio session services that is relevant. AudioServicesPlaySystemSound() looks promising, but there doesn't appear to be any system sound ID for the click sound (a closer look at the headers may turn up something). You could always loop over a call to AudioServicesPlaySystemSound(i) and see if anything plays. The iPhone software restore images probably have the sound, but it's probably not licensed for general use. Jailbreaking an iPhone to get at the tasty click sound doesn't need to be mentioned.

For (creative commons) sounds, check out the Freesound Project.

For the future, perhaps request that Apple expose system sounds other than the alert sound for use with AudioServicesPlaySystemSound().

outis
+1 for the Freesound link, thanks.
Caffeine Coma
+8  A: 

No need to copy the file into your own app - you should be able to get it directly from the UIKit framework:

CFURLRef soundFileURLRef = CFBundleCopyResourceURL(
    CFBundleGetBundleWithIdentifier(CFSTR("com.apple.UIKit")),
    CFSTR ("Tock"),CFSTR ("aiff"),NULL);
aSquared
how do you put this into the code? I tried AudioServicesCreateSystemSoundID(soundFileURLRef) but it throws an error...
Joe
+4  A: 

This is what I made out of it aSquared's comment:

NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.UIKit"] pathForResource:@"Tock" ofType:@"aiff"];
SystemSoundID soundID;
AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:path], &soundID);
AudioServicesPlaySystemSound(soundID);
AudioServicesDisposeSystemSoundID(soundID);
MrMage
+5  A: 

Using 0x450 as the SystemSoundID works for me (and at the correct volume - just playing the built-in Tock.aiff was too loud). No idea how portable that is - this is on an iPod Touch 3rd gen.

Still doesn't respect the preference for tick on/off.

Sam McCall
Thank you, this works perfect. Please post the same answer to http://stackoverflow.com/questions/1513986/how-to-get-iphone-os-3-1-muffled-keyboard-sound/1563097#1563097 so I can give the bounty to you (you deserve it most).
MrMage
+1  A: 

Maybe a bit late ... But in MrMage last post, if you do AudioServicesDisposeSystemSoundID(soundID); straight after AudioServicesPlaySystemSound(soundID); then you won't hear a thing as you're discarding the system sound right after creating it.

You have to let it finish playing first.. Only call AudioServicesDisposeSystemSoundID to cancel the sound before it finishes

jyavenard
Playing a system sound really makes my interface animation slow down. Are there any quicker ways?
Joe
You can play sound in a 2nd thread instead. Use NSOperationQueue to start a task in a 2nd thread. And start the sound there instead
jyavenard
+1  A: 
AudioServicesPlaySystemSound(soundID); 
AudioServicesDisposeSystemSoundID(soundID); 
then you won't hear a thing as you're discarding 
the system sound right after creating it.
You have to let it finish playing first.. 
Only call AudioServicesDisposeSystemSoundID to cancel the sound before it finishes

But HOW would you: play the sound now... let it fully finish... then correctly dispose of it?

(Don't you just love it when someone says: You've done it all wrong... you have to do it this other way... but I'm not going to tell you HOW to do it correctly.)

Patricia
A: 

You do not have to dispose of the sound object right away. Keep a pointer to that sound object in a property, and dispose of it only when you are about to play another sound before re-creating it.

And of course finally dispose of the SystemSoundID object in dealloc.

jyavenard