PDA

View Full Version : save audio record form mike to documents directory




gbenna
Mar 7, 2012, 09:45 AM
I want to record audio inside an app using the iPhone mike when the user taps a button and when they stop the recording by tapping the same button it is saved in the documents directory. Think I have it pretty well but I'm not sure how to save it to the documents directory.

Here is my code;



// I set up the button
-(IBAction)recordAudio:(id)sender;{
NSLog(@"recordAudio");
recorder = nil;
if ( isNotRecording){
isNotRecording=NO;
[record setTitle:@"Record Greeting" forState:UIControlStateNormal];

[recorder setDelegate:self];
[recorder prepareToRecord];
[recorder record];
}
else{
isNotRecording = YES;
[record setTitle:@"Stop Recording" forState:UIControlStateNormal];
[recorder stop];
}
//I start the audio session
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryRecord error:nil];
//I find the path to the documents directory and name the file after what is put into a textfield and append it with .AAC
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[[self imageNameTextField]text]];
fullPath = [fullPath stringByAppendingFormat:@".AAC"];
//set settings for audio file
NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] initWithCapacity:10];

if(recordEncoding==ENC_AAC){

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

//here's where I get lost!
NSURL *url = [NSURL fileURLWithPath:fullPath];
NSError *error = nil;
recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:&error];


}

Can anyone help me out I have looked on line but there isn't much that I can find, but maybe I'm searching wrong.



gbenna
Mar 8, 2012, 02:29 PM
I figured this out. I moved the button actions below the call of the audio session and the path and the settings. I also fiddled with the settings for the audio.

dejo
Mar 8, 2012, 02:52 PM
Resolved (http://forums.macrumors.com/showthread.php?t=1093283) then?

gbenna
Mar 8, 2012, 03:43 PM
I thought so but now no so I did not getting it working, Can I delete the part that said I had it figured out?

Thanks

gbenna
Mar 8, 2012, 04:07 PM
Do I have to save this to a temp file first before it is saved to the documents directory?

I am at a loss. I've looked at everything but can't seem to find out why is isn't working HELP

gbenna
Mar 8, 2012, 04:50 PM
I think I am a step closer. I found when I do the file to the temp folder it is not appending the format with the .m4a this is curious since when I did it with a photo it was fine. I am trying to name the recording with the text I put into a textfield by using the code.

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[[self imageNameTextField]text]];

it is saved to the temp folder as the textfield text but when I put

fullPath = [fullPath stringByAppendingFormat:@".m4a"];

it does't add it.

Any ideas why not and what I can do to change it?

dejo
Mar 8, 2012, 06:52 PM
You're calling stringByAppendingFormat: but not giving it the proper parameters, which consists of at least two pieces: a format string and a list of arguments. You should either rework it to call it properly or, based on how your using it, call another instance method of NSString that is better suited to working with paths.

gbenna
Mar 9, 2012, 01:52 AM
OK I'm totally baffled. I finally got this to work kind of.

here is my code in the .h file I have the <AVAudioRecorderDelegate>

IBOutlet UIButton *record;
AVAudioRecorder *recorder;
int recordEncoding;
enum
{ ENC_IMA4 = 3,
}encodingTypes;
BOOL isRecording;
}
@property (nonatomic, retain) IBOutlet UIButton *record;
-(IBAction)recordAudio:(id)sender;

then in my .m

I synthesize record and


-(IBAction)recordAudio:(id)sender{
NSLog(@"recordAudio");
recorder = nil;
//I do my settings for IMA4 I found when I was doing AAC it didn't work(no //longer supported?)
NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] initWithCapacity:100];
if(recordEncoding == ENC_IMA4)
{
[recordSettings setObject:[NSNumber numberWithInt: kAudioFormatAppleIMA4 ] forKey: AVFormatIDKey];
[recordSettings setObject:[NSNumber numberWithFloat:44100.0] forKey: AVSampleRateKey];
[recordSettings setObject:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
[recordSettings setObject:[NSNumber numberWithInt:6400] forKey:AVEncoderBitRateKey];
[recordSettings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[recordSettings setObject:[NSNumber numberWithInt: AVAudioQualityMin] forKey: AVEncoderAudioQualityKey];
}
//Then I set up my path to the documents directory and add the name from a //textfield and append an extension .caf (Apple's new .mov for audio
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *audioPath = [documentsDirectory stringByAppendingPathComponent: [[self imageNameTextField]text]];
audioPath = [audioPath stringByAppendingFormat:@".caf"];
//I hand that to the url file being recorded and allocate the memory
NSURL *url = [NSURL fileURLWithPath:audioPath];
recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];
//And at last I set up my button to record and stop recording
if(isRecording){
isRecording=NO;
[record setTitle:@"Record Greeting" forState:UIControlStateNormal];
[recorder setDelegate:self];
[recorder prepareToRecord];
[recorder record];
NSLog(@"isRecording");
}
else {
isRecording =YES;
[record setTitle:@"Stop Recording" forState:UIControlStateNormal];
[recorder stop];
NSLog(@"stopped");
}
}


You would think it would work. Well not really as I said.

I have tried every configuration of the isRecording vs isNotRecording
as well as YES/NO
The problem is that when load this ViewController from a tableViewViewController the audio is already recording... so when I tap the button for the first time it stops the recording and then when I tap it again to stop it, it either stays stopped and I get no file in my documents directory or Sometimes I get a file but it hasn't been saved right so it doesn't play.
BUT and here's the kicker. When I do it this way it starts out recording when the view is opened. When I tap the button the recording stops. then when I tap it again to what I would think would be stopping it it starts recording again.
I can do other things like take a photo or save text in the view ok, all the time recording audio. When I leave the view the app crashes because the recorder is still recording. I have taken a snapshot of the app after this and downloaded it to my computer and opened the file and played the saved audio and it plays fine just from when the view opens to when I tap the button then its silent for the time until I tap the button to stop the recording and it starts up again until I leave the view.
DOES anybody have any idea what is going on here? why should the recorder be turned on when the view is loaded? I have put session to NO and YES and no session and I have had a temp file to record to and no temp file nothing seems to matter. I'm sure it's something simple (at least I hope it is) but by now I can't see it. HELP

dejo
Mar 9, 2012, 08:58 AM
You are still not using stringByAppendingFormat: properly. If you are going to ignore my help, I'm not sure what else I can do.

chown33
Mar 9, 2012, 12:05 PM
-(IBAction)recordAudio:(id)sender{
NSLog(@"recordAudio");
recorder = nil;
//I do my settings for IMA4 I found when I was doing AAC it didn't work(no //longer supported?)
NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] initWithCapacity:100];
if(recordEncoding == ENC_IMA4)
{
[recordSettings setObject:[NSNumber numberWithInt: kAudioFormatAppleIMA4 ] forKey: AVFormatIDKey];
[recordSettings setObject:[NSNumber numberWithFloat:44100.0] forKey: AVSampleRateKey];
[recordSettings setObject:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
[recordSettings setObject:[NSNumber numberWithInt:6400] forKey:AVEncoderBitRateKey];
[recordSettings setObject:[NSNumber numberWithInt:16] forKey:AVLinearPCMBitDepthKey];
[recordSettings setObject:[NSNumber numberWithInt: AVAudioQualityMin] forKey: AVEncoderAudioQualityKey];
}
//Then I set up my path to the documents directory and add the name from a //textfield and append an extension .caf (Apple's new .mov for audio
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *audioPath = [documentsDirectory stringByAppendingPathComponent: [[self imageNameTextField]text]];
audioPath = [audioPath stringByAppendingFormat:@".caf"];
//I hand that to the url file being recorded and allocate the memory
NSURL *url = [NSURL fileURLWithPath:audioPath];
recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];
//And at last I set up my button to record and stop recording
if(isRecording){
isRecording=NO;
[record setTitle:@"Record Greeting" forState:UIControlStateNormal];
[recorder setDelegate:self];
[recorder prepareToRecord];
[recorder record];
NSLog(@"isRecording");
}
else {
isRecording =YES;
[record setTitle:@"Stop Recording" forState:UIControlStateNormal];
[recorder stop];
NSLog(@"stopped");
}
}

Think very carefully about what the red-hilited lines are doing. If you click the button once, walk through exactly what happens. Is that correct? Now suppose the button is clicked a second time. Walk through exactly what happens? Is it correct?

What is it called when you don't release an object you own, yet do something like assign nil to the only variable with a reference to that object? Does the object get dealloc'ed or get stopped? Or does it keep on existing, doing what it was doing before you lost track of it?


From the posted code, I get the sense that you didn't really sit down and design it. Instead, you just made it up as you typed it in. I say that because there's no accounting for the fact that recorder presumably starts as nil, being an unassigned ivar. There's also no accounting for the fact that when recorder is started, you have to stop the same recorder object, rather than making a new one.

If you sit down with pencil and paper, and write down a simple series of steps for what the method should do, you can then type that in and we can review the design. Or the flaws in your current code, where it doesn't match a simple design, should become apparent.

Example of simple design:
1. If recorder is nil, then we're not recording (logically, it's impossible to be recording if you don't have a recorder object).
2. If recorder is non-nil, then we're either recording or we're not.
3. If we're recording, stop.
4. If we're not recording, start.

Fiirst confirm that this is a logical design. Second, compare it to what your code does, and think about what happens in your code that isn't in the design, and vice versa.

The next part of the design would break down exactly what it means to start recording (build the format dictionary, create & start the recorder, setting delegates, etc.) and what it means to stop recording (stop the recorder, remove delegates, etc.). But if you don't have the basic logic down first, then start/stop won't work, because your object creation and lifetime will be wrong.


As to why the recording might be starting when you first show the view, look in your log for when recordAudio: is called. You have an NSLog, so find it.

If the recordAudio: happens with no button presses, then something in your nib or in your code is causing the button to act as if it were pressed. I recommend using the debugger to set a breakpoint on recordAudio:, and then see how it got there.

If you don't know how to use the debugger and set breakpoints, now is a good time to learn.

You can also force a crash by always throwing an exception, or making an assertion that's always false. Look up NSException and NSAssert.

https://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Assertions/Assertions.html%23//apple_ref/doc/uid/10000014i

After it crashes, you can look at the stack backtrace to see what led up to the crash. This is a brute-force way of debugging, and it's far easier to simply learn how to use the debugger and set breakpoints.

gbenna
Mar 9, 2012, 12:55 PM
I am trying to understand what you are saying.
Here is what I am trying to do. I want to record audio with mike and name it with the name the user puts into a UITextView and then append it with .caf so it has the correct extension to be a audio file. I want to put that file into the Documents Directory.

//So I create a string of the text from the textfield and append it

NSString *audioString=@".caf";
audioString = [audioString stringByAppendingFormat:@".caf",[[self imageNameTextField]Text];
//I find the path to the Documents Directory and name it with the textfield text

NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *audioPath = [documentsDirectory stringByAppendingPathComponent:audioString];

//Then I take the file and save it to the path with the name

NSURL *url = [NSURL fileURLWithPath:audioPath];

recorder = [[ AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];

This ends up naming the file .caf.caf (I know this is wrong, could you show me what you mean)

This file DOES NOT work as an audio file and even though the snapshot shows the .caf.caf in the documents directory when I download the screenshot and look at it the file .caf.caf isn't located in Documents Directory.
another problem is still when the view is opened the audio starts recording, when I press the button to start the recording the recording really stops and when the button is tapped again to stop the recording the recorder starts u again.

I hope when I get the stringByAppendingFormat it will work the way it should.

I would really like your help in figuring this out.

dejo
Mar 9, 2012, 01:33 PM
This ends up naming the file .caf.caf (I know this is wrong, could you show me what you mean)

I've already provided you with a link in your other thread (http://forums.macrumors.com/showthread.php?t=1334090) (with a similar issue) to the String Programming Guide, and asked that you pay notice of the "Formatting String Objects" section. Have you read that yet? If so, was there something in there that you didn't understand? If so, what?

gbenna
Mar 13, 2012, 02:55 PM
Thanks for everyone especially Dejo and chown33. Your help was invaluable, I finally got it to record correctly and with the name I wanted.