Search code examples
macoscore-audioaudiounit

Setting sampling rate of default audio output device programmatically


I'm working on an application that plays sounds through the default audio device on a Mac. I want to change the output sampling rate and bit depth of the default output device but it always gives me a kAudioUnitErr_PropertyNotWritable error code.

Here is my test code:

    AudioStreamBasicDescription streamFormat;
    AudioStreamBasicDescription newStreamFormat;
    newStreamFormat.mSampleRate = 96000;        // the sample rate of the audio stream
    newStreamFormat.mFormatID = kAudioFormatLinearPCM;     // the specific encoding type of audio stream
    newStreamFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger;//kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsNonMixable;
    newStreamFormat.mFramesPerPacket = 1;
    newStreamFormat.mChannelsPerFrame = 1;
    newStreamFormat.mBitsPerChannel = 24;
    newStreamFormat.mBytesPerPacket = 2;
    newStreamFormat.mBytesPerFrame = 2;
    
    UInt32 size = sizeof(AudioStreamBasicDescription);

    result = AudioUnitGetProperty(myUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
    
    result = AudioOutputUnitStop(myUnit);
    result = AudioUnitUninitialize(myUnit);
    
    result = AudioUnitSetProperty(myUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &newStreamFormat, size);
    result = AudioUnitInitialize(myUnit);
    result = AudioOutputUnitStart(myUnit);
    
    result = AudioUnitGetProperty(myUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &streamFormat, &size);
    result = AudioUnitGetProperty(myUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &streamFormat, &size);

When I make the call to set the stream format on kAudioUnitScope_Input I don't get any error but when I set it on kAudioUnitScope_Output if fails with the property not writable error.

It must be possible to do this programmatically (Audio MIDI Setup does it) but I have searched and searched but I haven't been able to find any solution.

I did find this post that implies that setting the input sampling rate of the device will update the output as well. I tried this but when I read back the property the output doesn't match what I set on the input.


Solution

  • I'm pretty sure it's not the output AudioUnit's job to configure devices. It's more of an intermediary between clients and audio devices. Which means AudioUnitSetProperty() is the wrong API for the job.

    So if you want to configure the device, try setting kAudioDevicePropertyNominalSampleRate on it using the AudioObjectSetPropertyData() function.

    Then, unless you want a gratuitous rate conversion, you probably want to make sure your audio unit input format matches the new device sample rate by doing what you're already doing: calling AudioUnitSetProperty() on the input (data going into the audio unit) scope.