diff --git a/doc/fluidsettings.xml b/doc/fluidsettings.xml index b0ce4d4e..38c10903 100644 --- a/doc/fluidsettings.xml +++ b/doc/fluidsettings.xml @@ -452,6 +452,27 @@ Developers: Selects the CoreAudio device to use. + + coreaudio.channel-map + str + 0,1 + + 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. + + dart.device str diff --git a/src/drivers/fluid_coreaudio.c b/src/drivers/fluid_coreaudio.c index be9b24a3..facb550d 100644 --- a/src/drivers/fluid_coreaudio.c +++ b/src/drivers/fluid_coreaudio.c @@ -114,6 +114,75 @@ get_num_outputs(AudioDeviceID deviceID) 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 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; 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"); 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 * 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; int period_size, periods, audio_channels = 1; double sample_rate; @@ -335,6 +405,13 @@ new_fluid_core_audio_driver2(fluid_settings_t *settings, fluid_audio_func_t func 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, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Input,