iDevGames Forums
My AUHAL AudioUnit Objective-C wrapper does not work with stereo line in - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Graphics & Audio Programming (/forum-9.html)
+--- Thread: My AUHAL AudioUnit Objective-C wrapper does not work with stereo line in (/thread-9054.html)



My AUHAL AudioUnit Objective-C wrapper does not work with stereo line in - Ender - Jun 13, 2011 03:09 PM

Hello all,

and thanks, in advance, for any help I will receive.

I am developing a wrapper for AUHAL AudioUnits. It is written in Objective-C.

Actually I had some problems to learn Core Audio, so there could be more errors then I can see. Documentation is not enough to really understand Core Audio and all examples are written in C++ (that does not help me) and use Core Audio SDK Public Utilities to achieve many tasks. But Core Audio SDK Public Utilities are not documented and poorly commented, therefore, to fully understand them is a really big job.

Though, I have been able to code this wrapper, that works with my LED Cinema Display microphone (apparently, a monophonic microphone, right?).

The problem is that it does not work with the Mac Pro jack line in (apparently, a stereophonic source, right?).

At this point, I don't know where to look for the cause of this problem.

What I can guess, is that the cause should not be a device/unit setup problem, since I check all the errors eventually produced by Core Audio calls and the wrapper should not initialize (neither starts rendering) if anything goes wrong.

But, when using line in jack, left and right buffer amplitudes are all zero. The buffer where I render is the _renderBuf member variable of the class.

This is the code (I pasted both header and source files here):

Code:
// Header file
#import <Foundation/Foundation.h>
#import <AudioUnit/AudioUnit.h>


@interface SpectreAUHALFromInputWrapper : NSObject {
@private
    AudioUnit _au;
    
    AudioStreamBasicDescription _in_format;
    AudioStreamBasicDescription _out_format;
    
    AudioBufferList * _renderBuf;
    
    UInt32 _framesNum;
}

@property (readonly) AudioUnit audioUnit;
@property (readonly) AudioStreamBasicDescription inputFormat;
@property (readonly) AudioStreamBasicDescription outputFormat;

- (OSStatus)start;
- (OSStatus)stop;

- (OSStatus)renderWithActionFlags:(AudioUnitRenderActionFlags *)flags
                        timeStamp:(const AudioTimeStamp *)ts
                              bus:(UInt32)bus
                     framesNumber:(UInt32)framesNum;

- (OSStatus)readBuffer:(AudioBufferList **)bufList;

- (OSStatus)numberOfFrames:(UInt32 *)num;

@end


// Source file
#import "SpectreAUHALFromInputWrapper.h"

#import "SpectreMacros.h"


@interface SpectreAUHALFromInputWrapper (SpectreAUHALFromInputWrapperPrivate)
- (OSStatus)createAUHAL;
- (OSStatus)enableIO;
- (OSStatus)setDeviceTo:(AudioDeviceID)devID;
- (OSStatus)setAudioFormat;
- (OSStatus)setupCallback;
- (OSStatus)getInputFormat:(AudioStreamBasicDescription *)desc;
- (OSStatus)getOutputFormat:(AudioStreamBasicDescription *)desc;
- (BOOL)isRunning;
- (OSStatus)setupRenderBuffer;
@end


OSStatus SpectreInputProc(void                            * inRefCon,
                          AudioUnitRenderActionFlags    * ioActionFlags,
                          const AudioTimeStamp            * inTimeStamp,
                          UInt32                          inBusNumber,
                          UInt32                          inNumberFrames,
                          AudioBufferList                * ioData)
{
    OSStatus err = noErr;
    SpectreAUHALFromInputWrapper * wrapper = (SpectreAUHALFromInputWrapper *)inRefCon;
    
    err = [wrapper renderWithActionFlags:ioActionFlags
                               timeStamp:inTimeStamp
                                     bus:inBusNumber
                            framesNumber:inNumberFrames];
    
    return err;
}


@implementation SpectreAUHALFromInputWrapper

@synthesize audioUnit = _au;
@synthesize inputFormat = _in_format;
@synthesize outputFormat = _out_format;

- (id)init {
    self = [super init];
    if (self) {
        OSStatus err = noErr;
        
        err = [self createAUHAL];
        spectreTestError2(err);
    }
    return self;
}

- (void)dealloc {
    [super dealloc];
}

- (OSStatus)start {
    OSStatus err = noErr;
    
    if (![self isRunning]) {
        err = AudioOutputUnitStart(_au);
        spectreTestError(err);
    }
    
    return err;
}

- (OSStatus)stop {
    OSStatus err = noErr;
    
    if ([self isRunning]) {
        err = AudioOutputUnitStop(_au);
        spectreTestError(err);
    }
    
    return err;
}

- (BOOL)isRunning {
    OSStatus err = noErr;
    UInt32 isRunning = 0;
    UInt32 size = 0;
    
    size = sizeof(isRunning);
    
    if (_au) {
        err = AudioUnitGetProperty(_au,
                                   kAudioOutputUnitProperty_IsRunning,
                                   kAudioUnitScope_Global,
                                   0,
                                   &isRunning,
                                   &size);
    }
    
    return isRunning;
}

- (OSStatus)createAUHAL {
    OSStatus err = noErr;
    AudioComponent comp;
    AudioComponentDescription desc;
    
    desc.componentType = kAudioUnitType_Output;
    desc.componentSubType = kAudioUnitSubType_HALOutput;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
    
    comp = AudioComponentFindNext(NULL, &desc);
    if (!comp) {
        NSLog(@"No AudioComponent found for the AUHAL unit description.");
        return -1;
    }
    
    err = AudioComponentInstanceNew(comp, &_au);
    spectreTestError(err);
    
    err = AudioUnitInitialize(_au);
    spectreTestError(err);
    
    err = [self enableIO];
    spectreTestError(err);
    
    err = [self setDeviceTo:kAudioDeviceUnknown];
    spectreTestError(err);
    
    err = [self setupCallback];
    spectreTestError(err);
    
    err = [self getInputFormat:&_in_format];
    spectreTestError(err);
    
    err = [self getOutputFormat:&_out_format];
    spectreTestError(err);
    
    err = [self setAudioFormat];
    spectreTestError(err);
    
    err = [self setupRenderBuffer];
    spectreTestError(err);
    
    err = AudioUnitInitialize(_au);
    
    return err;
}

- (OSStatus)enableIO {
    OSStatus err = noErr;
    UInt32 enableIO = 1;
    
    err = AudioUnitSetProperty(_au,
                               kAudioOutputUnitProperty_EnableIO,
                               kAudioUnitScope_Input,
                               kSpectreInputBus, // input element
                               &enableIO,
                               sizeof(enableIO));
    spectreTestError(err);
    
    enableIO = 0;
    err = AudioUnitSetProperty(_au,
                               kAudioOutputUnitProperty_EnableIO,
                               kAudioUnitScope_Output,
                               kSpectreOutputBus, //output element
                               &enableIO,
                               sizeof(enableIO));
    return err;
}

- (OSStatus)setDeviceTo:(AudioDeviceID)devID {
    OSStatus err = noErr;
    UInt32 size = sizeof(AudioDeviceID);
    
    if(devID == kAudioDeviceUnknown) {
        err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
                                       &size,
                                       &devID);
        spectreTestError(err);
    }
    
    /* ================================ DEBUG =============================== */
    err = AudioDeviceGetPropertyInfo(devID, 0, YES, kAudioDevicePropertyDeviceName, &size, NULL);
    spectreTestError(err);
    
    char * name = (char *)malloc(size);
    
    err = AudioDeviceGetProperty(devID, 0, YES, kAudioDevicePropertyDeviceName, &size, name);
    spectreTestError(err);
    
    NSLog(@"Input device name: %s", name);
    /* ================================ DEBUG =============================== */
    
    err = AudioUnitSetProperty(_au,
                               kAudioOutputUnitProperty_CurrentDevice,
                               kAudioUnitScope_Global,
                               0,
                               &devID,
                               sizeof(devID));
    
    return err;
}

- (OSStatus)setupCallback {
    OSStatus err = noErr;
    AURenderCallbackStruct cbs;
    
    cbs.inputProc = SpectreInputProc;
    cbs.inputProcRefCon = self;
    
    // Setup the input callback.
    err = AudioUnitSetProperty(_au,
                               kAudioOutputUnitProperty_SetInputCallback,
                               kAudioUnitScope_Global,
                               0,
                               &cbs,
                               sizeof(cbs));
    
    return err;
}

- (OSStatus)getInputFormat:(AudioStreamBasicDescription *)desc {
    OSStatus err = noErr;
    UInt32 size = sizeof(AudioStreamBasicDescription);
    
    err = AudioUnitGetProperty(_au,
                               kAudioUnitProperty_StreamFormat,
                               kAudioUnitScope_Input,
                               kSpectreInputBus,
                               desc,
                               &size);
    
    return err;
}

- (OSStatus)getOutputFormat:(AudioStreamBasicDescription *)desc {
    OSStatus err = noErr;
    UInt32 size = sizeof(AudioStreamBasicDescription);
    
    err = AudioUnitGetProperty(_au,
                               kAudioUnitProperty_StreamFormat,
                               kAudioUnitScope_Output,
                               kSpectreInputBus,
                               desc,
                               &size);
    
    return err;
}

- (OSStatus)renderWithActionFlags:(AudioUnitRenderActionFlags *)flags
                        timeStamp:(const AudioTimeStamp *)ts
                              bus:(UInt32)bus
                     framesNumber:(UInt32)framesNum
{
    //NSLog(@"Render input audio with time stamp: %f; bus number: %u; number of frames: %u", ts->mSampleTime, bus, framesNum);
    OSStatus err = noErr;
    
    @synchronized(self) {
        err = AudioUnitRender(_au,
                              flags,
                              ts,
                              bus,
                              framesNum,
                              _renderBuf);
        
        _framesNum = framesNum;
    }
    
    return err;
}

- (OSStatus)readBuffer:(AudioBufferList **)bufList {
//    NSLog(@"\tnumber of buffers = %u", _renderBuf->mNumberBuffers);
//    for (UInt32 i = 0; i < _renderBuf->mNumberBuffers; i++) {
//        NSLog(@"\tbuffer %u = (number of channels = %u; bytes number = %u)", i, _renderBuf->mBuffers[i].mNumberChannels, _renderBuf->mBuffers[i].mDataByteSize);
//        
//        Float32 * data = (Float32 *)_renderBuf->mBuffers[i].mData;
//        NSMutableString * seq = [NSMutableString string];
//        //for (UInt32 f = 0; f < _framesNum; f++) {
//            [seq appendFormat:@"%f ", data[0]];
//        //}
//        NSLog(@"\t\tdata = %@", seq);
//    }
    
    OSStatus err = noErr;
    
    @synchronized(self) {
        if (_renderBuf) {
            if (*bufList)
                free(bufList);
            
            size_t bufListSize;
            
            bufListSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * _renderBuf->mNumberBuffers);
            
            *bufList = (AudioBufferList *)malloc(bufListSize);
            
            (*bufList)->mNumberBuffers = _renderBuf->mNumberBuffers;
            
            for (UInt32 i = 0; i < _renderBuf->mNumberBuffers; i++) {
                (*bufList)->mBuffers[i].mNumberChannels = _renderBuf->mBuffers[i].mNumberChannels;
                (*bufList)->mBuffers[i].mDataByteSize = _renderBuf->mBuffers[i].mDataByteSize;
                
                (*bufList)->mBuffers[i].mData = malloc((*bufList)->mBuffers[i].mDataByteSize);
                
                memcpy((*bufList)->mBuffers[i].mData, _renderBuf->mBuffers[i].mData, (*bufList)->mBuffers[i].mDataByteSize);
            }
        }
    }
    
    return err;
}

- (OSStatus)numberOfFrames:(UInt32 *)num {
    OSStatus err = noErr;
    
    @synchronized(self) {
        *num = _framesNum;
    }
    
    return err;
}

- (OSStatus)setAudioFormat {
    OSStatus err = noErr;
    
    _out_format = _in_format;
    
    err = AudioUnitSetProperty(_au,
                               kAudioUnitProperty_StreamFormat,
                               kAudioUnitScope_Output,
                               kSpectreInputBus,
                               &_out_format,
                               sizeof(_out_format));
    
    return err;
}

- (OSStatus)setupRenderBuffer {
    OSStatus err = noErr;
    UInt32 size;
    UInt32 bufFrameNum;
    UInt32 bufSize;
    
    size = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * _in_format.mChannelsPerFrame);
    
    _renderBuf = (AudioBufferList *)malloc(size);
    
    _renderBuf->mNumberBuffers = _in_format.mChannelsPerFrame;
    
    size = sizeof(UInt32);
    err = AudioUnitGetProperty(_au,
                               kAudioDevicePropertyBufferFrameSize,
                               kAudioUnitScope_Global,
                               0,
                               &bufFrameNum,
                               &size);
    spectreTestError(err);
    
    bufSize = bufFrameNum * sizeof(Float32);
    
    for(UInt32 i = 0; i < _renderBuf->mNumberBuffers; i++) {
        _renderBuf->mBuffers[i].mNumberChannels = 1;
        _renderBuf->mBuffers[i].mDataByteSize = bufSize;
        _renderBuf->mBuffers[i].mData = malloc(bufSize);
    }
    
    return err;
}

@end