dsound: Add detection of output format and allow a maximum of 8 channels
Donny Yang
xiao.tai.lang.de.email at gmail.com
Sat Jul 14 02:00:02 CDT 2012
Andrew, I've removed the two driver edits. IAudioClient_GetMixFormat()
needs an initialised client parameter which doesn't exist in
DirectSoundDevice_Create(), so it can only be done after the client is
initialised, which is approximately where I put the code.
Also, I'm new to this, so is this the correct way to resend a patch?
On Fri, Jul 13, 2012 at 11:10 PM, Andrew Eikum <aeikum at codeweavers.com> wrote:
> Thanks, Donny. Nice work overall. Some comments below.
>
> On Fri, Jul 13, 2012 at 05:57:48PM +1000, Donny Yang wrote:
>> This patch makes dsound automatically get the output format when a
>> output device is initialised and also allows up to 8 output channels
>> to be used for ALSA and PulseAudio. Using ALSA with over 2 channels
>> outputs the channels in the wrong speakers with my testing but I don't
>> know why so I can't fix that.
>
> You probably already discovered that ALSA's multi-channel support is
> primitive at best. You have to test each channel and hand-craft an
> asoundrc to match your hardware. See some more information here:
> <http://drona.csa.iisc.ernet.in/~uday/alsamch.shtml>
>
>> @@ -1492,6 +1493,24 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG
>> } else
>> WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
>>
>> + hr = IAudioClient_GetMixFormat(device->client, &pwfx);
>> + if(FAILED(hr)){
>> + WARN("IAudioClient_GetMixFormat failed: %08x; Falling back to default output format\n", hr);
>> + }else{
>> + DWORD oldPriolevel = device->priolevel;
>> + device->priolevel = DSSCL_WRITEPRIMARY;
>> + hr = primarybuffer_SetFormat(device, pwfx);
>> + device->priolevel = oldPriolevel;
>
> This looks like a hack. I think it fits better in
> DirectSoundDevice_Create, where the default values are copied in. Is
> there a reason it couldn't be done there?
>
>> -void put_mono2stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
>> +void put_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
>> {
>> - dsb->put_aux(dsb, pos, 0, value);
>> - dsb->put_aux(dsb, pos, 1, value);
>> + /* XXX: Is this how Windows does this? */
>> + DWORD c;
>> + for (c = 0; c < dsb->device->pwfx->nChannels; ++c)
>> + dsb->put_aux(dsb, pos, c, value);
>> }
>>
>
> I think this is fine. I wonder if we should skip the LOWFREQ channel,
> though we'd have to be careful not to affect performance too much, as
> this runs in a really tight loop.
>
>> void mixieee32(float *src, float *dst, unsigned samples)
>> diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c
>> index 072b25a..538534d 100644
>> --- a/dlls/dsound/dsound_main.c
>> +++ b/dlls/dsound/dsound_main.c
>> @@ -642,7 +642,7 @@ DirectSoundCaptureEnumerateW(
>> */
>>
>> typedef HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj);
>> -
>> +
>> typedef struct {
>> IClassFactory IClassFactory_iface;
>> REFCLSID rclsid;
>> @@ -702,7 +702,7 @@ static HRESULT WINAPI DSCF_CreateInstance(
>> *ppobj = NULL;
>> return This->pfnCreateInstance(riid, ppobj);
>> }
>> -
>> +
>> static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
>> {
>> IClassFactoryImpl *This = impl_from_IClassFactory(iface);
>
> The only changes to this file are whitespace changes. I'd exclude
> these changes when you resend.
>
>> diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
>> index 1cf6daa..68f6242 100644
>> --- a/dlls/dsound/dsound_private.h
>> +++ b/dlls/dsound/dsound_private.h
>> @@ -202,7 +202,7 @@ struct IDirectSoundBufferImpl
>> };
>>
>> float get_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel) DECLSPEC_HIDDEN;
>> -void put_mono2stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value) DECLSPEC_HIDDEN;
>> +void put_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value) DECLSPEC_HIDDEN;
>>
>> HRESULT IDirectSoundBufferImpl_Create(
>> DirectSoundDevice *device,
>> @@ -288,7 +288,7 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex)
>> LONG capped_refcount_dec(LONG *ref) DECLSPEC_HIDDEN;
>>
>> /* duplex.c */
>> -
>> +
>> HRESULT DSOUND_FullDuplexCreate(REFIID riid, LPDIRECTSOUNDFULLDUPLEX* ppDSFD) DECLSPEC_HIDDEN;
>>
>> /* mixer.c */
>> @@ -305,7 +305,7 @@ DWORD CALLBACK DSOUND_mixthread(void *ptr) DECLSPEC_HIDDEN;
>> void DSOUND_Calc3DBuffer(IDirectSoundBufferImpl *dsb) DECLSPEC_HIDDEN;
>>
>> /* capture.c */
>> -
>> +
>> HRESULT DSOUND_CaptureCreate(REFIID riid, LPDIRECTSOUNDCAPTURE *ppDSC) DECLSPEC_HIDDEN;
>> HRESULT DSOUND_CaptureCreate8(REFIID riid, LPDIRECTSOUNDCAPTURE8 *ppDSC8) DECLSPEC_HIDDEN;
>>
>
> Likewise, more unneeded whitespace changes.
>
>> diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
>> index 93e2b8d..fa6b05a 100644
>> --- a/dlls/winealsa.drv/mmdevdrv.c
>> +++ b/dlls/winealsa.drv/mmdevdrv.c
>> @@ -1544,7 +1544,7 @@ static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient *iface,
>> goto exit;
>> }
>> if(max > 8)
>> - max = 2;
>> + max = 8;
>> if(fmt->nChannels > max){
>> hr = S_FALSE;
>> closest->nChannels = max;
>> @@ -1649,8 +1649,8 @@ static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient *iface,
>> goto exit;
>> }
>>
>> - if(max_channels > 2)
>> - fmt->Format.nChannels = 2;
>> + if(max_channels > 8)
>> + fmt->Format.nChannels = 8;
>> else
>> fmt->Format.nChannels = max_channels;
>>
>
> This needs to be a separate patch. Also, winealsa's multi-channel
> handling needs a closer examination. My current thinking is
> GetMixFormat should just return some sane, supported default (2ch,
> 48kHz, 16bps, or less as supported). It seems impossible to detect
> what the ALSA device actually supports, so we probably shouldn't even
> try like we do now.
>
>> diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
>> index b374b53..e86ed08 100644
>> --- a/dlls/winepulse.drv/mmdevdrv.c
>> +++ b/dlls/winepulse.drv/mmdevdrv.c
>> @@ -1050,13 +1050,13 @@ static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt)
>> This->ss.format = PA_SAMPLE_INVALID;
>> switch(fmt->wFormatTag) {
>> case WAVE_FORMAT_IEEE_FLOAT:
>> - if (!fmt->nChannels || fmt->nChannels > 2 || fmt->wBitsPerSample != 32)
>> + if (!fmt->nChannels || fmt->nChannels > 8 || fmt->wBitsPerSample != 32)
>> break;
>> This->ss.format = PA_SAMPLE_FLOAT32LE;
>> pa_channel_map_init_auto(&This->map, fmt->nChannels, PA_CHANNEL_MAP_ALSA);
>> break;
>> case WAVE_FORMAT_PCM:
>> - if (!fmt->nChannels || fmt->nChannels > 2)
>> + if (!fmt->nChannels || fmt->nChannels > 8)
>> break;
>> if (fmt->wBitsPerSample == 8)
>> This->ss.format = PA_SAMPLE_U8;
>
> Wine doesn't have a PulseAudio driver yet, so this chunk won't apply.
>
> Andrew
-------------- next part --------------
From 4d454498c8f4cf6593d4afe405fb602ebb0e2722 Mon Sep 17 00:00:00 2001
From: Donny Yang <xiao.tai.lang.de.email at gmail.com>
Date: Sat, 14 Jul 2012 16:40:07 +1000
Subject: dsound: Add detection of output format and upmixing from mono to any
number of channels
---
dlls/dsound/dsound.c | 19 +++++++++++++++++++
dlls/dsound/dsound_convert.c | 8 +++++---
dlls/dsound/dsound_private.h | 2 +-
dlls/dsound/mixer.c | 7 +++----
dlls/dsound/primary.c | 28 ++++++++++++++++++++++++++++
5 files changed, 56 insertions(+), 8 deletions(-)
diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c
index e401725..7d75760 100644
--- a/dlls/dsound/dsound.c
+++ b/dlls/dsound/dsound.c
@@ -1382,6 +1382,7 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG
GUID devGUID;
DirectSoundDevice *device;
IMMDevice *mmdevice;
+ WAVEFORMATEX *pwfx;
TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID));
@@ -1492,6 +1493,24 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG
} else
WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
+ hr = IAudioClient_GetMixFormat(device->client, &pwfx);
+ if(FAILED(hr)){
+ WARN("IAudioClient_GetMixFormat failed: %08x; Falling back to default output format\n", hr);
+ }else{
+ DWORD oldPriolevel = device->priolevel;
+ device->priolevel = DSSCL_WRITEPRIMARY;
+ hr = primarybuffer_SetFormat(device, pwfx);
+ device->priolevel = oldPriolevel;
+ CoTaskMemFree(pwfx);
+ if(FAILED(hr)){
+ HeapFree(GetProcessHeap(), 0, device);
+ LeaveCriticalSection(&DSOUND_renderers_lock);
+ IMMDevice_Release(mmdevice);
+ WARN("primarybuffer_SetFormat failed: %08x\n", hr);
+ return hr;
+ }
+ }
+
*ppDevice = device;
list_add_tail(&DSOUND_renderers, &device->entry);
diff --git a/dlls/dsound/dsound_convert.c b/dlls/dsound/dsound_convert.c
index d3d686a..a8c929c 100644
--- a/dlls/dsound/dsound_convert.c
+++ b/dlls/dsound/dsound_convert.c
@@ -159,10 +159,12 @@ void putieee32(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, floa
*fbuf = value;
}
-void put_mono2stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
+void put_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
{
- dsb->put_aux(dsb, pos, 0, value);
- dsb->put_aux(dsb, pos, 1, value);
+ /* XXX: Is this how Windows does this? */
+ DWORD c;
+ for (c = 0; c < dsb->device->pwfx->nChannels; ++c)
+ dsb->put_aux(dsb, pos, c, value);
}
void mixieee32(float *src, float *dst, unsigned samples)
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index 1cf6daa..68f6242 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -202,7 +202,7 @@ struct IDirectSoundBufferImpl
};
float get_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel) DECLSPEC_HIDDEN;
-void put_mono2stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value) DECLSPEC_HIDDEN;
+void put_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value) DECLSPEC_HIDDEN;
HRESULT IDirectSoundBufferImpl_Create(
DirectSoundDevice *device,
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 13eb03d..547c666 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -157,7 +157,7 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
else if (ichannels == 1)
{
dsb->mix_channels = 1;
- dsb->put = put_mono2stereo;
+ dsb->put = put_mono;
}
else if (ochannels == 1)
{
@@ -166,9 +166,8 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
}
else
{
- if (ichannels > 2)
- FIXME("Conversion from %u to %u channels is not implemented, falling back to stereo\n", ichannels, ochannels);
- dsb->mix_channels = 2;
+ FIXME("Conversion from %u to %u channels is not implemented, falling back to %u channels\n", ichannels, ochannels, ochannels);
+ dsb->mix_channels = ochannels;
}
}
diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c
index cfe674e..9fd1ff2 100644
--- a/dlls/dsound/primary.c
+++ b/dlls/dsound/primary.c
@@ -484,6 +484,34 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passe
goto done;
opened:
+ switch(device->pwfx->nChannels){
+ case 0:
+ device->speaker_config = DSSPEAKER_DIRECTOUT;
+ break;
+ case 1:
+ device->speaker_config = DSSPEAKER_MONO;
+ break;
+ case 2:
+ case 3:
+ device->speaker_config = DSSPEAKER_COMBINED(DSSPEAKER_STEREO, DSSPEAKER_GEOMETRY_WIDE);
+ break;
+ case 4:
+ case 5:
+ device->speaker_config = DSSPEAKER_QUAD;
+ break;
+ case 6:
+ case 7:
+ device->speaker_config = DSSPEAKER_5POINT1_SURROUND;
+ break;
+ case 8:
+ device->speaker_config = DSSPEAKER_7POINT1_SURROUND;
+ break;
+ default:
+ FIXME("Unknown speaker configuration: %u\n", device->pwfx->nChannels);
+ device->speaker_config = DSSPEAKER_DIRECTOUT;
+ break;
+ }
+
err = DSOUND_PrimaryOpen(device);
if (err != DS_OK) {
WARN("DSOUND_PrimaryOpen failed\n");
--
1.7.9.5
More information about the wine-devel
mailing list