dsound: Add detection of output format and allow a maximum of 8 channels
Andrew Eikum
aeikum at codeweavers.com
Sat Jul 14 13:38:30 CDT 2012
On Sat, Jul 14, 2012 at 05:00:02PM +1000, Donny Yang wrote:
> 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.
>
No, you can call GetMixFormat with an IAudioClient that hasn't been
initialized.
> Also, I'm new to this, so is this the correct way to resend a patch?
>
No, you'll have to resend the patch to wine-patches. It's probably
best to resend the entire series, and of course be sure to test your
changes before sending :)
Thanks,
Andrew
> 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
> 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