iOS SDK: Playing Touch Tones with the Audio Toolbox Framework

iOS SDK: Playing Touch Tones with the Audio Toolbox Framework

Tutorial Details
  • Technology: iOS SDK
  • Software: Xcode 4
  • Difficulty: Intermediate
  • Completion Time: 45 Minutes
This entry is part 7 of 7 in the series Design & Build a 1980s iOS Phone App

Welcome to the seventh installment in our series on how to both design and build a 1980s version of the iOS “Phone” app. In this tutorial, I’ll be demonstrating how to play the appropriate audio tone for each number on the phone keypad.

Final App Preview

This is a snapshot of what we will be building over the course of this series:

1980s App Final Preview

Before You Begin. . .

This is a multi-part series designed to teach intermediate iOS SDK topics. The content will become increasingly complex as the series progresses. If at any point you find yourself lost in following this series, you might need to take a step back and work your way through our Learn Objective-C series or our Beginning iOS SDK Development series.

In the last tutorial in this series, I demonstrated how to actually initiate an iPhone call after dialing a number on the keypad and also how to format the number display when a user taps a digit. In this tutorial, we’ll add the promised touch tones to each of the 0 – 9 buttons.

iOS Audio Overview

The iOS SDK ships with multiple frameworks that provide various methods of playing sound clips and even generating audio on-the-fly. Apple’s Multimedia Programming Guide describes the purpose of each available audio framework as follows:

  • Use the Media Player framework to play songs, audio books, or audio podcasts from a user’s iPod library. . .
  • Use the AV Foundation framework to play and record audio using a simple Objective-C interface. . .
  • Use the Audio Toolbox framework to play audio with synchronization capabilities, access packets of incoming audio, parse audio streams, convert audio formats, and record audio with access to individual packets. . .
  • Use the Audio Unit framework to connect to and use audio processing plug-ins. . .
  • Use the OpenAL framework to provide positional audio playback in games and other applications. iOS supports OpenAL 1.1. . .


-Multimedia Programming Guide: Using Audio

An entire series of tutorials could be dedicated to each of the frameworks in the above list, but suffice to say that for the purpose of this tutorial we’ll be using the Audio Toolbox framework because of one unique offering: System Sound Services. System Sound Services are a C-level interface intended for playing short user interface sound effects and other small audio clips of 30 seconds in duration or less.

The process for playing an audio clip with System Sound Services spans three steps:

  1. Register an Audio Clip with System Sound Services for playback in the (not-so-distant) future
  2. Receive a System Sound ID (SSID) that uniquely identifies the audio clip registered with the system sound server.
  3. Call the system sound server with the appropriate SSID to start begin playback.

The remainder of this tutorial will implement this process to play pre-recorded DTMF tones for the keypad digits 0-9.

Why are we using pre-recorded tones instead of generating the appropriate frequency on-the-fly? Essentially to save time and to keep this tutorial more accessible by avoiding the mathematics involved in sinusoidal functions and frequency generation. If you’d like to go that route instead, you’ll likely find the following articles handy:

Do you really want to know how to generate sound on-the-fly? If this post receives at least 10 comments requesting a tutorial on how to create audio from scratch by May 1st, 2011, I’ll make an advanced iOS SDK series on that topic. No promises that it will involve DTMF touch tones specifically, but it will definitely be something awesome.

With the theoretical knowledge out of the way, let’s dive into the code and get this feature done!

Step 1: Import the AudioToolbox Framework

We’ll need to begin by importing the Audio Toolbox framework into our application in order to make the System Sound Services available to our code. To do so, select the “PhoneAppSkin” project in the Project Navigator pane in Xcode, then select “PhoneAppSkin” under “TARGETS”, and finally select the “Build Phases” tab. After doing so, your screen should look something like this:

The Build Phases Tab in Xcode

Next, click the “Link Binary With Libraries” drop down, and click the “+” symbol to add a new framework to our project’s linking phase.

Add a new Framework

Finally, find the “AudioToolbox” framework in the popup window, and then click “Add”.

Add a new Framework

Next, open up the PhoneViewController.h file and add the line necessary to actually import the Audio Toolbox framework into your class:

#import <UIKit/UIKit.h>
#import <AudioToolbox/AudioToolbox.h>

The Audio Toolbox functions should now be accessible to your class!

Step 2: Import the Touch Tone WAV Files

Download and open the source code attached to this Mobiletuts+ post and find the folder entitled “Audio”. Drag this entire folder into the “Resources” folder of the Xcode Project Navigator, making sure to check “Copy” option when prompted. You should end up with a screen something like this:

DTMF Tones Imported

Step 3: Create an Array for the SSID References

In PhoneViewController.h, add the following C-style array declaration:

@interface PhoneViewController : UIViewController {
    SystemSoundID toneSSIDs[10];
}

Line 13 declares a C-style array of type SystemSoundID with a maximum capacity of 10.

Storing System Sound IDs in this array will enable us to quickly reference the appropriate sound clip in the numberButtonPressed: method later.

Step 4: Register the Tone System Sounds

Open PhoneViewController.m and jump to the initWithCoder method on line 17. When the PhoneViewController is first initialized and the initWithCoder method is called, we want to register system sounds for each tone to be used on the Keypad with the iOS system sound server. After a sound clip is registered with the sound server, we can instruct the server to playback the sound file later when the keypad is pressed.

To register the 0-9 touch tones, add the following code:

-(id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if(self)
    {
        phoneNumberString = [[NSString alloc] init];
        for(int count = 0; count < 10; count++){
            NSString *toneFilename = [NSString stringWithFormat:@"DTMF_%02d", count];
            NSURL *toneURLRef = [[NSBundle mainBundle] URLForResource:toneFilename
                                                        withExtension:@"wav"];
            SystemSoundID toneSSID = 0;
            AudioServicesCreateSystemSoundID(
                                                 (CFURLRef) toneURLRef,
                                                 &toneSSID
                                             );
            toneSSIDs[count] = toneSSID;
        }
    }
    return self;
}

On line 24, a for loop that will iterate 10 times begins. The purpose of this for loop is to add a new system sound ID to the toneSSIDs array for each of the touch tones associated with keys 0 – 9.

On line 25, an instance of NSString is instantiated that will hold the filename of the relevant DTMF touch tone. An interesting note on this line is the stringWithFormat: method call and the %02d format specifier. This format specifier will convert any digit to a zero-padded digit 2 characters in length. So, for example, 0 becomes ’00′, 1 becomes ’01′, 2 becomes ’02′, etc.

On line 27, the NSBundle class is used to generate a file system path to the filename generated on line 25 with an extension of “wav” appended. NSLog this value if you’d like to see the full path for yourself.

Line 29 creates a variable to hold the SystemSoundID that will be generated by the iOS system sound server next.

As a side note, SystemSoundID is just a typedef for the Mac Type UInt32, which is itself a typedef for unsigned long. Consequently, we could have declared the variable on this line of type unsigned long, but doing so would be poor practice because future iOS releases might modify the SystemSoundID type, causing the code to break. Nonetheless, it’s good to keep this in mind when working with SystemSoundIDs because the compiler will sometimes warn about incompatible unsigned long types.

Lines 31 – 34 are a c-style function call to AudioServicesCreateSystemSoundID, which takes a CFURL (Core Foundation URL) of the audio file path to generate a System Sound ID for and a SystemSoundID reference that will be used to store the SSID in after it is created. Notice how the ampersand character (i.e. ‘&’) appears before the toneSSID variable on line 33? This is an unary prefix operator that converts the toneSSID parameter into the memory address of the variable instead of the value stored in memory for that variable. This is done so that the AudioServicesCreateSystemSoundID function can store the generated SSID number directly in the toneSSID memory address, allowing the function caller (i.e. our method) access to the generated SSID without actually passing the SSID back from the function.

Sound a bit confusing? It probably will unless you’re already experienced with programming in a language like C, C++, or Go. Because Objective-C is a strict superset of the C language, the iOS SDK is sometimes reliant on libraries written in procedural C. If the C syntax doesn’t make sense to you, don’t worry about it too much right now, but take it as a challenge to complement your iOS SDK knowledge with the fundamentals of the C language in the future!

On line 35, the index of the toneSSIDs c-style array designated by the current count value is assigned the value created by the AudioServicesCreateSystemSoundID function. This SSID value will be referenced later to play the appropriate tone when the user taps on the keypad numbers.

Step 5: Play Touch Tones on Tap

The numberButtonPressed: method is called each time the touchUpInside event fires for buttons 0 – 9. Because indices 0-9 of the toneSSIDs array now contains the corresponding tone for each button, the following three lines of code are enough to play the appropriate tone:

-(IBAction)numberButtonPressed:(UIButton *)pressedButton
{
    int toneIndex = [pressedButton.titleLabel.text intValue];
    SystemSoundID toneSSID = toneSSIDs[toneIndex];
    AudioServicesPlaySystemSound(toneSSID);
    self.phoneNumberString = [self.phoneNumberString stringByAppendingString:pressedButton.titleLabel.text];
    [self displayPhoneNumber];
}

Line 96 converts the text value of the pressed UIButton title to an integer from a string.

With the dialpad digit obtained, line 97 retrieves a SystemSoundID from the toneSSIDs array.

On line 98, the AudioServicesPlaySystemSound function is called with the SSID just retrieved to begin the touch tone playback.

That’s it! Go ahead and save and build the project now, and you should find that the keypad numbers have sound effects!

Step 6: Making it Snappy

There’s one small problem with the current solution. Do you notice the delay between touching the button and the generated tone? In part 6 of this series, the keypad buttons were setup to trigger the touchUpInside action. This action fires after the user removes their finger from the button. However, the tone should play as soon as the user taps the button, not after removing their finger. To fix this, open PhoneView.xib, remove the touchUpInside IBAction references for all buttons, and then bind each button’s touchDown action to the numberButtonPressed: method.

If you need help figuring out how to do this step, just refer back to Part 6, Step 1 in this series.

Wrap Up

This tutorial has been a whirlwind tour of the Audio Toolbox framework and System Sound Services. If you have questions, feel free to leave them in the comments below and I’ll do my best to respond. Of course, there aren’t enough hours in the day for me to check all of my past posts for new comments each day, so as the months wear on you might have better luck contacting me via Twitter: @markhammonds.

Series Navigation«Design & Build a 1980s iOS Phone App: Making Phone Calls

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • JM

    Thanks for the great tutorial. By coincidence I just googled [ios audio] and came across this even though it was apparently just published. That’s some fast indexing.

    Here’s your first comment requesting a tutorial on how to create audio from scratch!

  • Riley

    I don’t hear any audio in the iphone simulator.

    • http://mobile.tutsplus.com Mark Hammonds
      Author

      Hey Riley, I’ve had mixed results on the Simulator myself. Sometimes the audio comes through, sometimes not. If you’re having problems, try closing and then re-opening the Simulator. Regardless, this code should *always* work correctly when tested on an actual iOS device.

      • Riley

        Nope I can’t get any audio, weird. I don’t have a dev account so I can’t test it on a real device. I trust it’s ok.

  • http://www.geekorgenius.com Chris Gomez

    Audio works perfectly on the device. Thank You very much for the tutorial.

  • http://howtocreateaniphoneoripadapp.blogspot.com/ Kristoffer Nordquist

    Hello

    Great tutorial and I love the series, I learned alot of new things I did not know about. Thank you so much for making this.

  • http://www.pw2.it Roberto

    Please post the tutorial on how to create audio from scratch :)

  • http://www.pw2.it Roberto

    Also noticed that first sound played after app started is always a litte late (no matter which sound).
    Than the other sounds are played instantly, but the first is in late (fraction of a second)
    Did anyone notice that ?
    Tnx
    Roberto

  • Reuben

    I’ve really enjoyed this series of tutorials.

    Through this tutorial I’ve gone from never touching Photoshop to being able to understand enough of it to mock up simply iOS apps. You had a good balance of step-by-step and figure-it-out-your-own-self.

    The coding tutorial has been great as well with easy explanations and working code. It was also nice that you updated to Xcode 4.

    I know its a bit late for the poll to get a generate sound on-the-fly tutorial, but I’d really like to see how that goes together.

    Thanks again to you and your team for a well put together tutorial.

  • Reuben

    Also, are there any plans to do a series to finish up the app or an article or two with hints on how to wrap up all the loose ends?

  • http://na JP

    thanks for posting this tutorial! It is great learning for me! I created a version of this using 3 viewControllers, however I can’t get the sounds to work with the two ViewControllers connected to the MainViewController, the sound only plays in the MainViewController which is connected to my AppDelegate. I double checked all the code in the other ViewControllers, everything is fine, the buttons show they are selected but no sound plays… any idea why?

  • Jon

    Thanks for the the great tutorial I really appreciated it.

    I would love to see a tutorial on generating sounds directly that sounds like fun.