/*
Erica Sadun, http://ericasadun.com
iPhone Developer's Cookbook, 3.0 Edition
BSD License, Use at your own risk
*/
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
#import "ModalAlert.h"
#define COOKBOOK_PURPLE_COLOR[UIColor colorWithRed:0.20392f green:0.19607f blue:0.61176f alpha:1.0f]
#define BARBUTTON(TITLE, SELECTOR)[[[UIBarButtonItem alloc] initWithTitle:TITLE style:UIBarButtonItemStylePlain target:self action:SELECTOR] autorelease]
#define SYSBARBUTTON(ITEM, TARGET, SELECTOR) [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:ITEM target:TARGET action:SELECTOR] autorelease]
#define DOCUMENTS_FOLDER [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]
#define FILEPATH [DOCUMENTS_FOLDER stringByAppendingPathComponent:[self dateString]]
#define XMAX20.0f
@interface TestBedViewController : UIViewController <AVAudioRecorderDelegate, AVAudioPlayerDelegate>
{
AVAudioRecorder *recorder;
AVAudioSession *session;
IBOutletUIProgressView *meter1;
IBOutletUIProgressView *meter2;
NSTimer *timer;
}
@property (retain) AVAudioSession *session;
@property (retain) AVAudioRecorder *recorder;
@end
@implementation TestBedViewController
@synthesize session;
@synthesize recorder;
/*
-(IBAction)start:(id)sender{
recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatAppleLossless] 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];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
soundsDirectoryPath = [documentsDirectory stringByAppendingPathComponent:@"Sounds"];
[[NSFileManager defaultManager] createDirectoryAtPath:soundsDirectoryPath withIntermediateDirectories:YES attributes:nil error:NULL];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/Test.m4a", [soundsDirectoryPath retain]]];
NSLog(@"Path : %@", [url absoluteString]);
NSError *err = nil;
if([recorder isRecording])
{
[recorder stop];
recorder = nil;
}
recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&err];
[recorder setDelegate:self];
if(err)
NSLog(@"ERROR : %@", err);
BOOL st = [recorder prepareToRecord];
if(!st){
NSLog(@"Failed");
}
recorder.meteringEnabled = YES;
BOOL status = [recorder record];
if(!status)
NSLog(@"Failed");
}
-(IBAction)stop:(id)sender{
if([recorder isRecording]){
NSLog(@"Recording...");
}else{
NSLog(@"Not recording...");
}
[recorder stop];
}
-(void) audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag{
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/Test.m4a", [soundsDirectoryPath retain]]];
NSError *err = nil;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&err];
[player setDelegate:self];
[player setMeteringEnabled:YES];
[player play];
}
*/
- (NSString *) dateString
{
// return a formatted string for a file name
NSDateFormatter *formatter = [[[NSDateFormatteralloc] init] autorelease];
formatter.dateFormat = @"ddMMMYY_hhmmssa";
return [[formatter stringFromDate:[NSDatedate]] stringByAppendingString:@".m4a"];
}
- (NSString *) formatTime: (int) num
{
// return a formatted ellapsed time string
int secs = num % 60;
int min = num / 60;
if (num < 60) return [NSString stringWithFormat:@"0:%02d", num];
return[NSString stringWithFormat:@"%d:%02d", min, secs];
}
- (void) updateMeters
{
// Show the current power levels
[self.recorderupdateMeters];
float avg = [self.recorderaveragePowerForChannel:0];
float peak = [self.recorderpeakPowerForChannel:0];
meter1.progress = (XMAX + avg) / XMAX;
meter2.progress = (XMAX + peak) / XMAX;
// Update the current recording time
self.title = [NSStringstringWithFormat:@"%@", [selfformatTime:self.recorder.currentTime]];
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
// Prepare UI for recording
self.title = nil;
meter1.hidden = NO;
meter2.hidden = NO;
{
// Return to play and record session
NSError *error;
if (![[AVAudioSessionsharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecorderror:&error])
{
NSLog(@"Error: %@", [error localizedDescription]);
return;
}
self.navigationItem.rightBarButtonItem = BARBUTTON(@"Record", @selector(record));
}
// Delete the current recording
[ModalAlertsay:@"Deleting recording"];
//[self.recorder deleteRecording]; <-- too flaky to use
NSError *error;
if (![[NSFileManagerdefaultManager] removeItemAtPath:[self.recorder.urlpath] error:&error])
NSLog(@"Error: %@", [error localizedDescription]);
// Release the player
[player release];
}
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
// Stop monitoring levels, time
[timerinvalidate];
meter1.progress = 0.0f;
meter1.hidden = YES;
meter2.progress = 0.0f;
meter2.hidden = YES;
self.navigationItem.leftBarButtonItem = nil;
self.navigationItem.rightBarButtonItem = nil;
[ModalAlertsay:@"File saved to %@", [[self.recorder.urlpath] lastPathComponent]];
self.title = @"Playing back recording...";
// Start playback
AVAudioPlayer *player = [[AVAudioPlayeralloc] initWithContentsOfURL:self.recorder.urlerror:nil];
player.delegate = self;
// Change audio session for playback
NSError *error;
if (![[AVAudioSessionsharedInstance] setCategory:AVAudioSessionCategoryPlaybackerror:&error])
{
NSLog(@"Error: %@", [error localizedDescription]);
return;
}
[player play];
}
- (void) stopRecording
{
// This causes the didFinishRecording delegate method to fire
[self.recorderstop];
}
- (void) continueRecording
{
// resume from a paused recording
[self.recorderrecord];
self.navigationItem.rightBarButtonItem = BARBUTTON(@"Done", @selector(stopRecording));
self.navigationItem.leftBarButtonItem = SYSBARBUTTON(UIBarButtonSystemItemPause, self, @selector(pauseRecording));
}
- (void) pauseRecording
{
// pause an ongoing recording
[self.recorderpause];
self.navigationItem.leftBarButtonItem = BARBUTTON(@"Continue", @selector(continueRecording));
self.navigationItem.rightBarButtonItem = nil;
}
- (BOOL) record
{
NSMutableDictionary *recordSetting = [[NSMutableDictionaryalloc] init];
[recordSetting setValue:[NSNumbernumberWithInt:kAudioFormatAppleLossless] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumbernumberWithFloat:22100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];
[recordSetting setValue:[NSNumbernumberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[recordSetting setValue:[NSNumbernumberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
[recordSetting setValue:[NSNumbernumberWithBool:NO] forKey:AVLinearPCMIsFloatKey];
// File URL
NSURL *url = [NSURLfileURLWithPath:FILEPATH];
NSLog(@"Path : %@", [url absoluteString]);
NSError *err = nil;
if([self.recorderisRecording])
{
[self.recorder stop];
self.recorder = nil;
}
self.recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSetting error:&err];
[self.recordersetDelegate:self];
if(err)
NSLog(@"ERROR : %@", err);
BOOL st = [self.recorder prepareToRecord];
if(!st){
NSLog(@"222Failed");
}
self.recorder.meteringEnabled = YES;
BOOL status = [self.recorder record];
if(!status)
NSLog(@"1111Failed");
// Set a timer to monitor levels, current time
timer = [NSTimerscheduledTimerWithTimeInterval:0.1ftarget:selfselector:@selector(updateMeters) userInfo:nilrepeats:YES];
// Update the navigation bar
self.navigationItem.rightBarButtonItem = BARBUTTON(@"Done", @selector(stopRecording));
self.navigationItem.leftBarButtonItem = SYSBARBUTTON(UIBarButtonSystemItemPause, self, @selector(pauseRecording));
returnYES;
}
- (BOOL) startAudioSession
{
// Prepare the audio session
NSError *error;
self.session = [AVAudioSession sharedInstance];
if (![self.session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error])
{
NSLog(@"Error: %@", [error localizedDescription]);
returnNO;
}
if (![self.session setActive:YES error:&error])
{
NSLog(@"Error: %@", [error localizedDescription]);
returnNO;
}
return self.session.inputIsAvailable;
}
- (void) viewDidLoad
{
self.navigationController.navigationBar.tintColor = COOKBOOK_PURPLE_COLOR;
self.title = @"Audio Recorder";
if ([selfstartAudioSession])
self.navigationItem.rightBarButtonItem = BARBUTTON(@"Record", @selector(record));
else
self.title = @"No Audio Input Available";
}
@end
@interface TestBedAppDelegate : NSObject <UIApplicationDelegate>
@end
@implementation TestBedAppDelegate
- (void)applicationDidFinishLaunching:(UIApplication *)application {
UIWindow *window = [[UIWindowalloc] initWithFrame:[[UIScreenmainScreen] bounds]];
UINavigationController *nav = [[UINavigationControlleralloc] initWithRootViewController:[[TestBedViewControlleralloc] init]];
[window addSubview:nav.view];
[window makeKeyAndVisible];
}
@end
int main(int argc, char *argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, @"TestBedAppDelegate");
[pool release];
return retVal;
}