Add an 'audio.coreaudio.channel-map' setting to enable multi-channel output routing for CoreAudio.

This commit is contained in:
Matt Taylor 2022-04-15 14:08:21 -06:00
parent 49a3b410f0
commit 326d90e952
2 changed files with 99 additions and 1 deletions

View file

@ -452,6 +452,27 @@ Developers:
Selects the CoreAudio device to use. Selects the CoreAudio device to use.
</desc> </desc>
</setting> </setting>
<setting>
<name>coreaudio.channel-map</name>
<type>str</type>
<def>0,1</def>
<desc>
This setting is a comma-separated integer list that maps CoreAudio channels
to output device channels. The value of each position in the list, up to the number of
output channels available on your device, is the zero-based index of the fluidsynth
output channel to route there. Additionally, the special value of -1 will turn off
an output.
For example, the default map for a single stereo output is "0,1". A value of "0,0" will
copy the left channel to the right, a value of "1,0" will flip left and right, and a
value of "-1,1" will play only the right channel.
With a six channel output device, and the synth.audio-channels and synth.audio-groups
settings both set to "2", a channel map of "-1,-1,0,1,2,3" will result in notes from odd
MIDI channels being sent to outputs 3 and 4, and even MIDI channels being send to
outputs 5 and 6.
</desc>
</setting>
<setting> <setting>
<name>dart.device</name> <name>dart.device</name>
<type>str</type> <type>str</type>

View file

@ -114,6 +114,75 @@ get_num_outputs(AudioDeviceID deviceID)
return total; return total;
} }
void
set_channel_map(AudioUnit outputUnit, int audio_channels, const char *map_string)
{
OSStatus status;
long int number_of_channels;
int i, *channel_map;
UInt32 property_size;
Boolean writable = false;
status = AudioUnitGetPropertyInfo(outputUnit,
kAudioOutputUnitProperty_ChannelMap,
kAudioUnitScope_Output,
0,
&property_size, &writable);
if(status != noErr)
{
FLUID_LOG(FLUID_ERR, "Failed to get the channel map size. Status=%ld\n", (long int) status);
return;
}
number_of_channels = property_size / sizeof(int);
if(!number_of_channels)
{
return;
}
channel_map = FLUID_ARRAY(int, number_of_channels);
if(channel_map == NULL)
{
FLUID_LOG(FLUID_ERR, "Out of memory.\n");
return;
}
FLUID_MEMSET(channel_map, 0, property_size);
status = AudioUnitGetProperty(outputUnit,
kAudioOutputUnitProperty_ChannelMap,
kAudioUnitScope_Output,
0,
channel_map, &property_size);
if(status != noErr)
{
FLUID_LOG(FLUID_ERR, "Failed to get the existing channel map. Status=%ld\n", (long int) status);
FLUID_FREE(channel_map);
return;
}
fluid_settings_split_csv(map_string, channel_map, (int) number_of_channels);
for(i = 0; i < number_of_channels; i++)
{
if(channel_map[i] < -1 || channel_map[i] >= audio_channels)
{
channel_map[i] = -1;
}
}
status = AudioUnitSetProperty(outputUnit,
kAudioOutputUnitProperty_ChannelMap,
kAudioUnitScope_Output,
0,
channel_map, property_size);
if(status != noErr)
{
FLUID_LOG(FLUID_ERR, "Failed to set the channel map. Status=%ld\n", (long int) status);
}
FLUID_FREE(channel_map);
}
void void
fluid_core_audio_driver_settings(fluid_settings_t *settings) fluid_core_audio_driver_settings(fluid_settings_t *settings)
{ {
@ -125,6 +194,7 @@ fluid_core_audio_driver_settings(fluid_settings_t *settings)
pa.mElement = kAudioObjectPropertyElementMain; pa.mElement = kAudioObjectPropertyElementMain;
fluid_settings_register_str(settings, "audio.coreaudio.device", "default", 0); fluid_settings_register_str(settings, "audio.coreaudio.device", "default", 0);
fluid_settings_register_str(settings, "audio.coreaudio.channel-map", "", 0);
fluid_settings_add_option(settings, "audio.coreaudio.device", "default"); fluid_settings_add_option(settings, "audio.coreaudio.device", "default");
if(OK(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, 0, &size))) if(OK(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, 0, &size)))
@ -169,7 +239,7 @@ new_fluid_core_audio_driver(fluid_settings_t *settings, fluid_synth_t *synth)
fluid_audio_driver_t * fluid_audio_driver_t *
new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data) new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func, void *data)
{ {
char *devname = NULL; char *devname = NULL, *channel_map = NULL;
fluid_core_audio_driver_t *dev = NULL; fluid_core_audio_driver_t *dev = NULL;
int period_size, periods, audio_channels = 1; int period_size, periods, audio_channels = 1;
double sample_rate; double sample_rate;
@ -335,6 +405,13 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func
goto error_recovery; goto error_recovery;
} }
if(fluid_settings_dupstr(settings, "audio.coreaudio.channel-map", &channel_map) == FLUID_OK /* alloc channel map */
&& channel_map && strlen(channel_map) > 0)
{
set_channel_map(dev->outputUnit, audio_channels, channel_map);
}
FLUID_FREE(channel_map); /* free channel map */
status = AudioUnitSetProperty(dev->outputUnit, status = AudioUnitSetProperty(dev->outputUnit,
kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Input, kAudioUnitScope_Input,