output/osx channel_map feature
This commit is contained in:
parent
b67e7df38e
commit
4cd9abe632
@ -251,6 +251,32 @@ errors on bandwidth-limited devices. Some users have reported good results
|
|||||||
with this set to 50000, but not all devices support values this high. Most
|
with this set to 50000, but not all devices support values this high. Most
|
||||||
users do not need to change this. The default is 256000000 / sample_rate(kHz),
|
users do not need to change this. The default is 256000000 / sample_rate(kHz),
|
||||||
or 5804 microseconds for CD-quality audio.
|
or 5804 microseconds for CD-quality audio.
|
||||||
|
.SH OPTIONAL OS X OUTPUT PARAMETERS
|
||||||
|
.TP
|
||||||
|
.B device <dev>
|
||||||
|
This specifies the device to use for audio output. The default is
|
||||||
|
"default". Use the names listed in the "Audio Devices" window of
|
||||||
|
"Audio MIDI Setup".
|
||||||
|
.TP
|
||||||
|
.B channel_map <input,input,input...>
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
.SH FILES
|
.SH FILES
|
||||||
.TP
|
.TP
|
||||||
.BI ~/.mpdconf
|
.BI ~/.mpdconf
|
||||||
|
@ -313,6 +313,15 @@ input {
|
|||||||
# mixer_type "software"
|
# 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:
|
## Example "pipe" output:
|
||||||
#
|
#
|
||||||
#audio_output {
|
#audio_output {
|
||||||
|
@ -39,6 +39,7 @@ struct OSXOutput {
|
|||||||
OSType component_subtype;
|
OSType component_subtype;
|
||||||
/* only applicable with kAudioUnitSubType_HALOutput */
|
/* only applicable with kAudioUnitSubType_HALOutput */
|
||||||
const char *device_name;
|
const char *device_name;
|
||||||
|
const char *channel_map;
|
||||||
|
|
||||||
AudioComponentInstance au;
|
AudioComponentInstance au;
|
||||||
AudioStreamBasicDescription asbd;
|
AudioStreamBasicDescription asbd;
|
||||||
@ -93,6 +94,8 @@ osx_output_configure(OSXOutput *oo, const ConfigBlock &block)
|
|||||||
/* XXX am I supposed to strdup() this? */
|
/* XXX am I supposed to strdup() this? */
|
||||||
oo->device_name = device;
|
oo->device_name = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oo->channel_map = block.GetBlockValue("channel_map");
|
||||||
}
|
}
|
||||||
|
|
||||||
static AudioOutput *
|
static AudioOutput *
|
||||||
@ -117,6 +120,126 @@ osx_output_finish(AudioOutput *ao)
|
|||||||
delete oo;
|
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
|
static bool
|
||||||
osx_output_set_device(OSXOutput *oo, Error &error)
|
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",
|
"set OS X audio output device ID=%u, name=%s",
|
||||||
(unsigned)deviceids[i], name);
|
(unsigned)deviceids[i], name);
|
||||||
|
|
||||||
|
if (oo->channel_map && !osx_output_set_channel_map(oo, error)) {
|
||||||
|
ret = false;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
delete[] deviceids;
|
delete[] deviceids;
|
||||||
if (cfname)
|
if (cfname)
|
||||||
|
Loading…
Reference in New Issue
Block a user