7

I'm coding a music recording app using Audio Units and I'm having some issues getting my resulting M4A file to play anything other than a not so awesome buzzing noise. I've used these SO sources as references, and I've tried everything to troubleshoot.

I've got an AUGraph with 2 nodes: a multi channel mixer and a remote I/O. I've got two input callbacks on my mixer: one that pulls input from the mic, and one that pulls from an audio file. The mixer output is connected to the input element of the output scope on the I/O unit. This enables simultaneous I/O.

To capture the output I've added a callback and two methods:

The callback

static OSStatus recordAndSaveCallback (void *                inRefCon,
                                       AudioUnitRenderActionFlags * ioActionFlags,
                                       const AudioTimeStamp *       inTimeStamp,
                                       UInt32                       inBusNumber,
                                       UInt32                       inNumberFrames,
                                       AudioBufferList *            ioData) 
{
    Mixer* THIS = (__bridge Mixer*)inRefCon;
    AudioBufferList bufferList;

    OSStatus status;
    status = AudioUnitRender(THIS.ioUnit,    
                             ioActionFlags,
                             inTimeStamp,
                             0,
                             inNumberFrames,
                             &bufferList);

    SInt16 samples[inNumberFrames]; // A large enough size to not have to worry about buffer overrun
    memset (&samples, 0, sizeof (samples));

    bufferList.mNumberBuffers = 1;
    bufferList.mBuffers[0].mData = samples;
    bufferList.mBuffers[0].mNumberChannels = 1;
    bufferList.mBuffers[0].mDataByteSize = inNumberFrames*sizeof(SInt16);

    OSStatus result;
    if (*ioActionFlags == kAudioUnitRenderAction_PostRender) {
        result =  ExtAudioFileWriteAsync(THIS.extAudioFileRef, inNumberFrames, &bufferList);
        if(result) printf("ExtAudioFileWriteAsync %ld \n", result);}
    return noErr; 
}

Recording Method:

- (void)recordFile
{    
    OSStatus result;

    NSArray  *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *recordFile = [documentsDirectory stringByAppendingPathComponent: @"audio.m4a"];

    CFURLRef destinationURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 
                                                            (__bridge   CFStringRef)recordFile, 
                                                            kCFURLPOSIXPathStyle, 
                                                            false);    

    AudioStreamBasicDescription destinationFormat;
    memset(&destinationFormat, 0, sizeof(destinationFormat));
    destinationFormat.mChannelsPerFrame = 1;
    destinationFormat.mFormatID = kAudioFormatMPEG4AAC;
    UInt32 size = sizeof(destinationFormat);
    result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat);        
    if(result) printf("AudioFormatGetProperty %ld \n", result);    


    result = ExtAudioFileCreateWithURL(destinationURL, 
                                       kAudioFileM4AType, 
                                       &destinationFormat, 
                                       NULL, 
                                       kAudioFileFlags_EraseFile, 
                                       &extAudioFileRef);
    if(result) printf("ExtAudioFileCreateWithURL %ld \n", result);


    AudioStreamBasicDescription clientFormat;
    memset(&clientFormat, 0, sizeof(clientFormat));


    UInt32 clientsize = sizeof(clientFormat);   
    result = AudioUnitGetProperty(ioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &clientFormat, &clientsize);
    if(result) printf("AudioUnitGetProperty %ld \n", result);

    UInt32 codec = kAppleHardwareAudioCodecManufacturer;

    result = ExtAudioFileSetProperty(extAudioFileRef, 
                                     kExtAudioFileProperty_CodecManufacturer, 
                                     sizeof(codec), 
                                     &codec);

    if(result) printf("ExtAudioFileSetProperty %ld \n", result);


    result = ExtAudioFileSetProperty(extAudioFileRef,kExtAudioFileProperty_ClientDataFormat,sizeof(clientFormat), &clientFormat);
    if(result) printf("ExtAudioFileSetProperty %ld \n", result);


    result =  ExtAudioFileWriteAsync(extAudioFileRef, 0, NULL);
    if (result) {[self printErrorMessage: @"ExtAudioFileWriteAsync error" withStatus: result];}

   result = AudioUnitAddRenderNotify(ioUnit, recordAndSaveCallback, (__bridge void*)self);
    if (result) {[self printErrorMessage: @"AudioUnitAddRenderNotify" withStatus: result];}     
}

Saving Method:

- (void) saveFile {
    OSStatus status = ExtAudioFileDispose(extAudioFileRef);
    NSLog(@"OSStatus(ExtAudioFileDispose): %ld\n", status);

}

This is what I see in my console:

Stopping audio processing graph
OSStatus(ExtAudioFileDispose): 0
ExtAudioFileWriteAsync -50 
ExtAudioFileWriteAsync -50 
ExtAudioFileWriteAsync -50 

It seems to me that my code is very similar to that of people who have gotten this to work, but clearly I've made a crucial error. I'm sure there must be others struggling with this.

Does anyone have any insight?

Thanks.

Community
  • 1
  • 1
Orpheus Mercury
  • 1,617
  • 2
  • 15
  • 30
  • It looks like your `-saveFile` method is being called before `recordAndSaveCallback` is finished. Are you stopping the AUGraph before calling saveFile? – sbooth May 12 '12 at 17:45
  • as an experiment to rule out any issues with your graph it might be a good idea to try and record using a simpler format such as wav. ASBD can be hard to get right, but if you can determine that the ASBD is the problem(my opinion) then at least you can just focus on experimenting with the file format. – dubbeat May 19 '12 at 18:31
  • running macerror -50 from the command line gives this: Mac OS error -50 (paramErr): error in user parameter list – morgancodes Jul 05 '12 at 16:18

1 Answers1

1

I know the question has been asked a long time ago and you probably found out the error by now, I'm just answering for other that might have the same issue.

I might be wrong but I think the problem is coming from the fact that you are doing a in-scope variable declaration for the buffer.

I would recommend that you change

SInt16 samples[inNumberFrames];

into

SInt16* samples = malloc(inNumberFrames * sizeof(SInt16));

Since the recordAndSaveCallback is meant to fill the buffer list, if you do an in-scope declaration, datas will be destroyed as soon as the scope is ended.

TheSquad
  • 7,385
  • 8
  • 40
  • 79