views:

1298

answers:

4

I am wondering if anyone has come across a way to generate tones in the iPhone SDK. I am trying to generate DTMF tones, and can't seem to find anything substantial out there. I want to be able to specify how long to play the tone for as well (i.e. to simulate holding the button down as opposed to just pressing it briefly..

I found an open source app called iPhreak. It supposedly generates DTMF tones to fool payphones (I Assure you this is not my intention - my company deals with telephone based Intercom systems). The only problem with that application is that there are files missing from the open source project. Perhaps someone else has gotten this project to work in the past?

If anyone has any idea on where I would look for something like this, I would be very appreciative with my votes :)

+3  A: 

hi,

should be easy enough to generate yourself. given that the hardware can playback a pcm buffer (16bit samples) at 44.1 khz (which it surely can with some library function or the other), you're only left with calculating the waveform:

 const int PLAYBACKFREQ = 44100;
 const float PI2 = 3.14159265359f * 2;

 void generateDTMF(short *buffer, int length, float freq1, float freq2)
 {
      int i;
      short *dest = buffer;
      for(i=0; i<length; i++)
      {
           *(dest++) = (sin(i*(PI2*(PLAYBACKFREQ/freq1))) + sin(i (PI2*(PLAYBACKFREQ/freq2)))) * 16383;
      }
 }

the 16383 is done since I'm using additive synthesis (just adding the sinewaves together). Therefore the max result is -2.0 - 2.0 So after multiplying by 16383 I get more or less the max 16 bit result: -32768 - +32767

EDIT: the 2 frequenties are the frequenties from the wikipedia article the other person who answered linked to. Two unique frequencies make a DTMF sound

Toad
Ok, maybe I am a bit behind the curve on this. Can you give me an example of how you would call that (for example for the #3)? I understand the freq's but don't really get the buffer concept.
Dutchie432
you create a buffer of sufficient length of a 16 bit signed datatype (I'm not sure how this is done in objective c). You then pick the 2 frequencies belonging to the dtmf tone of 3 (697hz and 1477hz). Call my function with a pointer to the buffer, the length you allocated and then the function will fill it with the waveform of the DTMF tone. This Waveform you'll then have to pass to the iPhone library function which can output the contents of a buffer to the audiohardware.
Toad
Note that the buffer is rendered with 44100 samples/seq. So when playing the waveform, the audio hardware should use the same frequenty or else the DTMF frequencies will be off. Also note, that if you want the waveform to last 10 seconds, then the buffer length should be PLAYBACKFREQ*10. I hope this explains it a bit. More info on PCM (the way a waveform is stored in computer memory) can be found here: http://en.wikipedia.org/wiki/Pulse-code_modulation
Toad
This does make sense now, although I still have no idea where to start... off to the Docs. Thanks very much.
Dutchie432
A: 

Reinier: I believe that w is 2pi*f/c so wouldn't sin part of formula be sin(i*(PI2*(freq1/PLAYBACKFREQ))?

+1  A: 

The easy answer is this:

soundArray = [[NSArray alloc] initWithObjects: 
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-0.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-1.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-2.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-3.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-4.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-5.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-6.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-7.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-8.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-9.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-0.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-pound.caf"] autorelease],
    [[[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-star.caf"] autorelease],
              nil];

There you have it. All the sounds of a standard phone keypad, in an array, ready for your enjoyment.

mahboudz
I am hoping to generate the tones programatically... so i can play them for any amount of time. this may be a good fallback if I can't get it working right.
Dutchie432
You can loop these, with varying amounts of work depending on how you chose to play them. Of course, they really are two pure sine waves, and easy to create. However, you still have to create them in some finite length and pass them to a sound player, in which case you'd have to loop those too once the sound player run out of data. Either way, you may have to do the extra work of looping.
mahboudz
Will this get you rejected from the App store?
NateS
Technically, you are not using any prohibited or private APIs. Also, you didn't have to do anything tricky to get outside of your sandbox. I'd use it as is, and if there is a problem, then go ahead and copy those files into your Bundle and distribute with your app. It won't be a huge hit if you got rejected and had to use that as your plan B.
mahboudz
This example has memory leaks. Don't post code with obvious memory leaks.
rpetrich
@rpetrich: How so? Are you talking about retain counts on array objects being 2 after this snippet?
mahboudz
@mahbouds: Ignore retain counts, they are for debugging only. Consider ownership. In your example both the surrounding block own the SoundEffects added to the array, but the surrounding blocks don't have a reference to them, thus a leak. Add autorelease calls to fix this.
rpetrich
I see what you mean. I'm correcting. Thank you. I don't call this a leak, but setting oneself up for a leak :-)
mahboudz
A: 

When I use the following code:

soundArray = [[NSArray alloc] initWithObjects: [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-0.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-1.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-2.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-3.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-4.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-5.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-6.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-7.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-8.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-9.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-0.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-pound.caf"], [[SoundEffect alloc] initWithContentsOfFile: @"/System/Library/Audio/UISounds/dtmf-star.caf"], nil];

I get the error: "Error -1500 loading sound at path: /System/Library/Audio/UISounds/dtmf-0.caf" for each path given above (Of course the name in the error changes to reflect the path). Does the iPhone have DTMF sound files built into it or not? If it does why do I get this error?

I seem to recall the simulator did not have these files. Are you running this on the simulator?
mahboudz