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