mmdevapi: Avoid reporting odd numbers of channels.
Fixes sound in Touhou Luna Nights with some surround sound configurations. Signed-off-by: Andrew Eikum <aeikum@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
8487265f81
commit
b56d19d3a0
|
@ -1831,6 +1831,22 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
|
|||
else
|
||||
fmt->Format.nChannels = max_channels;
|
||||
|
||||
if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1)){
|
||||
/* For most hardware on Windows, users must choose a configuration with an even
|
||||
* number of channels (stereo, quad, 5.1, 7.1). Users can then disable
|
||||
* channels, but those channels are still reported to applications from
|
||||
* GetMixFormat! Some applications behave badly if given an odd number of
|
||||
* channels (e.g. 2.1). */
|
||||
|
||||
if(fmt->Format.nChannels < max_channels)
|
||||
fmt->Format.nChannels += 1;
|
||||
else
|
||||
/* We could "fake" more channels and downmix the emulated channels,
|
||||
* but at that point you really ought to tweak your ALSA setup or
|
||||
* just use PulseAudio. */
|
||||
WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
|
||||
}
|
||||
|
||||
fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
|
||||
|
||||
if((err = snd_pcm_hw_params_get_rate_max(This->hw_params, &max_rate,
|
||||
|
|
|
@ -1736,6 +1736,122 @@ unsupported:
|
|||
return AUDCLNT_E_UNSUPPORTED_FORMAT;
|
||||
}
|
||||
|
||||
static DWORD ca_channel_layout_to_channel_mask(const AudioChannelLayout *layout)
|
||||
{
|
||||
int i;
|
||||
DWORD mask = 0;
|
||||
|
||||
for (i = 0; i < layout->mNumberChannelDescriptions; ++i) {
|
||||
switch (layout->mChannelDescriptions[i].mChannelLabel) {
|
||||
default: FIXME("Unhandled channel 0x%x\n", layout->mChannelDescriptions[i].mChannelLabel); break;
|
||||
case kAudioChannelLabel_Left: mask |= SPEAKER_FRONT_LEFT; break;
|
||||
case kAudioChannelLabel_Mono:
|
||||
case kAudioChannelLabel_Center: mask |= SPEAKER_FRONT_CENTER; break;
|
||||
case kAudioChannelLabel_Right: mask |= SPEAKER_FRONT_RIGHT; break;
|
||||
case kAudioChannelLabel_LeftSurround: mask |= SPEAKER_BACK_LEFT; break;
|
||||
case kAudioChannelLabel_CenterSurround: mask |= SPEAKER_BACK_CENTER; break;
|
||||
case kAudioChannelLabel_RightSurround: mask |= SPEAKER_BACK_RIGHT; break;
|
||||
case kAudioChannelLabel_LFEScreen: mask |= SPEAKER_LOW_FREQUENCY; break;
|
||||
case kAudioChannelLabel_LeftSurroundDirect: mask |= SPEAKER_SIDE_LEFT; break;
|
||||
case kAudioChannelLabel_RightSurroundDirect: mask |= SPEAKER_SIDE_RIGHT; break;
|
||||
case kAudioChannelLabel_TopCenterSurround: mask |= SPEAKER_TOP_CENTER; break;
|
||||
case kAudioChannelLabel_VerticalHeightLeft: mask |= SPEAKER_TOP_FRONT_LEFT; break;
|
||||
case kAudioChannelLabel_VerticalHeightCenter: mask |= SPEAKER_TOP_FRONT_CENTER; break;
|
||||
case kAudioChannelLabel_VerticalHeightRight: mask |= SPEAKER_TOP_FRONT_RIGHT; break;
|
||||
case kAudioChannelLabel_TopBackLeft: mask |= SPEAKER_TOP_BACK_LEFT; break;
|
||||
case kAudioChannelLabel_TopBackCenter: mask |= SPEAKER_TOP_BACK_CENTER; break;
|
||||
case kAudioChannelLabel_TopBackRight: mask |= SPEAKER_TOP_BACK_RIGHT; break;
|
||||
case kAudioChannelLabel_LeftCenter: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
|
||||
case kAudioChannelLabel_RightCenter: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* For most hardware on Windows, users must choose a configuration with an even
|
||||
* number of channels (stereo, quad, 5.1, 7.1). Users can then disable
|
||||
* channels, but those channels are still reported to applications from
|
||||
* GetMixFormat! Some applications behave badly if given an odd number of
|
||||
* channels (e.g. 2.1). Here, we find the nearest configuration that Windows
|
||||
* would report for a given channel layout. */
|
||||
static void convert_channel_layout(const AudioChannelLayout *ca_layout, WAVEFORMATEXTENSIBLE *fmt)
|
||||
{
|
||||
DWORD ca_mask = ca_channel_layout_to_channel_mask(ca_layout);
|
||||
|
||||
TRACE("Got channel mask for CA: 0x%x\n", ca_mask);
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions == 1)
|
||||
{
|
||||
fmt->Format.nChannels = 1;
|
||||
fmt->dwChannelMask = ca_mask;
|
||||
return;
|
||||
}
|
||||
|
||||
/* compare against known configurations and find smallest configuration
|
||||
* which is a superset of the given speakers */
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions <= 2 &&
|
||||
(ca_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 2;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions <= 4 &&
|
||||
(ca_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 4;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions <= 4 &&
|
||||
(ca_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 4;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions <= 6 &&
|
||||
(ca_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 6;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions <= 6 &&
|
||||
(ca_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 6;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions <= 8 &&
|
||||
(ca_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 8;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ca_layout->mNumberChannelDescriptions <= 8 &&
|
||||
(ca_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 8;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
|
||||
return;
|
||||
}
|
||||
|
||||
/* oddball format, report truthfully */
|
||||
fmt->Format.nChannels = ca_layout->mNumberChannelDescriptions;
|
||||
fmt->dwChannelMask = ca_mask;
|
||||
}
|
||||
|
||||
static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
|
||||
WAVEFORMATEX **pwfx)
|
||||
{
|
||||
|
@ -1745,6 +1861,7 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
|
|||
UInt32 size;
|
||||
Float64 rate;
|
||||
AudioBufferList *buffers;
|
||||
AudioChannelLayout *layout;
|
||||
AudioObjectPropertyAddress addr;
|
||||
int i;
|
||||
|
||||
|
@ -1760,6 +1877,37 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
|
|||
|
||||
fmt->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
|
||||
addr.mScope = This->scope;
|
||||
addr.mElement = 0;
|
||||
addr.mSelector = kAudioDevicePropertyPreferredChannelLayout;
|
||||
|
||||
sc = AudioObjectGetPropertyDataSize(This->adevid, &addr, 0, NULL, &size);
|
||||
if(sc == noErr){
|
||||
layout = HeapAlloc(GetProcessHeap(), 0, size);
|
||||
|
||||
sc = AudioObjectGetPropertyData(This->adevid, &addr, 0, NULL, &size, layout);
|
||||
if(sc == noErr){
|
||||
TRACE("Got channel layout: {tag: 0x%x, bitmap: 0x%x, num_descs: %u}\n",
|
||||
layout->mChannelLayoutTag, layout->mChannelBitmap, layout->mNumberChannelDescriptions);
|
||||
|
||||
if(layout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions){
|
||||
convert_channel_layout(layout, fmt);
|
||||
}else{
|
||||
WARN("Haven't implemented support for this layout tag: 0x%x, guessing at layout\n", layout->mChannelLayoutTag);
|
||||
fmt->Format.nChannels = 0;
|
||||
}
|
||||
}else{
|
||||
TRACE("Unable to get _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
|
||||
fmt->Format.nChannels = 0;
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, layout);
|
||||
}else{
|
||||
TRACE("Unable to get size for _PreferredChannelLayout property: %x, guessing at layout\n", (int)sc);
|
||||
fmt->Format.nChannels = 0;
|
||||
}
|
||||
|
||||
if(fmt->Format.nChannels == 0){
|
||||
addr.mScope = This->scope;
|
||||
addr.mElement = 0;
|
||||
addr.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
|
@ -1793,6 +1941,7 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
|
|||
HeapFree(GetProcessHeap(), 0, buffers);
|
||||
|
||||
fmt->dwChannelMask = get_channel_mask(fmt->Format.nChannels);
|
||||
}
|
||||
|
||||
addr.mSelector = kAudioDevicePropertyNominalSampleRate;
|
||||
size = sizeof(Float64);
|
||||
|
|
|
@ -1334,6 +1334,22 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
|
|||
if(fmt->Format.nChannels == 0 || fmt->Format.nChannels > 8)
|
||||
fmt->Format.nChannels = 2;
|
||||
|
||||
/* For most hardware on Windows, users must choose a configuration with an even
|
||||
* number of channels (stereo, quad, 5.1, 7.1). Users can then disable
|
||||
* channels, but those channels are still reported to applications from
|
||||
* GetMixFormat! Some applications behave badly if given an odd number of
|
||||
* channels (e.g. 2.1). */
|
||||
if(fmt->Format.nChannels > 1 && (fmt->Format.nChannels & 0x1))
|
||||
{
|
||||
if(fmt->Format.nChannels < This->ai.max_channels)
|
||||
fmt->Format.nChannels += 1;
|
||||
else
|
||||
/* We could "fake" more channels and downmix the emulated channels,
|
||||
* but at that point you really ought to tweak your OSS setup or
|
||||
* just use PulseAudio. */
|
||||
WARN("Some Windows applications behave badly with an odd number of channels (%u)!\n", fmt->Format.nChannels);
|
||||
}
|
||||
|
||||
if(This->ai.max_rate == 0)
|
||||
fmt->Format.nSamplesPerSec = 44100;
|
||||
else
|
||||
|
|
|
@ -341,11 +341,12 @@ static const enum pa_channel_position pulse_pos_from_wfx[] = {
|
|||
PA_CHANNEL_POSITION_TOP_REAR_RIGHT
|
||||
};
|
||||
|
||||
static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) {
|
||||
static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map)
|
||||
{
|
||||
int i;
|
||||
DWORD mask = 0;
|
||||
|
||||
for (i = 0; i < map->channels; ++i)
|
||||
for (i = 0; i < map->channels; ++i) {
|
||||
switch (map->map[i]) {
|
||||
default: FIXME("Unhandled channel %s\n", pa_channel_position_to_string(map->map[i])); break;
|
||||
case PA_CHANNEL_POSITION_FRONT_LEFT: mask |= SPEAKER_FRONT_LEFT; break;
|
||||
|
@ -368,10 +369,94 @@ static DWORD pulse_channel_map_to_channel_mask(const pa_channel_map *map) {
|
|||
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: mask |= SPEAKER_FRONT_LEFT_OF_CENTER; break;
|
||||
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: mask |= SPEAKER_FRONT_RIGHT_OF_CENTER; break;
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
/* For most hardware on Windows, users must choose a configuration with an even
|
||||
* number of channels (stereo, quad, 5.1, 7.1). Users can then disable
|
||||
* channels, but those channels are still reported to applications from
|
||||
* GetMixFormat! Some applications behave badly if given an odd number of
|
||||
* channels (e.g. 2.1). Here, we find the nearest configuration that Windows
|
||||
* would report for a given channel layout. */
|
||||
static void convert_channel_map(const pa_channel_map *pa_map, WAVEFORMATEXTENSIBLE *fmt)
|
||||
{
|
||||
DWORD pa_mask = pulse_channel_map_to_channel_mask(pa_map);
|
||||
|
||||
TRACE("got mask for PA: 0x%x\n", pa_mask);
|
||||
|
||||
if (pa_map->channels == 1)
|
||||
{
|
||||
fmt->Format.nChannels = 1;
|
||||
fmt->dwChannelMask = pa_mask;
|
||||
return;
|
||||
}
|
||||
|
||||
/* compare against known configurations and find smallest configuration
|
||||
* which is a superset of the given speakers */
|
||||
|
||||
if (pa_map->channels <= 2 &&
|
||||
(pa_mask & ~KSAUDIO_SPEAKER_STEREO) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 2;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pa_map->channels <= 4 &&
|
||||
(pa_mask & ~KSAUDIO_SPEAKER_QUAD) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 4;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_QUAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pa_map->channels <= 4 &&
|
||||
(pa_mask & ~KSAUDIO_SPEAKER_SURROUND) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 4;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_SURROUND;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pa_map->channels <= 6 &&
|
||||
(pa_mask & ~KSAUDIO_SPEAKER_5POINT1) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 6;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pa_map->channels <= 6 &&
|
||||
(pa_mask & ~KSAUDIO_SPEAKER_5POINT1_SURROUND) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 6;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pa_map->channels <= 8 &&
|
||||
(pa_mask & ~KSAUDIO_SPEAKER_7POINT1) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 8;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pa_map->channels <= 8 &&
|
||||
(pa_mask & ~KSAUDIO_SPEAKER_7POINT1_SURROUND) == 0)
|
||||
{
|
||||
fmt->Format.nChannels = 8;
|
||||
fmt->dwChannelMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
|
||||
return;
|
||||
}
|
||||
|
||||
/* oddball format, report truthfully */
|
||||
fmt->Format.nChannels = pa_map->channels;
|
||||
fmt->dwChannelMask = pa_mask;
|
||||
}
|
||||
|
||||
static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
|
||||
WAVEFORMATEX *wfx = &fmt->Format;
|
||||
pa_stream *stream;
|
||||
|
@ -433,10 +518,12 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
|
|||
|
||||
wfx->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||
wfx->nChannels = ss.channels;
|
||||
|
||||
convert_channel_map(&map, fmt);
|
||||
|
||||
wfx->wBitsPerSample = 8 * pa_sample_size_of_format(ss.format);
|
||||
wfx->nSamplesPerSec = ss.rate;
|
||||
wfx->nBlockAlign = pa_frame_size(&ss);
|
||||
wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8;
|
||||
wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
|
||||
if (ss.format != PA_SAMPLE_S24_32LE)
|
||||
fmt->Samples.wValidBitsPerSample = wfx->wBitsPerSample;
|
||||
|
@ -446,8 +533,6 @@ static void pulse_probe_settings(int render, WAVEFORMATEXTENSIBLE *fmt) {
|
|||
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
else
|
||||
fmt->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||
|
||||
fmt->dwChannelMask = pulse_channel_map_to_channel_mask(&map);
|
||||
}
|
||||
|
||||
static HRESULT pulse_connect(void)
|
||||
|
|
Loading…
Reference in New Issue