0

I'm trying to use kAudioUnitSubType_RemoteIO unit on Catalyst on a Mac where there's no microphone.

The problem is, the whole initialization of bus 1 (that's input) goes fine, including setting the format, even enabling the bus, but then it fails at AudioOutputUnitStart(unit) with some obscure error -66628 that's not even listed on osstatus.com.

However, if I start the unit with only the output bus 0, then it runs fine and plays my audio.

So, is there an easy way to detect the situation when there's no device behind RemoteIO without enumerating all the devices? Just by trying some function (set or get a parameter?) that will definitely fail.

Here is my code:

    // Instantiate the unit
    var desc = AudioComponentDescription(componentType: kAudioUnitType_Output, componentSubType: kAudioUnitSubType_RemoteIO, componentManufacturer: kAudioUnitManufacturer_Apple, componentFlags: 0, componentFlagsMask: 0)
    let comp = AudioComponentFindNext(nil, &desc)!
    var unit: AudioUnit!
    NotError(AudioComponentInstanceNew(comp, &unit), 51000)

    // Set maximum buffer size as recommended by Apple
    var maxFramesPerSlice = UInt32(Self.maxFramesPerSlice)
    NotError(AudioUnitSetProperty(unit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 1, &maxFramesPerSlice, SizeOf(maxFramesPerSlice)), 51003)

    // Set format and sample rate to the same as hardware output
    var descr = AudioStreamBasicDescription.canonical(isStereo: true, sampleRate: hardwareSampleRate)
    NotError(AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &descr, SizeOf(descr)), 51022)

    // Set render callback
    var callback = AURenderCallbackStruct(inputProc: stereoInputRenderCallback, inputProcRefCon: Bridge(obj: self))
    NotError(AudioUnitSetProperty(unit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Output, 1, &callback, SizeOf(callback)), 51008)

    // Enable input - required as it's disabled by default
    var enable: UInt32 = 1
    NotError(AudioUnitSetProperty(unit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &enable, SizeOf(enable)), 51021)

    NotError(AudioUnitInitialize(unit), 51007)

So it passes all that and only fails with OSStatus(-66628) here:

    NotError(AudioOutputUnitStart(unit), 51009)

I think there has to be some way of detecting the absense of the device before trying to start it.

Edit: ideally I'm looking for a cross-platform way (ioS/macOS)

mojuba
  • 11,842
  • 9
  • 51
  • 72
  • 1
    Perhaps `kAudioDevicePropertyStreamConfiguration` in `kAudioObjectPropertyScopeInput` and check `mBuffers > 0`? – sbooth Jun 26 '20 at 12:25
  • @sbooth did something similar already. I get the format descriptor on the hardware side of RemoteIO's two buses. If there's no device behind the bus then the format structure is all zeros. On a Mac Pro for example and under Catalyst I get all zeros for the microphone so seems to be working. – mojuba Jun 26 '20 at 18:47
  • Excellent, glad you got it working. I posted my suggestion as an answer so it won't be lost. – sbooth Jun 26 '20 at 18:58

1 Answers1

1

I believe that checking whether the AudioStreamBasicDescription returned for kAudioDevicePropertyStreamConfiguration, kAudioObjectPropertyScopeInput has mBuffers > 0 will indicate if a device is present.

sbooth
  • 16,646
  • 2
  • 55
  • 81
  • Thanks. This requires a deviceID though, I'm not aware of any cross-platform ways of getting the current input deviceID on iOS, Catalyst and macOS. Any suggestions? Been looking for a while. – mojuba Jun 26 '20 at 19:01
  • You're right about this requiring a device ID. I'm not sure how to get one on iOS. I know you can get a UID from `AVAudioSessionPortDescription` but I'm not sure if it's possible to translate to a device ID (normally on macOS I'd use `kAudioHardwarePropertyTranslateUIDToDevice`) but I don't know if the UIDs are equivalent. – sbooth Jun 26 '20 at 20:09