Exploring iPhone Audio Part 7
April 15th, 2008 Posted in Software Development, iPhone
In the last article of this series we started looking at iPhone audio playback. This time we are going to see how to actually play an audio file that our applicaiton has recorded.
The startPlayback method sets everything up for audio playback and starts the playback process.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | - (void)startPlayback { playState.currentPacket = 0; [self setupAudioFormat:&playState.dataFormat]; OSStatus status; status = AudioFileOpenURL(fileURL, fsRdPerm, kAudioFileAIFFType, &playState.audioFile); if(status == 0) { status = AudioQueueNewOutput( &playState.dataFormat, AudioOutputCallback, &playState, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &playState.queue); if(status == 0) { playState.playing = true; for(int i = 0; i < NUM_BUFFERS && playState.playing; i++) { if(playState.playing) { AudioQueueAllocateBuffer(playState.queue, 16000, &playState.buffers[i]); AudioOutputCallback(&playState, playState.queue, playState.buffers[i]); } } if(playState.playing) { status = AudioQueueStart(playState.queue, NULL); if(status == 0) { labelStatus.text = @"Playing"; } } } } if(status != 0) { labelStatus.text = @"Play failed"; } } |
We set up for playing audio by setting the currentPacket variable of the PlayState struct to 0 and using the setupAudioFormat method to initialize the dataFormat. Next we use the AudioFileOpenURL function to open the audio file we are playing. In the case of this example we are always playing and recording the same file.
If the file is opened successfully we call the AudioQueueNewOutput function to open a new audio output queue.
AudioQueueNewOuput arguments:
- A pointer to the dataFormat variable of our PlayState struct.
- The audio callback function that will be described later in the article.
- A pointer to our PlayState struct.
- The CFRunLoopGetCurrent function causes the callback to be called from the main application thread.
- The run loop mode to use.
- No flags are set for our call.
- A pointer to our audio queue that will be initialized by this call.
Upon successfully opening the output queue we allocate each of the 3 audio output buffers we will use to play audio and we pass each of them to our output callback function. The output callback function will populate the buffers and enqueue them for playback.
Once the three audio buffers are loaded with audio data to be played we call the AudioQueueStart function to start the playback process. As each buffer is played the AudioOutputCallback function will be called with the buffer.
The stopPlayback method stops audio playback and frees up any resources allocated for the playback process.
1 2 3 4 5 6 7 8 9 10 11 12 | - (void)stopPlayback { playState.playing = false; for(int i = 0; i < NUM_BUFFERS; i++) { AudioQueueFreeBuffer(playState.queue, playState.buffers[i]); } AudioQueueDispose(playState.queue, true); AudioFileClose(playState.audioFile); } |
The AudioOutputCallback function is called for each audio buffer that has been played. This function is responsible for loading and queuing additional audio buffers for playback. It will also stop the playback process when the complete audio file has been played.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | void AudioOutputCallback( void* inUserData, AudioQueueRef outAQ, AudioQueueBufferRef outBuffer) { PlayState* playState = (PlayState*)inUserData; if(!playState->playing) { printf("Not playing, returning\n"); return; } printf("Queuing buffer %d for playback\n", playState->currentPacket); AudioStreamPacketDescription* packetDescs; UInt32 bytesRead; UInt32 numPackets = 8000; OSStatus status; status = AudioFileReadPackets( playState->audioFile, false, &bytesRead, packetDescs, playState->currentPacket, &numPackets, outBuffer->mAudioData); if(numPackets) { outBuffer->mAudioDataByteSize = bytesRead; status = AudioQueueEnqueueBuffer( playState->queue, outBuffer, 0, packetDescs); playState->currentPacket += numPackets; } else { if(playState->playing) { AudioQueueStop(playState->queue, false); AudioFileClose(playState->audioFile); playState->playing = false; } } } |
Our PlayState structure is passed into the callback as the inUserData void* parameter. If the playing flag of our struct is set to false we simply return from the callback. The outBuffer parameter is the buffer that has just finished playing.
We use the AudioFileReadPackets function to read the next hunk of data into the just completed outBuffer buffer. We only work with three audio buffers at any given time just like when we are recording.
AudioFileReadPackets arguments:
- The audio file we opened.
- Flag specifying whether or not to cache data.
- The number of bytes read from the file.
- A list of packet descriptions.
- The number of audio packets read from the file.
- The audio buffer’s data variable.
With the audio data read into our buffer we now set the mAudioDataByteSize variable of our buffer to equal the number of bytes read from the file. Next we enqueue the packet to be played.
If we fail to read any packets from the file we stop the play process with the AudioQueueStop function and close the file. Passing false as the second parameter to AudioQueueStop will cause the remaining buffers that are enqueued to finish playing. If we passed true then playing would stop immediately.
That’s it for the baics of audio recording and play back using the iPhone SDK. I am posting the source files for you reference.
NOTE: On beta 3 of the SDK the AudioQueueStart call is failing when trying to start recording. This was working fine in beta 2. I’ll post an update when I’ve found a solution to this issue. If anyone finds a solution please post a comment.
I’m not sure if I will continue this series of articles into more advanced audio topics or if I’ll start a new “Advanced” series of articles.
Source Files:
main.m - AudioRecorderAppDelegate.h - AudioRecorderAppDelegate.m












Related Articles:

Have you had any updates on the Audio not working any longer?
When I compile your sample code I get an undeclared “fsRdPerm” from AudioRecorderAppDelegate.m
Are you missing some code or header file???
regards
angel
Hi!
Thanks for your tutorial.
Like the previous 2 comments here, the audio doesn’t seem to work. The label always says that the device is Idle, and I have to comment out the start recording function because of the “fsRdPerm” undeclared error. Any ideas?
Thanks!
Maxime
Try adding the AudioToolbox.framework to your project.
Use 0×01 instead of fsRdPerm.
Hi Pete, thanks for the quick reply!
I made sure to include the AudioToolbox framework.
Now when we press the record button, I get “Record Failed”.
I was able to downgrade the SDK to Beta 2, but not on the iPhone (when downgrading to Beta 2, it says it has expired and greets meet with the infamous pink screen). Hence, would the error come from using Beta 3 on the iPhone itself? Because you only mention you have noticed it doesn’t work with SDK Beta 3.
Let me know!
Thanks,
Maxime
It worked in beta 2 and stopped working after I updated to beta 3…
what do you mean on the iPhone, you are actually testing on the iPhone? I am still on the developer waiting list so I’m stuck on the simulator.
Yes we have a developer license, otherwise there is no way to test sound recording in the simulator. Unfortunately it doesn’t anymore, and we couldn’t find a mean to restore beta 2 OS on the iPhone itself.
how did you get through? I’ve been on the waiting list for months
I just tried the AudioRecorder app on Beta 4 of the SDK and it is working again!
Hi Pete, this is good news. We tried on our side but after we managed to get both SDK and OS to beta 4 we consistently get on all demos and even your sound recording application the following crash error: EXC_BAD_ACCESS. A few people mention some signing certificate issue, but we believe that it is not the problem for us. Any idea ?
Thanks,
Maxime
You’ll want to download new copies of the sample applications from the apple web site. They have been updated for the latest version of the SDK.
My audio recorder works on the simulator. I also get the bad access error on my device. BUT, my development device is an iPod touch which doesn’t have a mic so who know if it will even run on an iPod.
Interesting that it works in the simulator when it doesn’t have a mic !
I thought you were testing it on the iPhone itself.
What do you mean by working on the simulator? Can you actually record and playback the recording and hear your voice?
I only get the “Record Failed” error in the simulator.
I still haven’t figured the EXC_BAD_ACCESS error on the iPhone, but it finally looks like it is tied to the certificate issue since I get the same errors with the new demos.
What a game, seriously!
I’m able to record and play back audio on the simulator. It just uses the computer’s sound card.
The apple samples work for me both in the simulator and on the iPod Touch. I had to download the new versions though.
Hi!
How do I play compressed audio formats?
I’ve just found some demo code @apple dev site, but this code freezes always the GUI and cannot return after playback…
Any idea?
I’m trying to play some mp3 audio clips from a packed binary file, and I’m having a hell of a time figuring out what approach is appropriate. I was looking at the streaming stuff, but before that I tried just saving my binary chunks out as files, and then playing them using something like your code above. Everything executes, but no sound is played, and no errors occur.
I’m very frustrated. I just don’t understand why playing audio has to be so damned complicated.
Can any one comment on whether or not this works with the sdk v5? I run it but the UI never gets created even though NSLog’s added to the beginning and end of applicationDidFinishLaunching are getting fired.
IIs the full project somewhere? I see the source files (main.m, AudioRecorderAppDelegate.h,AudioRecorderAppDelegate.m), but it still seems like something is missing when I tried it today.
This is a GREAT help, but it be even better with a gzip/zip/dmg/whatever of the entire project.
Thank you for the example. I test these files on iPhone SDK 5, get UIButtonType error. I change it to other UIButton types and has EXC_BAD_ACCESS error on the iPhone Simulator. Does anybody figure it out?
Well, I’m using sdk beta 8. I tried to modify this example to be able just to play ANY long sound on iPhone, as long as I dont have any mic I have no chance to use original example provided by Pete. Anyways, I try to play “caf” files, but it gives me just lots of noise instead of sound. Wav files shows me “Playback failed” statement, MP3s just crash iPhoneSimulator. Any ideas how to organize playback on iPhone will be really appreciated.
error:’AudioOutputCallback’ undeclared
*kills self*
thanks
works fine for me
I just had to rewrite code for creating UIButtons as UIButtonTypeNavigation is not supported anymore
use this instead for buttons
// Create Record buttonbuttonRecord = [UIButton buttonWithType: UIButtonTypeRoundedRect];
[buttonRecord setTitle:@"Record" forState:UIControlStateNormal];
[buttonRecord addTarget:self action:@selector(recordPressed:)
forControlEvents:UIControlEventTouchUpInside];
//buttonRecord.center = CGPointMake(window.center.x, 200);
buttonRecord.frame = CGRectMake(6.0, 120.0, 140, 35);
buttonRecord.backgroundColor = [UIColor clearColor];
// Create Play button
buttonPlay = [UIButton buttonWithType: UIButtonTypeRoundedRect];
[buttonPlay setTitle:@"Play" forState:UIControlStateNormal];
[buttonPlay addTarget:self action:@selector(playPressed:)
forControlEvents:UIControlEventTouchUpInside];
//buttonPlay.center = CGPointMake(window.center.x, 150);
buttonPlay.frame = CGRectMake(161.0, 120.0, 140, 35);
buttonPlay.backgroundColor = [UIColor clearColor];
hi, can you please share a working code for the same for 3.1 sdk?
a full zip folder of the working code will be highly appreciated.
also is tehre anyway we can save the sound in .amr format???
Hi,
Thanks for this top on the button codes.
I’ve managed to get the app to build now with no warnings or errors.
I changed the printfs to NSLog outputs and also had to change fsRdPerm to 0×01 in AudioFileOpenURL.
The app installs fine, but on launching it gets to the ‘init’ (where added an NSLog debug message) and then it just closes with no more debug info. :/ hmmm…
I’m building against SDK 3.1.2
Any idea what I might be doing wrong?
I did add the AudioToolbox framework to a new ‘Window-based application’ project too.
Any pointers would be most appreciated. Thank you.
[...] its code from famous 7 part audio exploring on trailsinthesand.com Many people are unable to run that code on 3.1.2 SDK for that reason here’s a full project [...]
those unable to make this class to work,
i’ev uploaded xcode project on my blog
http://fkn1337.com/iphone-audio-recording-app-xcodepro/
thanks
Hello,
Your link isn’t working. I can’t even get a simle audio file to be played. The AudioQueueStart is giving problems.
Marc
i want to record live stream by copying the bytes or any other way which is feasible. can any one help to solve this.
Thanks
Great one, Thanks for sharing…. What Exactly I’m looking for…
I directly jump to the last page to get the source codes and tried to implement in my program and faced 13 errors related to AudioToolBox Framework errors because I didn’t add them to my Framework… for those who’s facing trouble… make sure you have the “AudioToolbox” framework added to your project framework!…
For the fsRdPerm constant you can specify one of this:
kAudioFileReadPermission: File is read-only.
kAudioFileWritePermission: File is write-only.
kAudioFileReadWritePermission: File has read-write permission.
Available in iPhone OS 2.0 and later.
Declared in AudioFile.h.