3908

Recording audio with Audio Unit with files segmented in X number of seconds each

Question:

I've been at this for a few days now. I'm not very familiar with the Audio Unit layer of the framework. Could someone point me to some full example on how I can let user record and than write the file on the fly with x number of interval. For example, user press record, every 10 seconds, I want to write to a file, on the 11th second, it'll write to the next file and in the 21th second, it's the same thing. So when I record 25 seconds word of audio, it'll produce 3 different files.

I've tried this with AVCapture but it produce clicks and pops in the middle. I've read up on it, it is due to the milliseconds between the read and write operations. I've tried Audio Queue Services but knowing the app I'm working on, I'll need full control over the audio layer; so I've decided to go with Audio Unit.

Answer1:

I think I'm getting closer...still pretty lost. I ended up using The Amazing Audio Engine (TAAE). I'm now looking at AEAudioReceiver, my callback code looks like this. I think logically is right but I don' think it's implemented correctly.

The task at hand: Record ~5-second segments in AAC format.

The attempt: Use the AEAudioReciever callback and store the AudioBufferList in the circular buffer. Track the number of seconds of audio have received in the recorder class; once it pass the 5 seconds mark (it can be a little bit over but not 6 seconds). Call Obj-c method to write the file using the AEAudioFileWriter

The outcome: Didn't work, the recordings sounded very slow and lots of noise constantly; I can hear some of the record audio; so I know some data is there but it's like I'm losing a lot of data. I'm not even sure how to debug this (I'll continue to try but pretty lost at the moment).

Another item is converting to AAC, do I write the file first in PCM format than convert to AAC or is it possible to convert just the audio segment to AAC?

Thanks ahead for helping!

----- Circular buffer init -----

//trying to get 5 seconds audio, how do I know what the length is if I don't know the frame size yet? and is that even the right question to ask? TPCircularBufferInit(&_buffer, 1024 * 256);

----- AEAudioReceiver callback ------

static void receiverCallback(__unsafe_unretained MyAudioRecorder *THIS, __unsafe_unretained AEAudioController *audioController, void *source, const AudioTimeStamp *time, UInt32 frames, AudioBufferList *audio) { //store the audio into the buffer TPCircularBufferCopyAudioBufferList(&THIS->_buffer, audio, time, kTPCircularBufferCopyAll, NULL); //increase the time interval to track by THIS THIS.numberOfSecondInCurrentRecording += AEConvertFramesToSeconds(THIS.audioController, frames); //if number of seconds passed an interval of 5 seconds, than write the last 5 seconds of the buffer to a file if (THIS.numberOfSecondInCurrentRecording > 5 * THIS->_currentSegment + 1) { NSLog(@"Segment %d is full, writing file", THIS->_currentSegment); [THIS writeBufferToFile]; //segment tracking variables THIS->_numberOfReceiverLoop = 0; THIS.lastTimeStamp = nil; THIS->_currentSegment += 1; } else { THIS->_numberOfReceiverLoop += 1; } // Do something with 'audio' if (!THIS.lastTimeStamp) { THIS.lastTimeStamp = (AudioTimeStamp *)time; } }

---- Writing to file (method inside of the MyAudioRecorderClass) ----

- (void)writeBufferToFileHandler { NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; NSString *filePath = [documentsFolder stringByAppendingPathComponent:[NSString stringWithFormat:@"Segment_%d.aiff", _currentSegment]]; NSError *error = nil; //setup audio writer, should the buffer be converted to aac first or save the file than convert; and how the heck do you do that? AEAudioFileWriter *writeFile = [[AEAudioFileWriter alloc] initWithAudioDescription:_audioController.inputAudioDescription]; [writeFile beginWritingToFileAtPath:filePath fileType:kAudioFileAIFFType error:&error]; if (error) { NSLog(@"Error in init. the file: %@", error); return; } int i = 1; //loop to write all the AudioBufferLists that is in the Circular Buffer; retrieve the ones based off of the _lastTimeStamp; but I had it in NULL too and worked the same way. while (1) { //NSLog(@"Processing buffer file list for segment [%d] and buffer index [%d]", _currentSegment, i); i += 1; // Discard any buffers with an incompatible format, in the event of a format change AudioBufferList *nextBuffer = TPCircularBufferNextBufferList(&_buffer, _lastTimeStamp); Float32 *frame = (Float32*) &nextBuffer->mBuffers[0].mData; //if buffer runs out, than we are done writing it and exit loop to close the file if ( !nextBuffer ) { NSLog(@"Ran out of frames, there were [%d] AudioBufferList", i - 1); break; } //Adding audio using AudioFileWriter, is the length correct? OSStatus status = AEAudioFileWriterAddAudio(writeFile, nextBuffer, sizeof(nextBuffer->mBuffers[0].mDataByteSize)); if (status) { NSLog(@"Writing Error? %d", status); } //consume/clear the buffer TPCircularBufferConsumeNextBufferList(&_buffer); } //close the file and hope it worked [writeFile finishWriting]; }

----- Audio Controller AudioStreamBasicDescription ------

//interleaved16BitStereoAudioDescription AudioStreamBasicDescription audioDescription; memset(&audioDescription, 0, sizeof(audioDescription)); audioDescription.mFormatID = kAudioFormatLinearPCM; audioDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian; audioDescription.mChannelsPerFrame = 2; audioDescription.mBytesPerPacket = sizeof(SInt16)*audioDescription.mChannelsPerFrame; audioDescription.mFramesPerPacket = 1; audioDescription.mBytesPerFrame = sizeof(SInt16)*audioDescription.mChannelsPerFrame; audioDescription.mBitsPerChannel = 8 * sizeof(SInt16); audioDescription.mSampleRate = 44100.0;

Recommend

  • Prevent custom actions from running during patch
  • Take photo without camera application [duplicate]
  • Design matrix for MLM from library(lme4) with fixed and random effects
  • Convert JSON response to DataTable
  • Implementing a memoization function in Haskell
  • Program does not contain a static 'main' method suitable for entry point
  • Is it possible to set two fields as indexes on an entity in ndb?
  • How to prevent LDM/STM instructions expansion in ARM Compiler 5 armcc inline assembler?
  • What is the ClojureScript analogue of delete from JavaScript?
  • Use double quote then curly brace in powershell -Command
  • Replace any string in columns with 1
  • Unknown C# type
  • How to lookup value with multiple criteria in excel 2007 and newer
  • CSS - how to trim text output?
  • Reloading table causes flickering
  • f:param to composite components
  • Bootstrap collapse within a foreach loop
  • Spring Integration Bridge with poller not working as expected for JMS
  • Is looping through all style sheets and classes a good idea in JavaScript?
  • runtime error when linking ffmpeg libraries in qt creator
  • Why cout is producing no output on Code Blocks?
  • Can you pass an array from javascript to asp.net mvc controller action without using a form?
  • How to pass solution folder as parameter in command line arguments (for debug)?
  • How can we prepend rows to a react native list-view?
  • Is it possible to run clang with llc flags
  • VSCode change debug shell to bash on windows
  • Passing “get” parameters doesn't work, parameter not visible in the link
  • What's the purpose of QString?
  • Visual Studio 2010 debugger build correctly - compiler pdb and linker pdb not in synch?
  • Jackson Parser: ignore deserializing for type mismatch
  • Very simple C++ DLL that can be called from .net
  • Can I check if a recipient has an automatic reply before I send an email?
  • Asynchronous UI Testing in Xcode With Swift
  • Getting last autonumber in access
  • jqPlot EnhancedLegendRenderer plugin does not toggle series for Pie charts
  • Comma separated Values
  • -fvisibility=hidden not passed by compiler for Debug builds
  • How to CLICK on IE download dialog box i.e.(Open, Save, Save As…)
  • Checking variable from a different class in C#
  • How to load view controller without button in storyboard?