Merge branch 'osx-channelmap' of git://github.com/jacobvosmaer/MPD

This commit is contained in:
Max Kellermann 2016-07-14 09:19:24 +02:00
commit bd8414f8ea
3 changed files with 186 additions and 0 deletions

View File

@ -313,6 +313,15 @@ input {
# mixer_type "software"
#}
#
# An example of an OS X output:
#
#audio_output {
# type "osx"
# name "My OS X Device"
## device "Built-in Output" # optional
## channel_map "-1,-1,0,1" # optional
#}
#
## Example "pipe" output:
#
#audio_output {

View File

@ -3149,6 +3149,55 @@ buffer_size: 16384</programlisting>
<para>
The "Mac OS X" plugin uses Apple's CoreAudio API.
</para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>Setting</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>
<varname>device</varname>
<parameter>NAME</parameter>
</entry>
<entry>
Sets the device which should be used. Uses device names as listed in the
"Audio Devices" window of "Audio MIDI Setup".
</entry>
</row>
<row>
<entry>
<varname>channel_map</varname>
<parameter>SOURCE,SOURCE,...</parameter>
</entry>
<entry><para>
Specifies a channel map. If your audio device has more than two
outputs this allows you to route audio to auxillary outputs. For
predictable results you should also specify a "format" with a fixed
number of channels, e.g. "*:*:2". The number of items in the channel
map must match the number of output channels of your output device.
Each list entry specifies the source for that output channel; use "-1"
to silence an output. For example, if you have a four-channel output
device and you wish to send stereo sound (format "*:*:2") to outputs 3
and 4 while leaving outputs 1 and 2 silent then set the channel map to
"-1,-1,0,1". In this example '0' and '1' denote the left and right
channel respectively.
</para>
<para>
The channel map may not refer to outputs that do not exist according
to the format. If the format is "*:*:1" (mono) and you have a
four-channel sound card then "-1,-1,0,0" (dual mono output on the
second pair of sound card outputs) is a valid channel map but
"-1,-1,0,1" is not because the second channel ('1') does not exist
when the output is mono.
</para></entry>
</row>
</tbody>
</tgroup>
</informaltable>
</section>
<section>

View File

@ -39,6 +39,7 @@ struct OSXOutput {
OSType component_subtype;
/* only applicable with kAudioUnitSubType_HALOutput */
const char *device_name;
const char *channel_map;
AudioComponentInstance au;
AudioStreamBasicDescription asbd;
@ -93,6 +94,8 @@ osx_output_configure(OSXOutput *oo, const ConfigBlock &block)
/* XXX am I supposed to strdup() this? */
oo->device_name = device;
}
oo->channel_map = block.GetBlockValue("channel_map");
}
static AudioOutput *
@ -117,6 +120,126 @@ osx_output_finish(AudioOutput *ao)
delete oo;
}
static bool
osx_output_parse_channel_map(
const char *device_name,
const char *channel_map_str,
SInt32 channel_map[],
UInt32 num_channels,
Error &error)
{
char *endptr;
unsigned int inserted_channels = 0;
bool want_number = true;
while (*channel_map_str) {
if (inserted_channels >= num_channels) {
error.Format(osx_output_domain,
"%s: channel map contains more than %u entries or trailing garbage",
device_name, num_channels);
return false;
}
if (!want_number && *channel_map_str == ',') {
++channel_map_str;
want_number = true;
continue;
}
if (want_number &&
(isdigit(*channel_map_str) || *channel_map_str == '-')
) {
channel_map[inserted_channels] = strtol(channel_map_str, &endptr, 10);
if (channel_map[inserted_channels] < -1) {
error.Format(osx_output_domain,
"%s: channel map value %d not allowed (must be -1 or greater)",
device_name, channel_map[inserted_channels]);
return false;
}
channel_map_str = endptr;
want_number = false;
FormatDebug(osx_output_domain,
"%s: channel_map[%u] = %d",
device_name, inserted_channels, channel_map[inserted_channels]);
++inserted_channels;
continue;
}
error.Format(osx_output_domain,
"%s: invalid character '%c' in channel map",
device_name, *channel_map_str);
return false;
}
if (inserted_channels < num_channels) {
error.Format(osx_output_domain,
"%s: channel map contains less than %u entries",
device_name, num_channels);
return false;
}
return true;
}
static bool
osx_output_set_channel_map(OSXOutput *oo, Error &error)
{
AudioStreamBasicDescription desc;
OSStatus status;
SInt32 *channel_map = nullptr;
UInt32 size, num_channels;
char errormsg[1024];
bool ret = true;
size = sizeof(desc);
memset(&desc, 0, size);
status = AudioUnitGetProperty(oo->au,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&desc,
&size);
if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status,
"%s: unable to get number of output device channels: %s",
oo->device_name, errormsg);
ret = false;
goto done;
}
num_channels = desc.mChannelsPerFrame;
channel_map = new SInt32[num_channels];
if (!osx_output_parse_channel_map(oo->device_name,
oo->channel_map,
channel_map,
num_channels,
error)
) {
ret = false;
goto done;
}
size = num_channels * sizeof(SInt32);
status = AudioUnitSetProperty(oo->au,
kAudioOutputUnitProperty_ChannelMap,
kAudioUnitScope_Input,
0,
channel_map,
size);
if (status != noErr) {
osx_os_status_to_cstring(status, errormsg, sizeof(errormsg));
error.Format(osx_output_domain, status,
"%s: unable to set channel map: %s", oo->device_name, errormsg);
ret = false;
goto done;
}
done:
delete[] channel_map;
return ret;
}
static bool
osx_output_set_device(OSXOutput *oo, Error &error)
{
@ -214,6 +337,11 @@ osx_output_set_device(OSXOutput *oo, Error &error)
"set OS X audio output device ID=%u, name=%s",
(unsigned)deviceids[i], name);
if (oo->channel_map && !osx_output_set_channel_map(oo, error)) {
ret = false;
goto done;
}
done:
delete[] deviceids;
if (cfname)