Search code examples
macoscore-audiovoipaudiounitsample-rate

Setting sample rate on AUHAL


I'm using Audio Unit Framework to develop a VOIP app on mac os x. In my program, I set up an input AUHAL and use the default stream format (44.1kHz,32bit/channel) to capture the audio from mic. In this case, my program works fine.

Here is the Code:

//The default setting in my program
CheckError(AudioUnitGetProperty(m_audCapUnit,
                                        kAudioUnitProperty_StreamFormat,
                                        kAudioUnitScope_Output,     //the value is 0
                                        inputBus,           //the value is 1
                                        &m_audCapUnitOutputStreamFormat,
                                        &propertySize),
                   "Couldn't get OutputSample ASBD from input unit") ;  

    //the inOutputSampleRate is 44100.0
        m_audCapUnitOutputStreamFormat.mSampleRate = inOutputSampleRate ; 

CheckError(AudioUnitSetProperty(m_audCapUnit,
                                        kAudioUnitProperty_StreamFormat,
                                        kAudioUnitScope_Output,
                                        inputBus,
                                        &m_audCapUnitOutputStreamFormat,
                                        propertySize),
                   "Couldn't set OutputSample ASBD on input unit"); 

//

Since I'm developing a VOIP app, the default format (44.1kHz, 32bits/Channel) isn't appropriate for my program, so I want to change the sample rate to 8kHz. And I had written this code to change the format in my program:

//......
    inOutputFormat.mSampleRate = 8000.  ;
    inOutputFormat.mFormatID = kAudioFormatLinearPCM ;
    inOutputFormat.mChannelsPerFrame = 2 ;
    inOutputFormat.mBitsPerChannel  = 16 ;
    inOutputFormat.mBytesPerFrame = 2 ;
    inOutputFormat.mBytesPerPacket = 2 ;
    inOutputFormat.mFramesPerPacket = 1 ;
    inOutputFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical ;   
    inOutputFormat.mReserved = 0 ;



    CheckError(AudioUnitSetProperty(m_audCapUnit,
                                    kAudioUnitProperty_StreamFormat,
                                    kAudioUnitScope_Output,
                                    inputBus, 
                                    &inOutputFormat, 
                                    ui32PropSize),
               "Couldn't set AUHAL Unit Output Format") ;

//.......

In this case, the program works fine until my program calls the AudioUnitRender in the callback function; it fails to call the AudioUnitRender with an error code -10876 that means kAudioUnitErr_NoConnection in the documentation. The error code puzzled me so much, so I googled it but I couldn't find any useful information. Can someone tell me what the error actually means?

This is not the end, I changed the format again by this code:

//.....
    inOutputFormat.mSampleRate = 8000.  ;
    inOutputFormat.mFormatID = kAudioFormatLinearPCM ;
    inOutputFormat.mChannelsPerFrame = 2 ;
    inOutputFormat.mBitsPerChannel  = 32 ;
    inOutputFormat.mBytesPerFrame = 4 ;
    inOutputFormat.mBytesPerPacket = 4 ;
    inOutputFormat.mFramesPerPacket = 1 ;
    inOutputFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical ;   
    inOutputFormat.mReserved = 0 ;



    CheckError(AudioUnitSetProperty(m_audCapUnit,
                                    kAudioUnitProperty_StreamFormat,
                                    kAudioUnitScope_Output,
                                    inputBus, 
                                    &inOutputFormat, 
                                    ui32PropSize),
               "Couldn't set AUHAL Unit Output Format") ;
//........

In this case, the program failed to call the AudioUnitRender again and returned another error code -10863(kAudioUnitErr_CannotDoInCurrentContext). I googled it, but I found something useful. After reading the information there, I guess the sample rate or format that I set on the AUHAL may not be supported by the hardware.

So I wrote some code to check the available sample rates on the default input device:

//..........
    UInt32 propertySize = sizeof(AudioDeviceID) ;
    Boolean isWritable = false ;

    CheckError(AudioDeviceGetPropertyInfo(inDeviceID,       //the inDeviceID is the default input device
                                          0,
                                          true,
                                          kAudioDevicePropertyAvailableNominalSampleRates,
                                          &propertySize, 
                                          &isWritable), 
               "Get the Available Sample Rate Count Failed") ;

    m_valueCount = propertySize / sizeof(AudioValueRange) ;
    printf("Available %d Sample Rate\n",m_valueCount) ;

    CheckError(AudioDeviceGetProperty(inDeviceID,
                                      0,
                                      false,
                                      kAudioDevicePropertyAvailableNominalSampleRates, 
                                      &propertySize, 
                                      m_valueTabe), 
               "Get the Available Sample Rate Count Failed") ;


    for(UInt32 i = 0 ; i < m_valueCount ; ++i)
    {
        printf("Available Sample Rate value : %ld\n",(long)m_valueTabe[i].mMinimum) ;
    }
//..............

And then I found the available sample rates are 8000, 16000, 32000, 44100, 48000, 88200, and 96000.

The 8000 sample rate is what I set just before, but I get an error code by calling AudioUnitRender, I just want to say, why ?

I had google so much and also read many documentations, but I can't get the answer, can someone solve this problem what I encounter?

In other words; how do I change the sample rate or format on an input-only AUHAL?


Solution

  • Finally I fixed this problem yesterday by myself.

    Here is my solution:

    1. Firstly , I use AudioDeviceGetProperty to get the available format list on my defaut input device, then I found the available format list contain : 8khz, 16khz, 32khz, 44.1khz, 48khz, 88.2khz,96khz(I just list the sample rate field here ,but there are other field in the list).
    2. And then I select one of the available format which obtained in the first step. Take my program as an example , I select the format(8khz,32bits/Channel) and use AudioDeviceSetProperty to set it on the default device but not the AUHAL , this is the key that my program work fine after setinng the format on the AUHAL (OutputScope , inputBus).
    3. The last step , I use the AudioUnitSetProperty to set the format I wanted , the program work fine.

    Through this problem and solution , I guess if I want to set the format on the input-only AUHAL ,the format must be match or can be shift to the available format which the input device is using. So what I need to do is setting the format on the input device firstly and set the format on the input-only AUHAL next.