Channel Mapping with AUHAL

Hi, our Mac app uses AUHAL for audio output. The data we’re playing has channels laid out in the WAVE default ordering (this is the same order as Core Audio’s AudioChannelBitmap). Playing this data usually results in the correct channel playing through the correct speaker, but there are cases where it does not:

  • Outputting audio over HDMI usually results in the Center and LFE channels being swapped
  • Or “Configure Speakers” in Audio MIDI Setup.app can be used to change the speaker<->channel mapping (i.e. even swapping L/R on a 2ch stereo setup), but this does not affect our app’s audio

I’ve found that getting kAudioUnitProperty_AudioChannelLayout on an output unit returns the speaker<->channel mapping as set up in Audio MIDI Setup (i.e. it shows that Center/LFE swap, or if L/R are swapped).

My question: is it possible to have Core Audio do the channel remapping between the WAVE default ordering, and the speaker<->channel mapping as returned by kAudioUnitProperty_AudioChannelLayout? I have tried setting kAudioUnitProperty_AudioChannelLayout on the input unit, and didn’t see a change. I’ve also tried to set kAudioOutputUnitProperty_ChannelMap on the output unit but cannot get it to work.

Or do I need to remap the channels myself before giving samples to Core Audio?

Answered by Frameworks Engineer in 892244022

My apologies, I didn't read your original message carefully and pasted back in your reply kAudioUnitProperty_AudioChannelLayout -- but I was thinking of kAudioOutputUnitProperty_ChannelMap.

AUHAL / AUConverter don't seem to support automatic remapping of channel layouts.

You can use AudioFormatGetProperty() with kAudioFormatProperty_ChannelMap to convert a pair of channel layouts to a channel map that AUHAL can use, however. This is sufficient for simple channel re-ordering.

If you need to support upmixing and downmixing (different input and output channel counts), then consider AUMultiChannelMixer, which accepts channel layouts on its input and output busses, upmixing and downmixing as needed.

You should be able to set kAudioUnitProperty_AudioChannelLayout on bus 0 (the output half of the unit) and have that work. One thing to note is that the map can look inverted at first glance -- it says, for each destination channel, which source channel to map from. If this isn't working can you please describe how you're setting up the channel map and what seems to be happening incorrectly?

The rough steps for getting the audio unit:

AudioComponentDescription desc = {
    .componentType = kAudioUnitType_Output,
    .componentSubType = kAudioUnitSubType_HALOutput,
    .componentManufacturer = kAudioUnitManufacturer_Apple,
};

AudioComponent comp = AudioComponentFindNext(NULL, &desc);

AudioComponentInstance unit;
AudioComponentInstanceNew(comp, &unit);

AudioUnitSetProperty(unit, kAudioOutputUnitProperty_CurrentDevice,
            kAudioUnitScope_Global, 0, &adevid, sizeof(adevid));

And then setting the stream format and channel layout:

AudioUnitSetProperty(unit, kAudioUnitProperty_StreamFormat,
                                  kAudioUnitScope_Input, 0, dev_desc, sizeof(*dev_desc));

AudioChannelLayout layout =
{
        .mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap,
        .mChannelBitmap    = 0x3f, // typical value for 5.1 audio
        .mNumberChannelDescriptions = 0,
};

AudioUnitSetProperty(unit, kAudioUnitProperty_AudioChannelLayout,
                                  kAudioUnitScope_Input, 0, &layout, sizeof(layout));

Setting the channel layout like this doesn't return any error codes, but seems to have no effect. For the map looking inverted, are you referring to kAudioOutputUnitProperty_ChannelMap? I've also tried that without success, although I can't remember exactly what the result was. I'll see if I can re-test it.

My apologies, I didn't read your original message carefully and pasted back in your reply kAudioUnitProperty_AudioChannelLayout -- but I was thinking of kAudioOutputUnitProperty_ChannelMap.

AUHAL / AUConverter don't seem to support automatic remapping of channel layouts.

You can use AudioFormatGetProperty() with kAudioFormatProperty_ChannelMap to convert a pair of channel layouts to a channel map that AUHAL can use, however. This is sufficient for simple channel re-ordering.

If you need to support upmixing and downmixing (different input and output channel counts), then consider AUMultiChannelMixer, which accepts channel layouts on its input and output busses, upmixing and downmixing as needed.

Simple channel re-ordering is all I need, but I can't get kAudioOutputUnitProperty_ChannelMap to work correctly. If I pass { -1, -1 } as a map it mutes both channels, but any other map results in no change to the audio. I've also tried setting the map on kAudioUnitScope_Output and kAudioUnitScope_Input, with no difference.

I'm attaching a small test program that generates noise to channel 0, but also sets a channel map that should reverse the L/R channels. It doesn't work, and I get noise on L. Maybe you can figure out what I'm doing wrong, or if there's a bug somewhere?

Channel Mapping with AUHAL
 
 
Q