Re-recording with AVAudioRecorder - iphone

I can record with the setup below - it works first time, but then when I try again the file is always 8192 bytes, i.e. not a correct recording.
-(void) startRecording
{
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 11025.0f], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityLow], AVEncoderAudioQualityKey,
nil];
NSString *filenameBasedOnTime = [[NSDate date] description];
if (_recordedFileURL) _recordedFileURL = nil;
_recordedFileURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingString:filenameBasedOnTime]];
NSError* error;
if (_audioRecorder) _audioRecorder = nil;
_audioRecorder = [[AVAudioRecorder alloc] initWithURL:_recordedFileURL settings:settings error:&error];
_audioRecorder.delegate = self;
if (error)
{
return;
}
[_audioRecorder prepareToRecord];
_audioRecorder.meteringEnabled = YES;
[_audioRecorder record];
}
-(void) stopRecord
{
[_audioRecorder stop];
}
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
[self saveRecording];
}
-(void) saveRecording
{
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:_recordedFileURL.relativeString]];
NSLog(#"Recording data size = %i", [data length]);
}
It is called inside a UIPopoverController if that helps...

Since found out that the problem was I was missing
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:NULL];
From here iPhone SDK: AVAudioRecorder will not record after calling [AVPlayer play]

Related

Memory issue for reading large size of Audio file while converting audio

I have sampleaudio.caf(300 MB) audio file. I convert the audio .caf to .wav format. It says warning "Received memory warning. Level=1" & "Received memory warning. Level=2" after that the app is crashed. But it works in ipad. How to read small amount of data using buffers while convertion is happening.
This is my Code:
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"sampleaudio" ofType:#"caf"];
NSURL *assetURL = [NSURL fileURLWithPath:soundFilePath];
AVURLAsset *songAsset = [AVURLAsset URLAssetWithURL:assetURL options:nil];
NSError *assetError = nil;
AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:songAsset
error:&assetError]
;
if (assetError) {
NSLog (#"error: %#", assetError);
return;
}
AVAssetReaderOutput *assetReaderOutput = [AVAssetReaderAudioMixOutput
assetReaderAudioMixOutputWithAudioTracks:songAsset.tracks
audioSettings: nil];
if (! [assetReader canAddOutput: assetReaderOutput]) {
NSLog (#"can't add reader output... die!");
return;
}
[assetReader addOutput: assetReaderOutput];
NSString *strWavFileName = [NSString stringWithFormat:#"%#.wav",[[soundFilePath lastPathComponent] stringByDeletingPathExtension]];
NSString *wavFilePath = [delegate.strCassettePathSide stringByAppendingPathComponent:strWavFileName];
if ([[NSFileManager defaultManager] fileExistsAtPath:wavFilePath])
{
[[NSFileManager defaultManager] removeItemAtPath:wavFilePath error:nil];
}
NSURL *exportURL = [NSURL fileURLWithPath:wavFilePath];
AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:exportURL
fileType:AVFileTypeWAVE
error:&assetError];
if (assetError)
{
NSLog (#"error: %#", assetError);
return;
}
AppDelegate *appDelegate =[[UIApplication sharedApplication]delegate];
int nSampleRate=[[appDelegate.dictWAVQuality valueForKey:#"samplerate"] integerValue];
AudioChannelLayout channelLayout;
memset(&channelLayout, 0, sizeof(AudioChannelLayout));
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
[NSNumber numberWithFloat:nSampleRate], AVSampleRateKey,
[NSNumber numberWithInt:2], AVNumberOfChannelsKey,
[NSData dataWithBytes:&channelLayout length:sizeof(AudioChannelLayout)], AVChannelLayoutKey,
[NSNumber numberWithInt:16], AVLinearPCMBitDepthKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
[NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey,
[NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
nil];
AVAssetWriterInput *assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
outputSettings:outputSettings];
if ([assetWriter canAddInput:assetWriterInput])
{
[assetWriter addInput:assetWriterInput];
}
else
{
NSLog(#"can't add asset writer input... die!");
return;
}
assetWriterInput.expectsMediaDataInRealTime = NO;
[assetWriter startWriting];
[assetReader startReading];
AVAssetTrack *soundTrack = [songAsset.tracks objectAtIndex:0];
CMTime startTime = CMTimeMake (0, soundTrack.naturalTimeScale);
[assetWriter startSessionAtSourceTime: startTime];
__block UInt64 convertedByteCount = 0;
dispatch_queue_t mediaInputQueue = dispatch_queue_create("mediaInputQueue", NULL);
[assetWriterInput requestMediaDataWhenReadyOnQueue:mediaInputQueue
usingBlock: ^
{
while (assetWriterInput.readyForMoreMediaData)
{
CMSampleBufferRef nextBuffer = [assetReaderOutput copyNextSampleBuffer];
if (nextBuffer)
{
// append buffer
[assetWriterInput appendSampleBuffer: nextBuffer];
convertedByteCount += CMSampleBufferGetTotalSampleSize (nextBuffer);
}
else
{
[assetWriterInput markAsFinished];
// [assetWriter finishWriting];
[assetReader cancelReading];
break;
}
}
}];}
How to Rectify the memory issue.
CMSampleBufferRef nextBuffer = [assetReaderOutput copyNextSampleBuffer];
if (nextBuffer)
{
// append buffer
[assetWriterInput appendSampleBuffer: nextBuffer];
convertedByteCount += CMSampleBufferGetTotalSampleSize (nextBuffer);
////////////////////you need to add following line////////////////////
CMSampleBufferInvalidate(nextBuffer);
CFRelease(nextBuffer);
nextBuffer = NULL;
}
https://developer.apple.com/library/mac/documentation/AVFoundation/Reference/AVAssetReaderOutput_Class/Reference/Reference.html#//apple_ref/occ/instm/AVAssetReaderOutput/copyNextSampleBuffer
Ownership follows the “The Create Rule” in Memory Management
Programming Guide for Core Foundation.
You have to release CMSampleBufferRef obtained from -(CMSampleBufferRef)copyNextSampleBuffer or you will get a memory leak.

Trying to record and play audio at the same time, but it's quiet

Okay so I've tried to look this up so it isn't a duplicate but I might have missed something. Anyways, in my app I have a song that should start playing when I hit record. So when I hit record, the AVAudioRecorder starts recording and my already initialized AVAudioPlayer starts playing the song. Yet the song's volume becomes very quiet. I know it isn't the song because if I simply play the song without attempting to record at the same time, it plays at full volume. Any help guys? Thanks.
How I'm Initializing:
NSDictionary *recordSettings = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:AVAudioQualityMin],
AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16],
AVEncoderBitRateKey,
[NSNumber numberWithInt: 2],
AVNumberOfChannelsKey,
[NSNumber numberWithFloat:44010.0],
AVSampleRateKey,
nil];
NSError *error = nil;
audioRecorder = [[AVAudioRecorder alloc]
initWithURL:soundFileURL
settings:recordSettings
error:&error];
if (error)
{
NSLog(#"error: %#", [error localizedDescription]);
} else {
[audioRecorder prepareToRecord];
}
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
pathForResource:"Rang De"
ofType:#"mp3"]];
audioPlayerForPreloadedMusic = [[AVAudioPlayer alloc]
initWithContentsOfURL:url
error:&error];
if (error)
{
NSLog(#"Error in audioPlayer: %#",
[error localizedDescription]);
} else
{
audioPlayerForPreloadedMusic.delegate = self;
[audioPlayerForPreloadedMusic prepareToPlay];
}
How I'm Playing:
-(void) recordAudio
{
if (!audioRecorder.recording)
{
playButton.enabled = NO;
stopButton.enabled = YES;
[audioRecorder record];
if(!audioPlayerForPreloadedMusic.playing)
[audioPlayerForPreloadedMusic play];
else {
NSLog(#"music is playing, so won't play");
}
}
}
You are playing the song out the earpiece when you start recording, instead of out of the speaker.
There is an audio session override to prevent this automatic switch. See using: kAudioSessionProperty_OverrideAudioRoute and kAudioSessionOverrideAudioRoute_Speaker in Apple's iOS API documentation.

Microphone code doesn't run on iPhone device

I am trying to run the following code on my device with no success. Although the code works perfectly on Simulator. I have been following this tutorial. It simply crash on device.
http://mobileorchard.com/tutorial-detecting-when-a-user-blows-into-the-mic/
Code is:
#interface MicBlowViewController : UIViewController {
AVAudioRecorder *recorder;
NSTimer *levelTimer;
double lowPassResults;
}
- (void)levelTimerCallback:(NSTimer *)timer;
#end
.m file :
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL fileURLWithPath:#"/dev/null"];
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
NSError *error;
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if (recorder)
{
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval: 3
target: self
selector: #selector(levelTimerCallback:)
userInfo: nil
repeats: YES];
}
else
NSLog(#"%#", [error description]);
}
- (void)levelTimerCallback:(NSTimer *)timer {
[recorder updateMeters];
const double ALPHA = 0.05;
double peakPowerForChannel = pow(10, (0.05 * [recorder peakPowerForChannel:0]));
lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;
if (lowPassResults < 0.95)
NSLog(#"Mic blow detected");
}
Add those two lines of code after [recorder prepareToRecord ]
[recorder prepareToRecord];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];

Reduce Noise While Recording users Voice in iphone Application?

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 */
}

Why is AVAudioRecorder prepareToRecord Failing?

I am trying to setup a basic controller that will record user audio input(voice). However, the AVAudioRecorder's prepareToRecord method is failing and I can't figure out why. I have setup the audio session in my app delegate and I do not receive an errors when I instantiate the AVAudioRecorder instance:
// App delegate snippet
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
NSError* audioSessionError = nil;
[audioSession setCategory: AVAudioSessionCategoryPlayAndRecord
error: &audioSessionError];
if (audioSessionError) {
NSLog (#"Error setting audio category: %#", [audioSessionError localizedDescription]);
} else {
NSLog(#"No session errors for setting category");
}
[audioSession setActive:YES error:&audioSessionError];
if (audioSessionError) {
NSLog (#"Error activating audio session: %#", [audioSessionError localizedDescription]);
} else {
NSLog(#"no session errors for setActive");
}
// VIEW DID LOAD IN RECORDERCONTROLLER
- (void)viewDidLoad {
self.navigationItem.title = [NSString stringWithFormat:#"%#", [[MyAppDelegate loadApplicationPlist] valueForKey:#"recorderViewTitle"]];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:#selector(dismiss)];
[self alertIfNoAudioInput];
[self createAVAudioRecorder];
minutesSecondsFormatter = [[SimpleMinutesSecondsFormatter alloc] init];
currentTimeUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:0.1
target:self selector:#selector(updateAudioDisplay)
userInfo:NULL repeats:YES];
[super viewDidLoad];
}
// CREATE AVAUDIORECORDER
- (NSError *)createAVAudioRecorder {
NSError *recorderSetupError = nil;
[audioRecorder release];
audioRecorder = nil;
NSString *timestamp = [NSString stringWithFormat:#"%d", (long)[[NSDate date] timeIntervalSince1970]];
NSString *destinationString = [[MyAppDelegate getAppDocumentsDirectory] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.caf", timestamp]];
NSLog(#"destinationString: %#", destinationString);
NSURL *destinationUrl = [NSURL fileURLWithPath: destinationString];
audioRecorder = [[AVAudioRecorder alloc] initWithURL:destinationUrl
settings:[[AVRecordSettings sharedInstance] getSettings]
error:&recorderSetupError];
if (recorderSetupError) {
UIAlertView *cantRecordAlert =
[[UIAlertView alloc] initWithTitle:#"Can't record"
message:[recorderSetupError localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[cantRecordAlert show];
[cantRecordAlert release];
return recorderSetupError;
} else {
NSLog(#"no av setup error");
}
if ([audioRecorder prepareToRecord]) {
recordPauseButton.enabled = YES;
audioRecorder.delegate = self;
} else {
NSLog(#"couldn't prepare to record");
}
NSLog (#"recorderSetupError: %#", recorderSetupError);
return recorderSetupError;
}
The prepareToRecord also fails (silently, without an error) if the directory where you try to save the file doesn't exist. Use NSFileManager to check if the directory already exists.
It is failing because you did not initialize the AVAudioRecorder object using proper settings. Do this before initializing it:
NSDictionary *recordSettings =
[[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithFloat: 44100.0], AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatAppleLossless], AVFormatIDKey,
[NSNumber numberWithInt: 1], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityMax], AVEncoderAudioQualityKey,
nil];
then you can instantiate it using
audioRecorder = [[AVAudioRecorder alloc] initWithURL:destinationUrl
settings:recordSettings
error:&recorderSetupError];