Access audio data for particular device (AVAudioSession) - ios

I am using AVAudioSession and
I know this sounds like insanity maybe but is there any way to access the input and output data for a microphone or speaker? For example, I want to print out all the audio data streaming into the microphone in bytes.
Is there any way to do this? I looked into core audio but I am not familiar with it enough to be able to use it to print out both the input and output.
Thanks!

AVAudioSession is only the very first step to getting iOS to recognize that your app wants to do something with the mic. I'm assuming by "print" you mean visually or appending the audio data to a file.
If you are looking to record audio to a file you could use the AVAudioRecorder class:
//What are our output settings?
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];
// Create the recorder where recorder is declared as a property of the class & outputFileURL is an NSURL to a file path you want to record to
recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:NULL];
recorder.delegate = self;
[recorder prepareToRecord];
// Tell the system you are going to use the mic
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:0 error:nil];
[session setActive:YES error:nil];
// Start recording
[recorder record];
---------------------------------
//When you are done call:
[recorder stop];
NSURL *savedFileURL = recorder.url;
If you want to use CoreAudio so that you could get each buffer and decide what to do with it your going to have to take a step back and learn the structure of CoreAudio. CoreAudio allows you to be more flexible about the routing and the design of your audio system. It can't be explained in one post but if you are going that way one method to get data from the mic is to:
Create an AudioUnit from an AudioComponent (Type: kAudioUnitType_Output, Subtype: kAudioUnitSubType_RemoteIO)
Enable IO on the audioUnit
Set the kAudioUnitProperty_StreamFormat property of the audioUnit to an AudioStreamBasicDescription (most likely PCM format)
Intercept data using AURenderCallbackStruct

Related

AVAudioRecorder not saving recording

I am making an iOS game. One of the things I need to do is to allow the user to make a quick little audio recording. This all works, but the recording is only temporarily saved. So when the user closes the app and reopens it, the recording should be able to play again, but it doesn't, it gets deleted when I close the app. I don't understand what I am doing wrong. Below is my code:
I setup the AVAudioRecorder in the ViewDidLoad method like this:
// Setup audio recorder to save file.
NSArray *pathComponents = [NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject], #"MyAudioMemo.m4a", nil];
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
// Setup audio session.
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
audio_recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:nil];
audio_recorder.delegate = self;
audio_recorder.meteringEnabled = YES;
[audio_recorder prepareToRecord];
I have got the AVAudio delegate methods, too:
-(void)audio_playerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag {
NSLog(#"Did finish playing: %d", flag);
}
-(void)audio_playerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error {
NSLog(#"Decode Error occurred");
}
-(void)audio_recorderDidFinishRecording:(AVAudioPlayer *)recorder successfully:(BOOL)flag {
NSLog(#"Did finish recording: %d", flag);
}
-(void)audio_recorderEncodeErrorDidOccur:(AVAudioPlayer *)recorder error:(NSError *)error {
NSLog(#"Encode Error occurred");
}
When I want to play, record or stop the audio, I have made the following IBActions which are linked to UIButtons:
-(IBAction)play_audio {
NSLog(#"Play");
if (!audio_recorder.recording){
audio_player = [[AVAudioPlayer alloc] initWithContentsOfURL:audio_recorder.url error:nil];
[audio_player setDelegate:self];
[audio_player play];
}
}
-(IBAction)record_voice {
NSLog(#"Record");
if (!audio_recorder.recording) {
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Start recording.
[audio_recorder record];
}
else {
// Pause recording.
[audio_recorder pause];
}
}
-(IBAction)stop_audio {
NSLog(#"Stop");
[audio_recorder stop];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setActive:NO error:nil];
}
If you try my code you will see that it works, but it only seems to save the audio file temporarily.
What am I doing wrong? I thought I had used all the correct AVAudioRecorder methods?
To make a working recorder and save the recorded files, you need to:
Create a new audio session
Make sure microphone is connected/working
Start recording
Stop recording
Save the recorded audio file
Play the saved voice file
You're missing step 5 in your code, so the file that's just recorded is still available for you to play, but once you close the app, as it's not saved into an actual file somewhere in the app's directories, you lose it. You should add a method to save the recorded audio into a file so that you can access it any time later:
-(void) saveAudioFileNamed:(NSString *)filename {
destinationString = [[self documentsPath] stringByAppendingPathComponent:filename];
NSLog(#"%#", destinationString);
NSURL *destinationURL = [NSURL fileURLWithPath: destinationString];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
audio_recorder = [[AVAudioRecorder alloc] initWithURL:destinationURL settings:settings error:&error];
audio_recorder.delegate = self;
}
Unrelated to this problem, but a general thing to mention is that you must follow Apple's (Objective-C's) naming conventions when defining variables, etc. audio_recording in no way follows these guidelines. You could use something like audioRecording instead.
Ok so thanks for #Neeku so much for answering my question, certainly something to take into account but its still not solving the problem. I was searching around and I found this example which works perfectly and more to the point shows me that I was approaching this entire functionality the wrong way. I think another one of my problems is that my app would delete the previously saved audio file in the ViewDidload method. And as well as that I don't think I have used the AVAudioSession instances correctly.
Anyway the example I found is very useful and solves my problem perfectly, you can find it here: Record audio and save permanently in iOS
Hope that helps anyone else who is having a similar problem to me.

save recorded audio in documents directory and create a database that contains all audio files

I am new to ios development and facing problem in creating an app.
My app can record audio and save and play it back again.
I have used AVAudiorecorder and AVAudioplayer.
As soon as the recording is stopped, it asks the user to give the name (in textfield) to the file to save and then, the list of all saved files should appear and can be played on the next screen. (same as the italk recorder app)
I want to save the recorded file in documents directory and want a database that will contain metadata of all audio files. Database should have id, date, title, path.
This is what i have done :
if (player.playing) {
[player stop];
}
if (!recorder.recording) {
[self updatefilecounter];
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
// Set the audio file
//filename = #"nnnn";
filename = #"Music/nnnn";
filename = [filename stringByAppendingString:#(filecounter).stringValue];
filename = [filename stringByAppendingString:#".m4a"];
NSArray *pathComponents = [NSArray arrayWithObjects:
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject],filename,nil];
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
// Setup audio session
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
// Define the recorder setting
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];
// Initiate and prepare the recorder
recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:NULL];
recorder.delegate = self;
recorder.meteringEnabled = YES;
NSLog(#"%#", outputFileURL);
NSLog(#"%#", filename);
[recorder record];
} else {
// Stop recording
[recorder stop];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setActive:NO error:nil];
SaveAudioVC *vc = [self.storyboardinstantiateViewControllerWithIdentifier:#"SaveAudioVC"];
[self.navigationController pushViewController:vc animated:YES];
}
}
File gets saved as nnnn.1, nnnn.2 and so on, but when i stop the app and run again it overwrites the last files and i want the the user to give the name of his/her choice to save the file and display in last screen with the given name to the recorded file.
I have referred this link : Record audio and save permanently in iOS
But my other requirements are not matching so plz guide me to save the recorded file in documents directory, give the name to the file as per user wish in the text field and display all the saved files and create a database as mentioned above.
i think you should give path string like [#"Music" stringByAppendingByPathComponents:#"nnnn"] instead of filename = #"Music/nnnn";i hope it will give you some idea.when i starts audio recording i give it temporary name like temp.m4a than when i stops recording i use bellow method to replace temporary name with new name.
/*
give name and replace it with defaultName'temp.m4a'
*/
- (void)convertNewName:(NSString*)newname{
NSString* folder=[documentDirectory stringByAppendingPathComponent:#"VoiceDB"];
NSString* defaultfile=[folder stringByAppendingPathComponent:#"temp.m4a"];
newname=[NSString stringWithFormat:#"%#.m4a",newname];
NSString *newPath = [[defaultfile stringByDeletingLastPathComponent] stringByAppendingPathComponent:newname];
[[NSFileManager defaultManager] moveItemAtPath:defaultfile toPath:newPath error:nil];
}

Record stereo from digital line in

I'm trying to get stereo samples from the digital line in on the iPhone/iPad.
To get a stereo line-in, I use the Mickey Blue microphone "Addon" for iOS.
See This.
I use the Aruts iOS CoreAudio example from Here.
This works perfect to get samples in mono from the line in, but I can't figure out the correct setup for stereo.
If there's anyone who can point me in the right direction that would be really helpful.
I need the samples directly to process them first, so writing to a file first is not an option.
I guess you could use the AVAudioRecorder like this:
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[session setActive:YES error:nil];
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
//this should enable stereo
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];
AVAudioRecorder *recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:NULL];
recorder.delegate = self;
[recorder prepareToRecord];
[recorder record];
Found the solution:
The AudioStreamBasicDescription will only accept 32bit per channel in stereo mode somehow. Using the code below enables getting the samples in stereo (currently only using additional hardware)
Here is the audio format I used to get it to work:
AudioStreamBasicDescription stereoStreamFormat;
stereoStreamFormat.mSampleRate = 44100.00;
stereoStreamFormat.mFormatID = kAudioFormatLinearPCM;
stereoStreamFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
stereoStreamFormat.mBytesPerPacket = 4;
stereoStreamFormat.mBytesPerFrame = 4;
stereoStreamFormat.mFramesPerPacket = 1;
stereoStreamFormat.mChannelsPerFrame = 2;
stereoStreamFormat.mBitsPerChannel = 32;
Furthermore I use a separate buffer for each channel and a modified AudioBufferList struct :
struct AudioBufferListStereo
{
UInt32 mNumberBuffers;
AudioBuffer mBuffers[2]; //<-- this array has been set hardcoded to 2 channels because the standard iOS version did not work as expected.
#if defined(__cplusplus) && CA_STRICT
public:
AudioBufferListStereo() {}
private:
// Copying and assigning a variable length struct is problematic so turn their use into a
// compile time error for eacy spotting.
AudioBufferListStereo(const AudioBufferListStereo&);
AudioBufferListStereo& operator=(const AudioBufferListStereo&);
#endif
};
typedef struct AudioBufferListStereo AudioBufferListStereo;
The complete source is quite long, so I'll post it on GitHub if anyone requires it.

How to prevent AVAudioRecorder taking 3% CPU when "sleeping"

I have an AudioPlayer implemented in an app. As soon as I call [AVAudioRecorder prepareToRecord]; the CPU usage goes up to 5%. Fine, because something useful is happening. But after [AVAudioRecorder stop]; is called, CPU drops to 3% and stays there.
This is the code where I implement the recorder:
- (void)prepareRecorder
{
outputFileString = [NSString stringWithFormat:#"%#/%#", [self applicationDocumentsDirectory], #"audio.m4a"];
NSArray *pathComponents = [NSArray arrayWithObjects:[self applicationDocumentsDirectory], #"audio.m4a", nil];
outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setActive:YES error:nil];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:nil];
recorder.delegate = self;
recorder.meteringEnabled = YES;
[recorder prepareToRecord];
}
I don't know if it's possible to release/dealloc an object in ARC, but I tried
recorder = Nil;
How can I solve this? Or shouldn't I bother about the 3%?
If You say it stays 3% no matter how much You start and stop AudioRecording, then there is no leak, but possibly SoundRecording framework has cached some private sound recording instances (something that is located and happens behind the Apple frameworks hood) and one of the reasons would be - next time to start recording faster than on first time. These cached instances would be deallocated in case sound player is not used anymore AND there would be a need of extra memory.
I think similarly is with AVPlayer and MFMessageComposeViewController (and probably many more frameworks/api's, which are heavy-to-run) - because - for both of these mentioned frameworks - on the first time I try to show SMS form, or play a video - there is a noticable delay. After that - there is no delay. So on first time it probably loads in cache something important, which can be auto-purged in case there is little memory available or application is closed.
So I think it is perfectly fine not to bother about this.

AVAudioRecorder throwing exception on prepareToRecord

I'm running sample code on audio recording (source code downloadable at the end of the article). The code is like this
NSArray *pathComponents = [NSArray arrayWithObjects:
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject],
#"MyAudioMemo.m4a",
nil];
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
// Setup audio session
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
// Define the recorder setting
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt: 2] forKey:AVNumberOfChannelsKey];
// Initiate and prepare the recorder
recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:nil];
recorder.delegate = self;
recorder.meteringEnabled = YES;
[recorder prepareToRecord];
However it throws an exception at prepareToRecord while running on the simulator:
It does record and it'll run fine if I just turn off the break point at exception. But this is annoying. What's wrong?
I'm getting this exception, too. Searching for solution. Important to note that you can only see these exceptions if you enable exception breakpoints. The app perform properly, but this is concerning. I'll keep investigating.
EDIT:
well, the answer is here: AVAudioPlayer throws breakpoint in debug mode
i figured that much myself, but still feel uncomfortable about having these exceptions. using different audio format while recording makes the exceptions to go away. however, i want to use the lossless format, which does cause these exceptions. hope this helps.

Resources