How do I record audio on iPhone with AVAudioRecorder?

156,184

Solution 1

Actually, there are no examples at all. Here is my working code. Recording is triggered by the user pressing a button on the navBar. The recording uses cd quality (44100 samples), stereo (2 channels) linear pcm. Beware: if you want to use a different format, especially an encoded one, make sure you fully understand how to set the AVAudioRecorder settings (read carefully the audio types documentation), otherwise you will never be able to initialize it correctly. One more thing. In the code, I am not showing how to handle metering data, but you can figure it out easily. Finally, note that the AVAudioRecorder method deleteRecording as of this writing crashes your application. This is why I am removing the recorded file through the File Manager. When recording is done, I save the recorded audio as NSData in the currently edited object using KVC.

#define DOCUMENTS_FOLDER [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]


- (void) startRecording{

UIBarButtonItem *stopButton = [[UIBarButtonItem alloc] initWithTitle:@"Stop" style:UIBarButtonItemStyleBordered  target:self action:@selector(stopRecording)];
self.navigationItem.rightBarButtonItem = stopButton;
[stopButton release];

AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = nil;
[audioSession setCategory :AVAudioSessionCategoryPlayAndRecord error:&err];
if(err){
    NSLog(@"audioSession: %@ %d %@", [err domain], [err code], [[err userInfo] description]);
    return;
}
[audioSession setActive:YES error:&err];
err = nil;
if(err){
    NSLog(@"audioSession: %@ %d %@", [err domain], [err code], [[err userInfo] description]);
    return;
}

recordSetting = [[NSMutableDictionary alloc] init];

[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey]; 
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];

[recordSetting setValue :[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
[recordSetting setValue :[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey];



// Create a new dated file
NSDate *now = [NSDate dateWithTimeIntervalSinceNow:0];
NSString *caldate = [now description];
recorderFilePath = [[NSString stringWithFormat:@"%@/%@.caf", DOCUMENTS_FOLDER, caldate] retain];

NSURL *url = [NSURL fileURLWithPath:recorderFilePath];
err = nil;
recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&err];
if(!recorder){
    NSLog(@"recorder: %@ %d %@", [err domain], [err code], [[err userInfo] description]);
    UIAlertView *alert =
    [[UIAlertView alloc] initWithTitle: @"Warning"
                               message: [err localizedDescription]
                              delegate: nil
                     cancelButtonTitle:@"OK"
                     otherButtonTitles:nil];
    [alert show];
    [alert release];
    return;
}

//prepare to record
[recorder setDelegate:self];
[recorder prepareToRecord];
recorder.meteringEnabled = YES;

BOOL audioHWAvailable = audioSession.inputIsAvailable;
if (! audioHWAvailable) {
    UIAlertView *cantRecordAlert =
    [[UIAlertView alloc] initWithTitle: @"Warning"
                               message: @"Audio input hardware not available"
                              delegate: nil
                     cancelButtonTitle:@"OK"
                     otherButtonTitles:nil];
    [cantRecordAlert show];
    [cantRecordAlert release]; 
    return;
}

// start recording
[recorder recordForDuration:(NSTimeInterval) 10];

}

- (void) stopRecording{

[recorder stop];

NSURL *url = [NSURL fileURLWithPath: recorderFilePath];
NSError *err = nil;
NSData *audioData = [NSData dataWithContentsOfFile:[url path] options: 0 error:&err];
if(!audioData)
    NSLog(@"audio data: %@ %d %@", [err domain], [err code], [[err userInfo] description]);
[editedObject setValue:[NSData dataWithContentsOfURL:url] forKey:editedFieldKey];   

//[recorder deleteRecording];


NSFileManager *fm = [NSFileManager defaultManager];

err = nil;
[fm removeItemAtPath:[url path] error:&err];
if(err)
    NSLog(@"File Manager: %@ %d %@", [err domain], [err code], [[err userInfo] description]);



UIBarButtonItem *startButton = [[UIBarButtonItem alloc] initWithTitle:@"Record" style:UIBarButtonItemStyleBordered  target:self action:@selector(startRecording)];
self.navigationItem.rightBarButtonItem = startButton;
[startButton release];

}

- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *) aRecorder successfully:(BOOL)flag
{

NSLog (@"audioRecorderDidFinishRecording:successfully:");
// your actions here

}

Solution 2

Although this is an answered question (and kind of old) i have decided to post my full working code for others that found it hard to find good working (out of the box) playing and recording example - including encoded, pcm, play via speaker, write to file here it is:

AudioPlayerViewController.h:

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

@interface AudioPlayerViewController : UIViewController {
AVAudioPlayer *audioPlayer;
AVAudioRecorder *audioRecorder;
int recordEncoding;
enum
{
    ENC_AAC = 1,
    ENC_ALAC = 2,
    ENC_IMA4 = 3,
    ENC_ILBC = 4,
    ENC_ULAW = 5,
    ENC_PCM = 6,
} encodingTypes;
}

-(IBAction) startRecording;
-(IBAction) stopRecording;
-(IBAction) playRecording;
-(IBAction) stopPlaying;

@end

AudioPlayerViewController.m:

#import "AudioPlayerViewController.h"

@implementation AudioPlayerViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    recordEncoding = ENC_AAC;
}

-(IBAction) startRecording
{
NSLog(@"startRecording");
[audioRecorder release];
audioRecorder = nil;

// Init audio with record capability
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryRecord error:nil];

NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] initWithCapacity:10];
if(recordEncoding == ENC_PCM)
{
    [recordSettings setObject:[NSNumber numberWithInt: kAudioFormatLinearPCM] forKey: AVFormatIDKey];
    [recordSettings setObject:[NSNumber numberWithFloat:44100.0] forKey: AVSampleRateKey];
    [recordSettings setObject:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
    [recordSettings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
    [recordSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
    [recordSettings setObject:[NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey];   
}
else
{
    NSNumber *formatObject;

    switch (recordEncoding) {
        case (ENC_AAC): 
            formatObject = [NSNumber numberWithInt: kAudioFormatMPEG4AAC];
            break;
        case (ENC_ALAC):
            formatObject = [NSNumber numberWithInt: kAudioFormatAppleLossless];
            break;
        case (ENC_IMA4):
            formatObject = [NSNumber numberWithInt: kAudioFormatAppleIMA4];
            break;
        case (ENC_ILBC):
            formatObject = [NSNumber numberWithInt: kAudioFormatiLBC];
            break;
        case (ENC_ULAW):
            formatObject = [NSNumber numberWithInt: kAudioFormatULaw];
            break;
        default:
            formatObject = [NSNumber numberWithInt: kAudioFormatAppleIMA4];
    }

    [recordSettings setObject:formatObject forKey: AVFormatIDKey];
    [recordSettings setObject:[NSNumber numberWithFloat:44100.0] forKey: AVSampleRateKey];
    [recordSettings setObject:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
    [recordSettings setObject:[NSNumber numberWithInt:12800] forKey:AVEncoderBitRateKey];
    [recordSettings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
    [recordSettings setObject:[NSNumber numberWithInt: AVAudioQualityHigh] forKey: AVEncoderAudioQualityKey];
}

NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/recordTest.caf", [[NSBundle mainBundle] resourcePath]]];


NSError *error = nil;
audioRecorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:&error];

if ([audioRecorder prepareToRecord] == YES){
    [audioRecorder record];
}else {
    int errorCode = CFSwapInt32HostToBig ([error code]); 
    NSLog(@"Error: %@ [%4.4s])" , [error localizedDescription], (char*)&errorCode); 

}
NSLog(@"recording");
}

-(IBAction) stopRecording
{
NSLog(@"stopRecording");
[audioRecorder stop];
NSLog(@"stopped");
}

-(IBAction) playRecording
{
NSLog(@"playRecording");
// Init audio with playback capability
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];

NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/recordTest.caf", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 0;
[audioPlayer play];
NSLog(@"playing");
}

-(IBAction) stopPlaying
{
NSLog(@"stopPlaying");
[audioPlayer stop];
NSLog(@"stopped");
}

- (void)dealloc
{
[audioPlayer release];
[audioRecorder release];
[super dealloc];
}

@end

Hope this will help some of you guys.

Solution 3

I have uploaded a sample project. You can take a look.

VoiceRecorder

Solution 4

Its really helpful. The only problem i had was the size of sound file created after recording. I needed to reduce the file size so i did some changes in settings.

NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue :[NSNumber numberWithInt:kAudioFormatAppleIMA4] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:16000.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];

File size reduced from 360kb to just 25kb (2 seconds recording).

Solution 5

I've been trying to get this code to work for the last 2 hours and though it showed no error on the simulator, there was one on the device.

Turns out, at least in my case that the error came from directory used (bundle) :

NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/recordTest.caf", [[NSBundle mainBundle] resourcePath]]];

It was not writable or something like this... There was no error except the fact that prepareToRecord failed...

I therefore replaced it by :

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *recDir = [paths objectAtIndex:0];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/recordTest.caf", recDir]]

It now Works like a Charm.

Hope this helps others.

Share:
156,184

Related videos on Youtube

naitsirch
Author by

naitsirch

Updated on March 24, 2022

Comments

  • naitsirch
    naitsirch about 2 years

    Now that iPhone 3.0 SDK is public, I think I can ask this question for those of you that have already been playing with the 3.0 SDK. I want to record audio in my application, but I want to use AVAudioRecorder and not the older way of recording like the example SpeakHere shows. There are not any examples of how to best do this in the iPhone Dev Center and only reference to the classes. I am a newbie at iPhone development, so I am looking for a simple sample to get me started.

  • naitsirch
    naitsirch almost 15 years
    I think I am close to getting your code to work, but I am struggling with the delegate stuff. I am pretty new to Objective C and still have not gotten my head around the proper way to do the delegate for something like this. I have my delegate trying to implement NSObject <AVAudioRecorder>, but I don't think I am doing it right. Would it be too much trouble to post the delegate code also? Thanks.
  • naitsirch
    naitsirch almost 15 years
    I just finally got it working by adding this to my delegate class @protocol AVAudioRecorder @optional - (void)audioRecorderBeginInterruption:(AVAudioRecorder *)recorder; - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag; - (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error; - (void)audioRecorderEndInterruption:(AVAudioRecorder *)recorder; It seems to work, but I don't know if this is best practice or not. Now I need to persist it to a local data store and play it back among other things.
  • dizy
    dizy almost 15 years
    Thats not right Jim. In your recorder controller header you would do something like... #import <AVFoundation/AVFoundation.h> @interface RecorderViewController : UIViewController <AVAudioRecorderDelegate> {
  • Vaerenberg
    Vaerenberg over 14 years
    Thanks. This is probably the only good piece of code on the internet for recording. However using this code gives me a mono playback when I play the recorded file. I have specified the number of channels as 2 in the record settings but don't get a stereo playback. Any ideas?
  • Raptor
    Raptor over 13 years
    some questions: editedObject & editedFieldKey in stopRecording undefined. Can you give me some lights?
  • Admin
    Admin over 13 years
    Hello, When I build and run the second code provided by ShayBC on iphone simulator, I don't get any results but in console it shows that it's working. Does the Iphone simulator use my laptop's speaker and microphone or is it mute and I have to build the app on device?
  • Shaybc
    Shaybc about 13 years
    @Bataly the iphone simulator will play your sound files (mp3 / caf...) and can record through your laptop mic try looking into leopard System Preferences if there are problems, but the best way to test your app is to run it on a real iDevice, and its a good idea for all code you write since there are allot of apps being rejected from the app-store due to crash since they never been tested on a real iDevice, there are more features that are not supported by the simulator (bluetooth, camera, proper multitouch, accelerometer, IAP, GPS, it will have a better performance then the most iDevice...)
  • blakkheartt12
    blakkheartt12 about 13 years
    [recordSettings setObject:formatObject forKey: AVFormatIDKey]; This line won't let me use AVFormatIDKey as a key. What gives? If I set it to another value it works...
  • Swapnil Luktuke
    Swapnil Luktuke almost 13 years
    can you post the rest of your code as well? cause i think i am missing somethong. I tried changing many settings but am unable to reduce the file size..
  • Ahsan
    Ahsan almost 13 years
    @Shaybc : not working for me..am using the same code, only modified to include the 1st 10 seconds incase of recording.... any suggestions ?
  • saurabh
    saurabh almost 13 years
    u dnt have permission to write in resource folder
  • Avinash
    Avinash over 12 years
    I have uploaded a sample project. You can take a look. github.com/AvinashP/VoiceRecorder
  • Philipp Schlösser
    Philipp Schlösser over 12 years
    Just in case you don't find the download: Go to 'Downloads' in the upper right corner and then to 'DOWNLOAD PACKAGES'
  • joe kirk
    joe kirk over 12 years
    I found that recordEncoding = ENC_AAC; in the viewDidLoad method is the cause of your issue. Try other format, other formats work for me, but not kAudioFormatMPEG4AAC. Not sure why though.
  • joe kirk
    joe kirk over 12 years
    Lastest findings, for kAudioFormatMPEG4AAC, we cannot set AVEncoderBitRateKey to 12800. Comment off the line and it will work. Will try to find out what should be the correct bit rate for AAC.
  • user523234
    user523234 over 12 years
    @Phlibbo: Can't find anything there now?
  • rajt
    rajt over 12 years
    @user523234: here is the download url - github.com/AvinashP/VoiceRecorder/downloads#download_128004
  • AJPatel
    AJPatel about 12 years
    @unforgiven hey have idea how to save playing sound in AVAudioPlayer ? not Surrounding || Mic-in sound.
  • William Entriken
    William Entriken about 12 years
    Ignore this comment, I am bookmarking your answer. Works great in iOS 5 with minor changes
  • Abhishek
    Abhishek about 12 years
    with the help of this code how can i record .amr file format tell me please
  • Olof
    Olof over 11 years
    Hmm. Isn´t there a memory leak at "NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] initWithCapacity:10];"? recordSettings doesn´t seem to be released anywhere?
  • Dewayne
    Dewayne over 11 years
    The sample code does not compile as noted in the Github issues 6 months ago.
  • Avinash
    Avinash over 11 years
    I have fixed the code and checked in into github. It should work now.
  • Tim Kozak
    Tim Kozak about 11 years
    OPTIMAL SOUND QUALITY SETTINGS I FOUND
  • Prince Kumar Sharma
    Prince Kumar Sharma about 11 years
    @Massimo Cafaro , can we record current playing file in iphone.
  • marxy
    marxy almost 11 years
    I don't think you can write to the bundle so I write to Documents like this: NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *basePath = paths[0]; NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/recordTest.caf", basePath]];
  • Olie
    Olie almost 11 years
    I'm curious why you use now = [NSDate dateWithTimeIntervalSinceNow:0], rather than just now = [NSDate date]. Are they not functionally the same?
  • Massimo Cafaro
    Massimo Cafaro almost 11 years
    @Olie, they are functionally the same. I adapted my original code in which I was not using the current date, so I had already in my code a statement like recordingDate = [NSDate dateWithTimeIntervalSinceNow:lastEvent]. I just changed it to the one you saw in the code snippet, but, being lazy, I did not rewrite the statement ;-)
  • Vaibhav Limbani
    Vaibhav Limbani about 10 years
    @Raptor editedObject is NSMutableDictionary & editedFieldKey is Key for that Dictionary Consider NSMutableDictionary=[editedObject setValue:[NSData dataWithContentsOfURL:url] forKey:@"editedFieldKey"]; Hope this will help you.
  • Husam
    Husam over 8 years
    Thanks bro, this is ur code in Swift : let recordSettings:[String:AnyObject] = [ AVFormatIDKey:Int(kAudioFormatLinearPCM), AVLinearPCMIsFloatKey:false, AVLinearPCMIsBigEndianKey:0, AVLinearPCMIsNonInterleaved:false, AVSampleRateKey:44100.0, AVNumberOfChannelsKey:2, AVEncoderBitRateKey:12800, AVLinearPCMBitDepthKey:16, AVEncoderAudioQualityKey:AVAudioQuality.Max.rawValue]
  • Yogendra Girase
    Yogendra Girase almost 7 years
    @MassimoCafaro how i audio record when app is background ?. i have tested your code it is not working on background mode.
  • CodeBrew
    CodeBrew about 6 years
    This example code has a major mistake of writing audio file to the bundle resource URL. Check the correction provided by @marxy
  • Ashley Mills
    Ashley Mills over 5 years
    Don't use -[NSUserDefaults synchronize]. From Apple's documentation "this method is unnecessary and shouldn't be used."
  • Debug Arnaut
    Debug Arnaut over 3 years
    Thanks man! This answer needs to be higher up since recent iOS versions require that privacy info in the info.plist file. Also, FYI to any other iOS newbies who stumble across this: You can open "Info.plist" and then just right-click the background near the bottom of the file, hit "Add row", and paste in "NSMicrophoneUsageDescription"