I have been looking all over the place for an answer to this but cant find exactly what I need. Basically in my app I am recording voice to an audio file (like the iOS Voice Memo app) and then would like to save it to the local document dir. From some reason the URL that I am being given with the recorded file expires next time I launch the app. Besides, even if it did not, if I record twice, the second file URL gets the same URL as the first one, so I am losing the first file.
Recording this way:
[audioRecorder record];
Where: AVAudioRecorder *audioRecorder;
Playing is ok:
[audioPlayer play];
Where: AVAudioPlayer *audioPlayer;
What is the best way to record voice memo and save it to the local disk on the iPhone?
Thanks.
Update:
I tried to use this code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
BOOL status = [data writeToFile:filePath atomically:YES];
With the data being the data of my AVAudioPlayer NSData property, but BOOL gets 0, and no idea why.
Return current date and time which we are use as sound file name.
Objective-c
- (NSString *) dateString
{
// return a formatted string for a file name
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = #"ddMMMYY_hhmmssa";
return [[formatter stringFromDate:[NSDate date]] stringByAppendingString:#".aif"];
}
Swift 4
func dateString() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "ddMMMYY_hhmmssa"
let fileName = formatter.string(from: Date())
return "\(fileName).aif"
}
Setup Audio Session
Objective-c
- (BOOL) startAudioSession
{
// Prepare the audio session
NSError *error;
AVAudioSession *session = [AVAudioSession sharedInstance];
if (![session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error])
{
NSLog(#"Error setting session category: %#", error.localizedFailureReason);
return NO;
}
if (![session setActive:YES error:&error])
{
NSLog(#"Error activating audio session: %#", error.localizedFailureReason);
return NO;
}
return session.inputIsAvailable;
}
Swift 4
func startAudioSession() -> Bool {
let session = AVAudioSession()
do {
try session.setCategory(AVAudioSessionCategoryPlayAndRecord)
} catch(let error) {
print("--> \(error.localizedDescription)")
}
do {
try session.setActive(true)
} catch (let error) {
print("--> \(error.localizedDescription)")
}
return session.isInputAvailable;
}
Record Sound..
Objective-c
- (BOOL) record
{
NSError *error;
// Recording settings
NSMutableDictionary *settings = [NSMutableDictionary dictionary];
[settings setValue: [NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
[settings setValue: [NSNumber numberWithFloat:8000.0] forKey:AVSampleRateKey];
[settings setValue: [NSNumber numberWithInt: 1] forKey:AVNumberOfChannelsKey];
[settings setValue: [NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[settings setValue: [NSNumber numberWithBool:NO] forKey:AVLinearPCMIsBigEndianKey];
[settings setValue: [NSNumber numberWithBool:NO] forKey:AVLinearPCMIsFloatKey];
[settings setValue: [NSNumber numberWithInt: AVAudioQualityMax] forKey:AVEncoderAudioQualityKey];
NSArray *searchPaths =NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath_ = [searchPaths objectAtIndex: 0];
NSString *pathToSave = [documentPath_ stringByAppendingPathComponent:[self dateString]];
// File URL
NSURL *url = [NSURL fileURLWithPath:pathToSave];//FILEPATH];
// Create recorder
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (!recorder)
{
NSLog(#"Error establishing recorder: %#", error.localizedFailureReason);
return NO;
}
// Initialize degate, metering, etc.
recorder.delegate = self;
recorder.meteringEnabled = YES;
//self.title = #"0:00";
if (![recorder prepareToRecord])
{
NSLog(#"Error: Prepare to record failed");
//[self say:#"Error while preparing recording"];
return NO;
}
if (![recorder record])
{
NSLog(#"Error: Record failed");
// [self say:#"Error while attempting to record audio"];
return NO;
}
// Set a timer to monitor levels, current time
timer = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(updateMeters) userInfo:nil repeats:YES];
return YES;
}
Swift 4
func record() -> Bool {
var settings: [String: Any] = [String: String]()
settings[AVFormatIDKey] = kAudioFormatLinearPCM
settings[AVSampleRateKey] = 8000.0
settings[AVNumberOfChannelsKey] = 1
settings[AVLinearPCMBitDepthKey] = 16
settings[AVLinearPCMIsBigEndianKey] = false
settings[AVLinearPCMIsFloatKey] = false
settings[AVAudioQualityMax] = AVEncoderAudioQualityKey
let searchPaths: [String] = NSSearchPathForDirectoriesInDomains(.documentDirectory, .allDomainsMask, true)
let documentPath_ = searchPaths.first
let pathToSave = "\(documentPath_)/\(dateString)"
let url: URL = URL(pathToSave)
recorder = try? AVAudioRecorder(url: url, settings: settings)
// Initialize degate, metering, etc.
recorder.delegate = self;
recorder.meteringEnabled = true;
recorder?.prepareToRecord()
if let recordIs = recorder {
return recordIs.record()
}
return false
}
Play sound...Retrieve From document directiory
Objective-c
-(void)play
{
NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentPath_ = [searchPaths objectAtIndex: 0];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[self recordingFolder]])
{
arrayListOfRecordSound=[[NSMutableArray alloc]initWithArray:[fileManager contentsOfDirectoryAtPath:documentPath_ error:nil]];
NSLog(#"====%#",arrayListOfRecordSound);
}
NSString *selectedSound = [documentPath_ stringByAppendingPathComponent:[arrayListOfRecordSound objectAtIndex:0]];
NSURL *url =[NSURL fileURLWithPath:selectedSound];
//Start playback
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if (!player)
{
NSLog(#"Error establishing player for %#: %#", recorder.url, error.localizedFailureReason);
return;
}
player.delegate = self;
// Change audio session for playback
if (![[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error])
{
NSLog(#"Error updating audio session: %#", error.localizedFailureReason);
return;
}
self.title = #"Playing back recording...";
[player prepareToPlay];
[player play];
}
Swift 4
func play() {
let searchPaths: [String] = NSSearchPathForDirectoriesInDomains(.documentDirectory, .allDomainsMask, true)
let documentPath_ = searchPaths.first
let fileManager = FileManager.default
let arrayListOfRecordSound: [String]
if fileManager.fileExists(atPath: recordingFolder()) {
let arrayListOfRecordSound = try? fileManager.contentsOfDirectory(atPath: documentPath_)
}
let selectedSound = "\(documentPath_)/\(arrayListOfRecordSound.first)"
let url = URL.init(fileURLWithPath: selectedSound)
let player = try? AVAudioPlayer(contentsOf: url)
player?.delegate = self;
try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
player?.prepareToPlay()
player?.play()
}
stopRecording
Objective-c
- (void) stopRecording
{
// This causes the didFinishRecording delegate method to fire
[recorder stop];
}
Swift 4
func stopRecording() {
recorder?.stop()
}
continueRecording
Objective-c
- (void) continueRecording
{
// resume from a paused recording
[recorder record];
}
Swift 4
func continueRecording() {
recorder?.record()
}
pauseRecording
Objective-c
- (void) pauseRecording
{ // pause an ongoing recording
[recorder pause];
}
Swift 4
func pauseRecording() {
recorder?.pause()
}
Related
I have to implement functionality to repeatedly pause and resume video capture in a single session, but have each new segment (the captured segments after each pause) added to the same video file, with AVFoundation. Currently, every time I press "stop" then "record" again, it just saves a new video file to my iPhone's Document directory and starts capturing to a new file. I need to be able to press the "record/stop" button over, only capture video & audio when record is active... then when the "done" button is pressed, have a single AV file with all the segments together. And all this needs to happen in the same capture session / preview session.
I am not using AVAssetWriterInput.
The only way I can think of to try this is when the "done" button is pressed, taking each individual output file and combining them together into a single file.
This code is working for iOS 5 but not for iOS 6. Actually for iOS 6, the first time when I pause recording (stop recording) AVCaptureFileOutputRecordingDelegate method (captureOutput: didFinishRecordingToOutputFileAtURL: fromConnections: error:) is called but after that when I start the recording the delegate method (captureOutput: didFinishRecordingToOutputFileAtURL: fromConnections: error:) is called again but it is not called at the time of stop recording.
I need a solution for that issue. Please help me.
//View LifeCycle
- (void)viewDidLoad
{
[super viewDidLoad];
self.finalRecordedVideoName = [self stringWithNewUUID];
arrVideoName = [[NSMutableArray alloc]initWithCapacity:0];
arrOutputUrl = [[NSMutableArray alloc] initWithCapacity:0];
CaptureSession = [[AVCaptureSession alloc] init];
captureDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
if ([captureDevices count] > 0)
{
NSError *error;
VideoInputDevice = [[AVCaptureDeviceInput alloc] initWithDevice:[self backFacingCamera] error:&error];
if (!error)
{
if ([CaptureSession canAddInput:VideoInputDevice])
[CaptureSession addInput:VideoInputDevice];
else
NSLog(#"Couldn't add video input");
}
else
{
NSLog(#"Couldn't create video input");
}
}
else
{
NSLog(#"Couldn't create video capture device");
}
//ADD VIDEO PREVIEW LAYER
NSLog(#"Adding video preview layer");
AVCaptureVideoPreviewLayer *layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:CaptureSession];
[self setPreviewLayer:layer];
UIDeviceOrientation currentOrientation = [UIDevice currentDevice].orientation;
NSLog(#"%d",currentOrientation);
if (currentOrientation == UIDeviceOrientationPortrait)
{
PreviewLayer.orientation = AVCaptureVideoOrientationPortrait;
}
else if (currentOrientation == UIDeviceOrientationPortraitUpsideDown)
{
PreviewLayer.orientation = AVCaptureVideoOrientationPortraitUpsideDown;
}
else if (currentOrientation == UIDeviceOrientationLandscapeRight)
{
PreviewLayer.orientation = AVCaptureVideoOrientationLandscapeRight;
}
else if (currentOrientation == UIDeviceOrientationLandscapeLeft)
{
PreviewLayer.orientation = AVCaptureVideoOrientationLandscapeLeft;
}
[[self PreviewLayer] setVideoGravity:AVLayerVideoGravityResizeAspectFill];
//ADD MOVIE FILE OUTPUT
NSLog(#"Adding movie file output");
MovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
VideoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
[VideoDataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
NSString* key = (NSString*)kCVPixelBufferBytesPerRowAlignmentKey;
NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key];
[VideoDataOutput setVideoSettings:videoSettings];
Float64 TotalSeconds = 60; //Total seconds
int32_t preferredTimeScale = 30; //Frames per second
CMTime maxDuration = CMTimeMakeWithSeconds(TotalSeconds, preferredTimeScale);//<<SET MAX DURATION
MovieFileOutput.maxRecordedDuration = maxDuration;
MovieFileOutput.minFreeDiskSpaceLimit = 1024 * 1024; //<<SET MIN FREE SPACE IN BYTES FOR RECORDING TO CONTINUE ON A VOLUME
//SET THE CONNECTION PROPERTIES (output properties)
[self CameraSetOutputProperties]; //(We call a method as it also has to be done after changing camera)
AVCaptureConnection *videoConnection = nil;
for ( AVCaptureConnection *connection in [MovieFileOutput connections] )
{
NSLog(#"%#", connection);
for ( AVCaptureInputPort *port in [connection inputPorts] )
{
NSLog(#"%#", port);
if ( [[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
}
}
}
if([videoConnection isVideoOrientationSupported]) // **Here it is, its always false**
{
[videoConnection setVideoOrientation:[[UIDevice currentDevice] orientation]];
} NSLog(#"Setting image quality");
[CaptureSession setSessionPreset:AVCaptureSessionPresetLow];
//----- DISPLAY THE PREVIEW LAYER -----
CGRect layerRect = CGRectMake(5, 5, 299, ([[UIScreen mainScreen] bounds].size.height == 568)?438:348);
[self.PreviewLayer setBounds:layerRect];
[self.PreviewLayer setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))];
if ([CaptureSession canAddOutput:MovieFileOutput])
[CaptureSession addOutput:MovieFileOutput];
[CaptureSession addOutput:VideoDataOutput];
//We use this instead so it goes on a layer behind our UI controls (avoids us having to manually bring each control to the front):
CameraView = [[UIView alloc] init];
[videoPreviewLayer addSubview:CameraView];
[videoPreviewLayer sendSubviewToBack:CameraView];
[[CameraView layer] addSublayer:PreviewLayer];
//----- START THE CAPTURE SESSION RUNNING -----
[CaptureSession startRunning];
}
#pragma mark - IBACtion Methods
-(IBAction)btnStartAndStopPressed:(id)sender
{
UIButton *StartAndStopButton = (UIButton*)sender;
if ([StartAndStopButton isSelected] == NO)
{
[StartAndStopButton setSelected:YES];
[btnPauseAndResume setEnabled:YES];
[btnBack setEnabled:NO];
[btnSwitchCameraInput setHidden:YES];
NSDate *date = [NSDate date];
NSLog(#" date %#",date);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *recordedFileName = nil;
recordedFileName = [NSString stringWithFormat:#"output%#.mov",date];
NSString *documentsDirectory = [paths objectAtIndex:0];
self.outputPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",recordedFileName]];
NSLog(#"%#",self.outputPath);
[arrVideoName addObject:recordedFileName];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:self.outputPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputPath])
{
NSError *error;
if ([[NSFileManager defaultManager] removeItemAtPath:self.outputPath error:&error] == NO)
{
//Error - handle if requried
}
}
//Start recording
[MovieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
recordingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(VideoRecording) userInfo:nil repeats:YES];
}
else
{
[StartAndStopButton setSelected:NO];
[btnPauseAndResume setEnabled:NO];
[btnBack setEnabled:YES];
[btnSwitchCameraInput setHidden:NO];
NSLog(#"STOP RECORDING");
WeAreRecording = NO;
[MovieFileOutput stopRecording];
[((ActOutAppDelegate *)ActOut_AppDelegate) showLoadingViewOnView:self.view withLabel:#"Please wait...."];
if ([recordingTimer isValid])
{
[recordingTimer invalidate];
recordingTimer = nil;
recordingTime = 30;
}
stopRecording = YES;
}
}
- (IBAction)btnPauseAndResumePressed:(id)sender
{
UIButton *PauseAndResumeButton = (UIButton*)sender;
if (PauseAndResumeButton.selected == NO)
{
PauseAndResumeButton.selected = YES;
NSLog(#"recording paused");
WeAreRecording = NO;
[MovieFileOutput stopRecording];
[self pauseTimer:recordingTimer];
[btnStartAndStop setEnabled:NO];
[btnBack setEnabled:YES];
[btnSwitchCameraInput setHidden:NO];
}
else
{
PauseAndResumeButton.selected = NO;
NSLog(#"recording resumed");
[btnStartAndStop setEnabled:YES];
[btnBack setEnabled:NO];
[btnSwitchCameraInput setHidden:YES];
WeAreRecording = YES;
NSDate *date = [NSDate date];
NSLog(#" date %#",date);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *recordedFileName = nil;
recordedFileName = [NSString stringWithFormat:#"output%#.mov",date];
NSString *documentsDirectory = [paths objectAtIndex:0];
self.outputPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",recordedFileName]];
NSLog(#"%#",self.outputPath);
[arrVideoName addObject:recordedFileName];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:self.outputPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:self.outputPath])
{
NSError *error;
if ([[NSFileManager defaultManager] removeItemAtPath:self.outputPath error:&error] == NO)
{
//Error - handle if requried
}
}
[self resumeTimer:recordingTimer];
//Start recording
[MovieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:self];
}
}
- (void) CameraSetOutputProperties
{
//SET THE CONNECTION PROPERTIES (output properties)
AVCaptureConnection *CaptureConnection = [MovieFileOutput connectionWithMediaType:AVMediaTypeVideo];
[CaptureConnection setVideoOrientation:AVCaptureVideoOrientationPortrait];
//Set frame rate (if requried)
CMTimeShow(CaptureConnection.videoMinFrameDuration);
CMTimeShow(CaptureConnection.videoMaxFrameDuration);
if (CaptureConnection.supportsVideoMinFrameDuration)
CaptureConnection.videoMinFrameDuration = CMTimeMake(1, CAPTURE_FRAMES_PER_SECOND);
if (CaptureConnection.supportsVideoMaxFrameDuration)
CaptureConnection.videoMaxFrameDuration = CMTimeMake(1, CAPTURE_FRAMES_PER_SECOND);
CMTimeShow(CaptureConnection.videoMinFrameDuration);
CMTimeShow(CaptureConnection.videoMaxFrameDuration);
}
- (AVCaptureDevice *) CameraWithPosition:(AVCaptureDevicePosition) Position
{
NSArray *Devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *Device in Devices)
{
if ([Device position] == Position)
{
NSLog(#"%d",Position);
return Device;
}
}
return nil;
}
#pragma mark - AVCaptureFileOutputRecordingDelegate Method
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
if(videoWriterInput.readyForMoreMediaData && WeAreRecording) [videoWriterInput appendSampleBuffer:sampleBuffer];
for(AVCaptureConnection *captureConnection in [captureOutput connections])
{
if ([captureConnection isVideoOrientationSupported])
{
AVCaptureVideoOrientation orientation = AVCaptureVideoOrientationLandscapeLeft;
[captureConnection setVideoOrientation:orientation];
}
}
UIDeviceOrientation curOr = [[UIDevice currentDevice] orientation];
CGAffineTransform t;
if (curOr == UIDeviceOrientationPortrait)
{
t = CGAffineTransformMakeRotation(-M_PI / 2);
}
else if (curOr == UIDeviceOrientationPortraitUpsideDown)
{
t = CGAffineTransformMakeRotation(M_PI / 2);
}
else if (curOr == UIDeviceOrientationLandscapeRight)
{
t = CGAffineTransformMakeRotation(M_PI);
}
else
{
t = CGAffineTransformMakeRotation(0);
}
}
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog(#"didFinishRecordingToOutputFileAtURL - enter");
NSLog(#"output file url : %#", [outputFileURL absoluteString]);
BOOL RecordedSuccessfully = YES;
if ([error code] != noErr)
{
// A problem occurred: Find out if the recording was successful.
id value = [[error userInfo] objectForKey:AVErrorRecordingSuccessfullyFinishedKey];
if (value)
{
RecordedSuccessfully = [value boolValue];
}
}
AVCaptureConnection *videoConnection=nil;
for ( AVCaptureConnection *connection in [MovieFileOutput connections] )
{
NSLog(#"%#", connection);
for ( AVCaptureInputPort *port in [connection inputPorts] )
{
NSLog(#"%#", port);
if ( [[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
}
}
}
if([videoConnection isVideoOrientationSupported]) // **Here it is, its always false**
{
[videoConnection setVideoOrientation:[[UIDevice currentDevice] orientation]];
} NSLog(#"Setting image quality");
NSData *videoData = [NSData dataWithContentsOfURL:outputFileURL];
[videoData writeToFile:self.outputPath atomically:NO];
[arrOutputUrl addObject:outputFileURL];
if (stopRecording)
{
[self mergeMultipleVideo];
}
}
//Method to merge multiple audios
-(void)mergeMultipleVideo
{
mixComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime nextClipStartTime = kCMTimeZero;
NSLog(#"Array of output file url : %#", arrOutputUrl);
if (arrOutputUrl.count > 0)
{
for(int i = 0 ;i < [arrOutputUrl count];i++)
{
AVURLAsset* VideoAsset = [[AVURLAsset alloc]initWithURL:[arrOutputUrl objectAtIndex:i] options:nil];
CMTimeRange timeRangeInAsset;
timeRangeInAsset = CMTimeRangeMake(kCMTimeZero, [VideoAsset duration]);
[compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, VideoAsset.duration) ofTrack:[[VideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
nextClipStartTime = CMTimeAdd(nextClipStartTime, timeRangeInAsset.duration);
}
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *myPathDocs = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.mov",self.finalRecordedVideoName]];
NSURL *url = [NSURL fileURLWithPath:myPathDocs];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
exportSession.outputURL=url;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
exportSession.shouldOptimizeForNetworkUse = YES;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
[self exportDidFinish:exportSession path:myPathDocs];
});
}];
}
-(void)exportDidFinish:(AVAssetExportSession*)session path:(NSString*)outputVideoPath
{
NSLog(#"session.status : %d",session.status);
if (session.status == AVAssetExportSessionStatusCompleted)
{
NSURL *outputURL = session.outputURL;
NSData *videoData = [NSData dataWithContentsOfURL:outputURL];
[videoData writeToFile:outputVideoPath atomically:NO];
if ([arrVideoName count] > 0)
{
for (int i = 0; i < [arrVideoName count]; i++)
{
NSArray* documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* fullFilePath = [[documentPaths objectAtIndex:0] stringByAppendingPathComponent: [NSString stringWithFormat:#"%#",[arrVideoName objectAtIndex:i]]];
NSLog(#"Full path of file to be deleted: %#",fullFilePath);
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if ([fileManager fileExistsAtPath:fullFilePath])
{
[fileManager removeItemAtPath:fullFilePath error:&error];
}
}
[arrVideoName removeAllObjects];
}
if (arrOutputUrl.count > 0)
{
[arrOutputUrl removeAllObjects];
}
[((ActOutAppDelegate *)ActOut_AppDelegate) removeLoadingViewfromView:self.view];
[self.view addSubview:afterRecordingPopupView];
}
}
Look at the AVCaptureConnection's enabled property. For your output connection, set enabled to NO instead of stopping the session.
I'm having trouble saving recorded audio files and playing them back after navigating between views.
I've been using the tutorial at http://www.techotopia.com/index.php/Recording_Audio_on_an_iPhone_with_AVAudioRecorder_%28iOS_4%29
I've added in a line to save the audio file, so my prepareForAudioRecording looks like the code below. This method is called in viewDidLoad.
-(void) prepareForAudioRecording
{
btnPlay.enabled = NO;
btnStop.enabled = NO;
NSArray *dirPaths;
NSString *docsDir;
dirPaths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *soundFilePath = [docsDir
stringByAppendingPathComponent:#"page1.caf"];
//this line added to save the audio file
[audioPlayer.data writeToFile:soundFilePath atomically:YES];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSDictionary *recordSettings = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:AVAudioQualityMin],
AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16],
AVEncoderBitRateKey,
[NSNumber numberWithInt: 2],
AVNumberOfChannelsKey,
[NSNumber numberWithFloat:44100.0],
AVSampleRateKey,
nil];
NSError *error = nil;
audioRecorder = [[AVAudioRecorder alloc]
initWithURL:soundFileURL
settings:recordSettings
error:&error];
if (error)
{
NSLog(#"error: %#", [error localizedDescription]);
} else {
[audioRecorder prepareToRecord];
}
}
As I said, i can record the audio and then play back the audio while in the same view controller, but when i navigate away from the view and return to it I can't playback the recording.
Firstly the play and stop buttons were disabled, which i thought should have only happen when the view was first loaded - but even when i enabled them and pressed the play button, there was no audio.
My other methods are below for my play, stop and record button.
- (IBAction)playAudio:(id)sender {
if (!audioRecorder.recording)
{
btnStop.enabled = YES;
btnRecord.enabled = NO;
//if (audioPlayer)
//[audioPlayer release];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc]
initWithContentsOfURL:audioRecorder.url
error:&error];
audioPlayer.delegate = self;
if (error)
NSLog(#"Error: %#",
[error localizedDescription]);
else
[audioPlayer play];
}
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
btnRecord.enabled = YES;
btnStop.enabled = NO;
}
-(void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
NSLog(#"Decode Error occurred");
}
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
}
-(void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error
{
NSLog(#"Encode Error occurred");
}
- (IBAction)recordAudio:(id)sender {
if (!audioRecorder.recording)
{
btnPlay.enabled = NO;
btnStop.enabled = YES;
[audioRecorder record];
}
}
- (IBAction)stopAudio:(id)sender {
btnStop.enabled = NO;
btnPlay.enabled = YES;
btnRecord.enabled = YES;
if (audioRecorder.recording)
{
[audioRecorder stop];
} else if (audioPlayer.playing) {
[audioPlayer stop];
}
}
I know there's a lot of code to look at there but if i'm missing out anything please let me know. I'd really appreciate any help with this, thanks.
EDIT 1: Ok, i've done a bit more investigating and opened up the folder where my audio files are saved. The audio records fine and the file size is around 400KB. When i push to the next view controller the filesize remains at 400KB. But as soon as i press my back button (not the preprogrammed one in the nav bar) the filesize goes to 4KB; an empty file. The code for my back button is simply:
- (IBAction)backPressed:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
Please help!
EDIT 2: It also happens with the back button in the nav bar too.
You're allocating audioPlayer with audioRecorder.url. If something happend to audioRecorder it's url might be invalid.
Try setting audioPlayer this way:
- (IBAction)playAudio:(id)sender {
if (!audioRecorder.recording)
{
btnStop.enabled = YES;
btnRecord.enabled = NO;
NSArray *dirPaths;
NSString *docsDir;
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *soundFilePath = [docsDir stringByAppendingPathComponent:#"page1.caf"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] nitWithContentsOfURL:soundFileUR
error:&error];
audioPlayer.delegate = self;
if (error)
NSLog(#"Error: %#",[error localizedDescription]);
else
[audioPlayer play];
}
}
In my application for recording and playing audio using AVAudioRecorder and AVAudioPlayer I came across a scenario in the case of incoming phone call.While the recording is in progress and if the phone call comes,the audio recorded after the phone call is only recorded.I want the recording recorded after the phone call to be the continuation of the audio recorded before the phone call.
I track the interruption occuring in audio recorder using the AVAudioRecorderDelegate methods
(void)audioRecorderBeginInterruption:(AVAudioRecorder *)avRecorder
and
(void)audioRecorderEndInterruption:(AVAudioRecorder *)avRecorder.
In my EndInterruption method I activates the audioSession.
Here is the recording code that I use
- (void)startRecordingProcess
{
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = nil;
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&err];
if(err)
{
DEBUG_LOG(#"audioSession: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return;
}
[audioSession setActive:YES error:&err];
err = nil;
if(err)
{
DEBUG_LOG(#"audioSession: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return;
}
// Record settings for recording the audio
recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatMPEG4AAC],AVFormatIDKey,
[NSNumber numberWithInt:44100],AVSampleRateKey,
[NSNumber numberWithInt: 2],AVNumberOfChannelsKey,
[NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey,
[NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
nil];
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:recorderFilePath];
if (fileExists)
{
BOOL appendingFileExists =
[[NSFileManager defaultManager] fileExistsAtPath:appendingFilePath];
if (appendingFileExists)
{
[[NSFileManager defaultManager]removeItemAtPath:appendingFilePath error:nil];
}
if (appendingFilePath)
{
[appendingFilePath release];
appendingFilePath = nil;
}
appendingFilePath = [[NSString alloc]initWithFormat:#"%#/AppendedAudio.m4a", DOCUMENTS_FOLDER];
fileUrl = [NSURL fileURLWithPath:appendingFilePath];
}
else
{
isFirstTime = YES;
if (recorderFilePath)
{
DEBUG_LOG(#"Testing 2");
[recorderFilePath release];
recorderFilePath = nil;
}
DEBUG_LOG(#"Testing 3");
recorderFilePath = [[NSString alloc]initWithFormat:#"%#/RecordedAudio.m4a", DOCUMENTS_FOLDER];
fileUrl = [NSURL fileURLWithPath:recorderFilePath];
}
err = nil;
recorder = [[recorder initWithURL:fileUrl settings:recordSetting error:&err]retain];
if(!recorder)
{
DEBUG_LOG(#"recorder: %# %d %#", [err domain], [err code], [[err userInfo] description]);
[[AlertFunctions sharedInstance] showMessageWithTitle:kAppName
message:[err localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"];
return;
}
//prepare to record
[recorder setDelegate:self];
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
}
While searching for a solution to this issue I came across another link
how to resume recording after interruption occured in iphone? and http://www.iphonedevsdk.com/forum/iphone-sdk-development/31268-avaudiorecorderdelegate-interruption.html which speaks of the same issue.
I tried the suggestions that were given in those links but were not successful.
I hope to make it work with AVAudioRecorder itself.
Is there any way I could find a solution to this issue?
All valuable suggestions are appreciated.
After several research I was notified by Apple that it's an issue with the current API. So I managed to find a workaround for the issue by saving the previous audio file just after interruption and joining it with the resumed audio file. Hope it helps someone out there who may face the same issue.
I was also facing a similar issue where AVAudioRecorder was recording only after interruption.
So i fixed this issue by maintaining an array of recordings and keeping them in the NSTemporaryDirectory and finally merging them at the end.
Below are the key steps:
Make your class listen to the AVAudioSessionInterruptionNotification.
On interruption begin (AVAudioSessionInterruptionTypeBegan), save your recording
On interruption end(AVAudioSessionInterruptionTypeEnded), start a new recording for interruption option AVAudioSessionInterruptionOptionShouldResume
Append all recordings on hitting the Save button.
The code snippets for the above mentioned steps are:
// 1. Make this class listen to the AVAudioSessionInterruptionNotification in viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];
// other coding stuff
}
// observe the interruption begin / end
- (void)handleAudioSessionInterruption:(NSNotification*)notification
{
AVAudioSessionInterruptionType interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
AVAudioSessionInterruptionOptions interruptionOption = [notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
switch (interruptionType) {
// 2. save recording on interruption begin
case AVAudioSessionInterruptionTypeBegan:{
// stop recording
// Update the UI accordingly
break;
}
case AVAudioSessionInterruptionTypeEnded:{
if (interruptionOption == AVAudioSessionInterruptionOptionShouldResume) {
// create a new recording
// Update the UI accordingly
}
break;
}
default:
break;
}
}
// 4. append all recordings
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully:(BOOL)flag
{
// append all recordings one after other
}
Here is a working example:
//
// XDRecordViewController.m
//
// Created by S1LENT WARRIOR
//
#import "XDRecordViewController.h"
#interface XDRecordViewController ()
{
AVAudioRecorder *recorder;
__weak IBOutlet UIButton* btnRecord;
__weak IBOutlet UIButton* btnSave;
__weak IBOutlet UIButton* btnDiscard;
__weak IBOutlet UILabel* lblTimer; // a UILabel to display the recording time
// some variables to display the timer on a lblTimer
NSTimer* timer;
NSTimeInterval intervalTimeElapsed;
NSDate* pauseStart;
NSDate* previousFireDate;
NSDate* recordingStartDate;
// interruption handling variables
BOOL isInterrupted;
NSInteger preInterruptionDuration;
NSMutableArray* recordings; // an array of recordings to be merged in the end
}
#end
#implementation XDRecordViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Make this class listen to the AVAudioSessionInterruptionNotification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:[AVAudioSession sharedInstance]];
[self clearContentsOfDirectory:NSTemporaryDirectory()]; // clear contents of NSTemporaryDirectory()
recordings = [NSMutableArray new]; // initialize recordings
[self setupAudioSession]; // setup the audio session. you may customize it according to your requirements
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self initRecording]; // start recording as soon as the view appears
}
- (void)dealloc
{
[self clearContentsOfDirectory:NSTemporaryDirectory()]; // remove all files files from NSTemporaryDirectory
[[NSNotificationCenter defaultCenter] removeObserver:self]; // remove this class from NSNotificationCenter
}
#pragma mark - Event Listeners
// called when recording button is tapped
- (IBAction) btnRecordingTapped:(UIButton*)sender
{
sender.selected = !sender.selected; // toggle the button
if (sender.selected) { // resume recording
[recorder record];
[self resumeTimer];
} else { // pause recording
[recorder pause];
[self pauseTimer];
}
}
// called when save button is tapped
- (IBAction) btnSaveTapped:(UIButton*)sender
{
[self pauseTimer]; // pause the timer
// disable the UI while the recording is saving so that user may not press the save, record or discard button again
btnSave.enabled = NO;
btnRecord.enabled = NO;
btnDiscard.enabled = NO;
[recorder stop]; // stop the AVAudioRecorder so that the audioRecorderDidFinishRecording delegate function may get called
// Deactivate the AVAudioSession
NSError* error;
[[AVAudioSession sharedInstance] setActive:NO error:&error];
if (error) {
NSLog(#"%#", error);
}
}
// called when discard button is tapped
- (IBAction) btnDiscardTapped:(id)sender
{
[self stopTimer]; // stop the timer
recorder.delegate = Nil; // set delegate to Nil so that audioRecorderDidFinishRecording delegate function may not get called
[recorder stop]; // stop the recorder
// Deactivate the AVAudioSession
NSError* error;
[[AVAudioSession sharedInstance] setActive:NO error:&error];
if (error) {
NSLog(#"%#", error);
}
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark - Notification Listeners
// called when an AVAudioSessionInterruption occurs
- (void)handleAudioSessionInterruption:(NSNotification*)notification
{
AVAudioSessionInterruptionType interruptionType = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
AVAudioSessionInterruptionOptions interruptionOption = [notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
switch (interruptionType) {
case AVAudioSessionInterruptionTypeBegan:{
// • Recording has stopped, already inactive
// • Change state of UI, etc., to reflect non-recording state
preInterruptionDuration += recorder.currentTime; // time elapsed
if(btnRecord.selected) { // timer is already running
[self btnRecordingTapped:btnRecord]; // pause the recording and pause the timer
}
recorder.delegate = Nil; // Set delegate to nil so that audioRecorderDidFinishRecording may not get called
[recorder stop]; // stop recording
isInterrupted = YES;
break;
}
case AVAudioSessionInterruptionTypeEnded:{
// • Make session active
// • Update user interface
// • AVAudioSessionInterruptionOptionShouldResume option
if (interruptionOption == AVAudioSessionInterruptionOptionShouldResume) {
// Here you should create a new recording
[self initRecording]; // create a new recording
[self btnRecordingTapped:btnRecord];
}
break;
}
default:
break;
}
}
#pragma mark - AVAudioRecorderDelegate
- (void) audioRecorderDidFinishRecording:(AVAudioRecorder *)avrecorder successfully:(BOOL)flag
{
[self appendAudiosAtURLs:recordings completion:^(BOOL success, NSURL *outputUrl) {
// do whatever you want with the new audio file :)
}];
}
#pragma mark - Timer
- (void)timerFired:(NSTimer*)timer
{
intervalTimeElapsed++;
[self updateDisplay];
}
// function to time string
- (NSString*) timerStringSinceTimeInterval:(NSTimeInterval)timeInterval
{
NSDate *timerDate = [NSDate dateWithTimeIntervalSince1970:timeInterval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"mm:ss"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];
return [dateFormatter stringFromDate:timerDate];
}
// called when recording pauses
- (void) pauseTimer
{
pauseStart = [NSDate dateWithTimeIntervalSinceNow:0];
previousFireDate = [timer fireDate];
[timer setFireDate:[NSDate distantFuture]];
}
- (void) resumeTimer
{
if (!timer) {
timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerFired:)
userInfo:Nil
repeats:YES];
return;
}
float pauseTime = - 1 * [pauseStart timeIntervalSinceNow];
[timer setFireDate:[previousFireDate initWithTimeInterval:pauseTime sinceDate:previousFireDate]];
}
- (void)stopTimer
{
[self updateDisplay];
[timer invalidate];
timer = nil;
}
- (void)updateDisplay
{
lblTimer.text = [self timerStringSinceTimeInterval:intervalTimeElapsed];
}
#pragma mark - Helper Functions
- (void) initRecording
{
// Set the audio file
NSString* name = [NSString stringWithFormat:#"recording_%#.m4a", #(recordings.count)]; // creating a unique name for each audio file
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:#[NSTemporaryDirectory(), name]];
[recordings addObject:outputFileURL];
// Define the recorder settings
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:#(kAudioFormatMPEG4AAC) forKey:AVFormatIDKey];
[recordSetting setValue:#(44100.0) forKey:AVSampleRateKey];
[recordSetting setValue:#(1) forKey:AVNumberOfChannelsKey];
NSError* error;
// Initiate and prepare the recorder
recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:&error];
recorder.delegate = self;
recorder.meteringEnabled = YES;
[recorder prepareToRecord];
if (![AVAudioSession sharedInstance].inputAvailable) { // can not record audio if mic is unavailable
NSLog(#"Error: Audio input device not available!");
return;
}
intervalTimeElapsed = 0;
recordingStartDate = [NSDate date];
if (isInterrupted) {
intervalTimeElapsed = preInterruptionDuration;
isInterrupted = NO;
}
// Activate the AVAudioSession
[[AVAudioSession sharedInstance] setActive:YES error:&error];
if (error) {
NSLog(#"%#", error);
}
recordingStartDate = [NSDate date]; // Set the recording start date
[self btnRecordingTapped:btnRecord];
}
- (void)setupAudioSession
{
static BOOL audioSessionSetup = NO;
if (audioSessionSetup) {
return;
}
AVAudioSession* session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord
withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker
error:Nil];
[session setMode:AVAudioSessionModeSpokenAudio error:nil];
audioSessionSetup = YES;
}
// gets an array of audios and append them to one another
// the basic logic was derived from here: http://stackoverflow.com/a/16040992/634958
// i modified this logic to append multiple files
- (void) appendAudiosAtURLs:(NSMutableArray*)urls completion:(void(^)(BOOL success, NSURL* outputUrl))handler
{
// Create a new audio track we can append to
AVMutableComposition* composition = [AVMutableComposition composition];
AVMutableCompositionTrack* appendedAudioTrack =
[composition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
// Grab the first audio track that need to be appended
AVURLAsset* originalAsset = [[AVURLAsset alloc]
initWithURL:urls.firstObject options:nil];
[urls removeObjectAtIndex:0];
NSError* error = nil;
// Grab the first audio track and insert it into our appendedAudioTrack
AVAssetTrack *originalTrack = [[originalAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
CMTimeRange timeRange = CMTimeRangeMake(kCMTimeZero, originalAsset.duration);
[appendedAudioTrack insertTimeRange:timeRange
ofTrack:originalTrack
atTime:kCMTimeZero
error:&error];
CMTime duration = originalAsset.duration;
if (error) {
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(NO, Nil);
});
}
}
for (NSURL* audioUrl in urls) {
AVURLAsset* newAsset = [[AVURLAsset alloc]
initWithURL:audioUrl options:nil];
// Grab the rest of the audio tracks and insert them at the end of each other
AVAssetTrack *newTrack = [[newAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
timeRange = CMTimeRangeMake(kCMTimeZero, newAsset.duration);
[appendedAudioTrack insertTimeRange:timeRange
ofTrack:newTrack
atTime:duration
error:&error];
duration = appendedAudioTrack.timeRange.duration;
if (error) {
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(NO, Nil);
});
}
}
}
// Create a new audio file using the appendedAudioTrack
AVAssetExportSession* exportSession = [AVAssetExportSession
exportSessionWithAsset:composition
presetName:AVAssetExportPresetAppleM4A];
if (!exportSession) {
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(NO, Nil);
});
}
}
NSArray* appendedAudioPath = #[NSTemporaryDirectory(), #"temp.m4a"]; // name of the final audio file
exportSession.outputURL = [NSURL fileURLWithPathComponents:appendedAudioPath];
exportSession.outputFileType = AVFileTypeAppleM4A;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
BOOL success = NO;
// exported successfully?
switch (exportSession.status) {
case AVAssetExportSessionStatusFailed:
break;
case AVAssetExportSessionStatusCompleted: {
success = YES;
break;
}
case AVAssetExportSessionStatusWaiting:
break;
default:
break;
}
if (handler) {
dispatch_async(dispatch_get_main_queue(), ^{
handler(success, exportSession.outputURL);
});
}
}];
}
- (void) clearContentsOfDirectory:(NSString*)directory
{
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
for (NSString *file in [fm contentsOfDirectoryAtPath:directory error:&error]) {
[fm removeItemAtURL:[NSURL fileURLWithPathComponents:#[directory, file]] error:&error];
}
}
#end
I know its too late to answer to question, but hope this helps someone else!
I am trying to record users voice with background music playing behind. I am able to set a session and play background and record concurrently using AVAudioSessionCategoryPlayAndRecord. But it's recording lot's of noice,
Does any one have an idea how to reduce the noise?
#define DOCUMENTS [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
#define PATH_ARQUIVO [DOCUMENTS stringByAppendingPathComponent:#"gravacao.ma4"]
-(IBAction) recordAudio:(UIButton *)sender {
NSURL* urlArquivo = [[NSURL alloc] initFileURLWithPath:PATH_ARQUIVO];
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:AVAudioQualityMin], AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16], AVEncoderBitRateKey,
[NSNumber numberWithInt:2], AVNumberOfChannelsKey,
[NSNumber numberWithFloat:44.1], AVSampleRateKey,
nil];
NSError* error;
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:urlArquivo settings:dic error:&error];
if (error) {
NSLog(#"error: %#", [erro localizedDescription]);
} else {
//buffering
[self.audioRecorder prepareToRecord];
//recording
[self.audioRecorder record];
}
}
-(IBAction) stopRecorder:(UIButton *)sender {
if ([self.audioRecorder isRecording]) {
[self.audioRecorder stop];
}
}
-(IBAction) PlayAudio:(UIButton *)sender {
NSURL* urlArquivo = [[NSURL alloc] initFileURLWithPath:PATH_ARQUIVO];
NSError* error;
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlArquivo error:&error];
if (erro) {
NSLog(#"error %#", [error localizedDescription]);
} else {
self.audioPlayer.numberOfLoops = HUGE_VALF;
self.audioPlayer.enableRate = YES;
[self.audioPlayer prepareToPlay];
[self.audioPlayer play];
}
}
-(IBAction) stopPlaying:(UIButton *)sender {
if ([self.audioPlayer isPlaying]) {
[self.audioPlayer stop];
}
}
-(IBAction) changeRate:(UISlider *)sender {
self.audioPlayer.rate = sender.value * 2;
/* it's a UISlider, max value = 1, min = 0 */
}
I am using some code to record and save some audio. Then play it back later.
It works perfect on the Emulator, but on the device the file is size 0.
I am thinking maybe there is something I am missing.
Here is my path where the file gets saved to, and loaded from.
On the phone : /var/mobile/Applications/8CE3099A-0CA6-411F-B197-B57699076E8F/Documents/soundfile.caf
On the Emulator: /Users/johny/Library/Application Support/iPhone Simulator/4.0.2/Applications/8BE98F55-250D-49EC-B1C0-C2C4D48B4CCE/Documents/soundfile.caf
The code I use to make the path is :
NSString* fileName = [[self
documentsPath]stringByAppendingPathComponent:#"soundfile.caf"];
Method to load the audio file. This seems to work ok. At least it doest show any debug message.
-(AudioFileID)openAudioFile:(NSString*)filePath
{
NSLog(#"openAudioFile");
AudioFileID outAFID;
// use the NSURl instead of a cfurlref cuz it is easier
NSURL * afUrl = [NSURL fileURLWithPath:filePath];
OSStatus result = AudioFileOpenURL((CFURLRef)afUrl, kAudioFileReadPermission, 0,
&outAFID);
if (result != 0) NSLog(#"cannot open file: %#",filePath);
return outAFID;
}
-(UInt32)audioFileSize:(AudioFileID)fileDescriptor
{
NSLog(#"audioFileSize");
UInt64 outDataSize = 0;
UInt32 thePropSize = sizeof(UInt64);
OSStatus result = AudioFileGetProperty(fileDescriptor,
kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
if(result != 0) NSLog(#"cannot find file size");
return (UInt32)outDataSize;
}
The problem i see is the filesize is 0. Since these 2 methods work. When i print out the filesize in an NSLog it is 0.
The method where i create my AudioRecorder
- (NSError*) createAVAudioRecorder
{
NSLog(#"creatAVAudioRecorder");
[audioRecorder release];
audioRecorder = nil;
NSString *destinationString = [[self
documentsPath]stringByAppendingPathComponent:#"soundfile.caf"];
NSURL *destinationURL = [NSURL fileURLWithPath: destinationString];
NSMutableDictionary *recordSettings =[[NSMutableDictionary alloc]
initWithCapacity:10];
[recordSettings setObject:[NSNumber numberWithInt: kAudioFormatLinearPCM] forKey:
AVFormatIDKey];
float sampleRate = 44100; // 22050
[recordSettings setObject:[NSNumber numberWithFloat:sampleRate] forKey:
AVSampleRateKey];
int NumChannels = 2; //stero or mono
[recordSettings setObject:[NSNumber
numberWithInt:NumChannels]forKey:AVNumberOfChannelsKey];
int bitDepth = 16;
[recordSettings setObject:[NSNumber numberWithInt:bitDepth]
forKey:AVLinearPCMBitDepthKey];
bool bigEndian = NO;
[recordSettings setObject:[NSNumber
numberWithBool:bigEndian]forKey:AVLinearPCMIsBigEndianKey];
bool floatingSamples = NO;
[recordSettings setObject:[NSNumber
numberWithBool:floatingSamples]forKey:AVLinearPCMIsFloatKey];
NSError *recorderSetupError = nil;
audioRecorder = [[AVAudioRecorder alloc] initWithURL:destinationURL
settings:recordSettings error:&recorderSetupError];
[recordSettings release];
if (recorderSetupError)
{
NSLog(#"Record Setup Error");
UIAlertView *cantRecordAlert = [[UIAlertView alloc] initWithTitle: #"Can't
record" message: [recorderSetupError localizedDescription]delegate: nil
cancelButtonTitle:#"OK" otherButtonTitles:nil];
[cantRecordAlert show];
[cantRecordAlert release];
return recorderSetupError;
}
[audioRecorder prepareToRecord];
audioRecorder.delegate = self;
return recorderSetupError;
}
I call these methods to start and stop recording
-(IBAction) startRecording
{
[self createAVAudioRecorder ];
if (! [self alertIfNoAudioInput])
return;
[audioRecorder record];
}
-(IBAction) stopRecording
{
NSLog(#"stopRecording Method");
[audioRecorder stop];
}
Many Thanks,
-Code