7
votes

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?

3

3 Answers

4
votes

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.

2
votes

In my experience, using settings other than 44.1kHz and 16 bit audio results in all sorts of weird errors. Some generic suggestions which might set you on the right path:

  • Try Novocaine (https://github.com/alexbw/novocaine). It really takes the pain out of working with AudioUnits.
  • Try getting your app working with 44.1kHz and then downsampling the audio yourself.
  • The bitrate you are setting may not be compatible with the desired sample rate.
1
votes

Your answer was very helpful for me. However, the use of AudioDeviceGetProperty is depreciated. The following listing may be helpful to get things up to date. As an example the sample rate is set to 32 kHz.

 // Get the available sample rates of the default input device.
defaultDeviceProperty.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;


CheckError    (AudioObjectGetPropertyDataSize(defaultDevice,
                                   &defaultDeviceProperty,
                                   0,
                                   NULL,
                                   &propertySize),
            "Couldn't get sample rate count");
int m_valueCount = propertySize / sizeof(AudioValueRange) ;

printf("Available %d Sample Rates\n",m_valueCount) ;

AudioValueRange m_valueTabe[m_valueCount];

CheckError    (AudioObjectGetPropertyData(defaultDevice,
                                          &defaultDeviceProperty,
                                          0,
                                          NULL,
                                          &propertySize,
                                          m_valueTabe),
               "Couldn't get available sample rates");


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

// Set the sample rate to one of the available values.
AudioValueRange inputSampleRate;
inputSampleRate.mMinimum = 32000;
inputSampleRate.mMaximum = 32000;
defaultDeviceProperty.mSelector = kAudioDevicePropertyNominalSampleRate;
CheckError    (AudioObjectSetPropertyData(defaultDevice,
                                          &defaultDeviceProperty,
                                          0,
                                          NULL,
                                          sizeof(inputSampleRate),
                                          &inputSampleRate),
               "Couldn't get available sample rates");