Fix DirectSound's primary buffer Volume and Pan handling
Francois Gouget
fgouget at codeweavers.com
Fri Jul 23 12:10:36 CDT 2004
This patch is identical to the one I sent to wine-devel:
http://www.winehq.org/hypermail/wine-devel/2004/07/0305.html
Noone objected so I guess the patch is good to go<g>.
---
Currently the way DirectSound handles Volume and Pan is wrong:
* whenever a primary buffer is created (e.g. in DirectSoundCreate())
we initialize an internal variable that sets the volume to the max and
pan to the center.
* we also call IDsDriverBuffer_SetVolumePan() or waveOutSetVolume() to
set the system volume to that value. On my machine this has the same
effect as
amixer set PCM 100%
* we ignore changes in the volume or balance made outside of
DirectSound. So if the user does:
amixer set PCM 50%
And then the application calls GetVolume() it will still get 0 (i.e.
max volume)
This is not what happens on Windows. On Windows primary buffers don't
have their internal volume. Instead SetVolume()/SetPan() and
GetVolume()/GetPan() query directly the underlying volume.
I hacked the dsound.c test to verify this. Basically, now it does the
following:
* it calls GetVolume() and GetPan() and prints the results
* it sleeps for 20 seconds
* during these 20 seconds, start Windows' mixer control, and change
the 'Wave Sound' (translated from French) volume and/or balance.
* the test then calls GetVolume() and GetPan() and prints the results
again
Running the test we can see that the second time DierctSound returns the
new volume:
dsound.c:771:Primary volume=-566 pan=0
dsound.c:776:Primary volume=-566 pan=0
--- the test sleeps here while we change the volume ---
dsound.c:782:Primary volume=-1438 pan=0
See the patch for the hacked dsound.c test in my previous email.
The patch removes the primary buffer's 'volpan' variable and instead
uses waveOutGetVolume() on dsound->hwo to get the current volume. This
is then converted from the 'AmplificationFactor' format to the
traditional DirectSound Volume/Pan format by DSOUND_AmpFactorToVolPan().
Settnig the volume goes through IDsDriverBuffer_SetVolumePan() or
waveOutSetVolume() as before.
Also of note is that DSOUND_AmpFactorToVolPan() uses round(). It's
straneg that there's no problem getting ceil() or floor() but round() is
more tricky. On Linux _GNU_SOURCE needs to be defined before math.h is
included in order to get it. I hope this will be ok on FreeBSD too. If
not we'll figure out something.
Changelog:
* dlls/dsound/dsound.c
dlls/dsound/dsound_private.h
dlls/dsound/mixer.c
dlls/dsound/primary.c
Francois Gouget <fgouget at codeweavers.com>
Fix the volume and balance of primary buffers so it is the system's
volume and balance, like on Windows.
--
Francois Gouget
fgouget at codeweavers.com
-------------- next part --------------
Index: dlls/dsound/dsound.c
===================================================================
RCS file: /var/cvs/wine/dlls/dsound/dsound.c,v
retrieving revision 1.7
diff -u -r1.7 dsound.c
--- dlls/dsound/dsound.c 22 Jul 2004 19:42:11 -0000 1.7
+++ dlls/dsound/dsound.c 22 Jul 2004 23:11:47 -0000
@@ -922,10 +922,6 @@
pDS->drvcaps.dwPrimaryBuffers = 1;
}
- pDS->volpan.lVolume = 0;
- pDS->volpan.lPan = 0;
- DSOUND_RecalcVolPan(&(pDS->volpan));
-
InitializeCriticalSection(&(pDS->mixlock));
RtlInitializeResource(&(pDS->lock));
Index: dlls/dsound/dsound_private.h
===================================================================
RCS file: /var/cvs/wine/dlls/dsound/dsound_private.h,v
retrieving revision 1.19
diff -u -r1.19 dsound_private.h
--- dlls/dsound/dsound_private.h 21 Jul 2004 03:23:13 -0000 1.19
+++ dlls/dsound/dsound_private.h 21 Jul 2004 12:27:47 -0000
@@ -91,7 +91,6 @@
IDirectSoundBufferImpl** buffers;
RTL_RWLOCK lock;
CRITICAL_SECTION mixlock;
- DSVOLUMEPAN volpan;
PrimaryBufferImpl* primary;
DSBUFFERDESC dsbd;
DWORD speaker_config;
@@ -450,6 +449,7 @@
extern IClassFactoryImpl DSOUND_FULLDUPLEX_CF;
void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan);
+void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan);
void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb);
/* primary.c */
Index: dlls/dsound/mixer.c
===================================================================
RCS file: /var/cvs/wine/dlls/dsound/mixer.c,v
retrieving revision 1.20
diff -u -r1.20 mixer.c
--- dlls/dsound/mixer.c 12 Jan 2004 21:02:22 -0000 1.20
+++ dlls/dsound/mixer.c 21 Jul 2004 11:21:55 -0000
@@ -20,6 +20,7 @@
*/
#include "config.h"
+#define _GNU_SOURCE /* for round() in math.h */
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
@@ -54,6 +55,7 @@
double temp;
TRACE("(%p)\n",volpan);
+ TRACE("Vol=%ld Pan=%ld\n", volpan->lVolume, volpan->lPan);
/* the AmpFactors are expressed in 16.16 fixed point */
volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 0xffff);
/* FIXME: dwPan{Left|Right}AmpFactor */
@@ -67,6 +69,39 @@
TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
}
+void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan)
+{
+ double left,right;
+ TRACE("(%p)\n",volpan);
+
+ TRACE("left=%lx, right=%lx\n",volpan->dwTotalLeftAmpFactor,volpan->dwTotalRightAmpFactor);
+ if (volpan->dwTotalLeftAmpFactor==0)
+ left=-10000;
+ else
+ left=600 * log(((double)volpan->dwTotalLeftAmpFactor) / 0xffff) / log(2);
+ if (volpan->dwTotalRightAmpFactor==0)
+ right=-10000;
+ else
+ right=600 * log(((double)volpan->dwTotalRightAmpFactor) / 0xffff) / log(2);
+ if (left<right)
+ {
+ volpan->lVolume=round(right);
+ volpan->dwVolAmpFactor=volpan->dwTotalRightAmpFactor;
+ }
+ else
+ {
+ volpan->lVolume=round(left);
+ volpan->dwVolAmpFactor=volpan->dwTotalLeftAmpFactor;
+ }
+ if (volpan->lVolume < -10000)
+ volpan->lVolume=-10000;
+ volpan->lPan=round(right-left);
+ if (volpan->lPan < -10000)
+ volpan->lPan=-10000;
+
+ TRACE("Vol=%ld Pan=%ld\n", volpan->lVolume, volpan->lPan);
+}
+
void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
{
DWORD sw;
Index: dlls/dsound/primary.c
===================================================================
RCS file: /var/cvs/wine/dlls/dsound/primary.c,v
retrieving revision 1.25
diff -u -r1.25 primary.c
--- dlls/dsound/primary.c 13 Jul 2004 23:35:09 -0000 1.25
+++ dlls/dsound/primary.c 21 Jul 2004 11:06:06 -0000
@@ -74,8 +74,6 @@
HRESULT err = DS_OK;
TRACE("(%p)\n",This);
- DSOUND_RecalcVolPan(&(This->volpan));
-
/* are we using waveOut stuff? */
if (!This->driver) {
LPBYTE newbuf;
@@ -135,11 +133,6 @@
}
if ((err == DS_OK) && (merr != DS_OK))
err = merr;
-
- if (!err) {
- DWORD vol = (This->volpan.dwTotalLeftAmpFactor & 0xffff) | (This->volpan.dwTotalRightAmpFactor << 16);
- err = mmErr(waveOutSetVolume(This->hwo, vol));
- }
} else {
if (!This->hwbuf) {
err = IDsDriver_CreateSoundBuffer(This->driver,&(This->wfx),
@@ -154,7 +147,6 @@
if (dsound->state == STATE_PLAYING) dsound->state = STATE_STARTING;
else if (dsound->state == STATE_STOPPING) dsound->state = STATE_STOPPED;
}
- err = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
}
return err;
@@ -458,7 +450,8 @@
) {
ICOM_THIS(PrimaryBufferImpl,iface);
IDirectSoundImpl* dsound = This->dsound;
- LONG oldVol;
+ DWORD ampfactors;
+ DSVOLUMEPAN volpan;
TRACE("(%p,%ld)\n",This,vol);
@@ -475,24 +468,26 @@
/* **** */
EnterCriticalSection(&(dsound->mixlock));
- oldVol = dsound->volpan.lVolume;
- dsound->volpan.lVolume = vol;
- DSOUND_RecalcVolPan(&dsound->volpan);
-
- if (vol != oldVol) {
- if (dsound->hwbuf) {
- HRESULT hres;
- hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &(dsound->volpan));
- if (hres != DS_OK) {
- LeaveCriticalSection(&(dsound->mixlock));
- WARN("IDsDriverBuffer_SetVolumePan failed\n");
- return hres;
- }
- } else {
- DWORD vol = (dsound->volpan.dwTotalLeftAmpFactor & 0xffff) | (dsound->volpan.dwTotalRightAmpFactor << 16);
- waveOutSetVolume(dsound->hwo, vol);
- }
- }
+ waveOutGetVolume(dsound->hwo, &factors);
+ volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
+ volpan.dwTotalRightAmpFactor=ampfactors >> 16;
+ DSOUND_AmpFactorToVolPan(&volpan);
+ if (vol != volpan.lVolume) {
+ volpan.lVolume=vol;
+ DSOUND_RecalcVolPan(&volpan);
+ if (dsound->hwbuf) {
+ HRESULT hres;
+ hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &volpan);
+ if (hres != DS_OK) {
+ LeaveCriticalSection(&(dsound->mixlock));
+ WARN("IDsDriverBuffer_SetVolumePan failed\n");
+ return hres;
+ }
+ } else {
+ ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
+ waveOutSetVolume(dsound->hwo, ampfactors);
+ }
+ }
LeaveCriticalSection(&(dsound->mixlock));
/* **** */
@@ -504,6 +499,8 @@
LPDIRECTSOUNDBUFFER8 iface,LPLONG vol
) {
ICOM_THIS(PrimaryBufferImpl,iface);
+ DWORD ampfactors;
+ DSVOLUMEPAN volpan;
TRACE("(%p,%p)\n",This,vol);
if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
@@ -516,7 +513,11 @@
return DSERR_INVALIDPARAM;
}
- *vol = This->dsound->volpan.lVolume;
+ waveOutGetVolume(dsound->hwo, &factors);
+ volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
+ volpan.dwTotalRightAmpFactor=ampfactors >> 16;
+ DSOUND_AmpFactorToVolPan(&volpan);
+ *vol = volpan.lVolume;
return DS_OK;
}
@@ -769,7 +770,8 @@
) {
ICOM_THIS(PrimaryBufferImpl,iface);
IDirectSoundImpl* dsound = This->dsound;
- LONG oldPan;
+ DWORD ampfactors;
+ DSVOLUMEPAN volpan;
TRACE("(%p,%ld)\n",This,pan);
@@ -786,25 +788,27 @@
/* **** */
EnterCriticalSection(&(dsound->mixlock));
- oldPan = dsound->volpan.lPan;
- dsound->volpan.lPan = pan;
- DSOUND_RecalcVolPan(&dsound->volpan);
-
- if (pan != oldPan) {
- if (dsound->hwbuf) {
- HRESULT hres;
- hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &(dsound->volpan));
- if (hres != DS_OK) {
- LeaveCriticalSection(&(dsound->mixlock));
- WARN("IDsDriverBuffer_SetVolumePan failed\n");
- return hres;
- }
- }
- else {
- DWORD vol = (dsound->volpan.dwTotalLeftAmpFactor & 0xffff) | (dsound->volpan.dwTotalRightAmpFactor << 16);
- waveOutSetVolume(dsound->hwo, vol);
- }
- }
+ waveOutGetVolume(dsound->hwo, &factors);
+ volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
+ volpan.dwTotalRightAmpFactor=ampfactors >> 16;
+ DSOUND_AmpFactorToVolPan(&volpan);
+ if (pan != volpan.lPan) {
+ volpan.lPan=pan;
+ DSOUND_RecalcVolPan(&volpan);
+ if (dsound->hwbuf) {
+ HRESULT hres;
+ hres = IDsDriverBuffer_SetVolumePan(dsound->hwbuf, &volpan);
+ if (hres != DS_OK) {
+ LeaveCriticalSection(&(dsound->mixlock));
+ WARN("IDsDriverBuffer_SetVolumePan failed\n");
+ return hres;
+ }
+ }
+ else {
+ ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
+ waveOutSetVolume(dsound->hwo, ampfactors);
+ }
+ }
LeaveCriticalSection(&(dsound->mixlock));
/* **** */
@@ -816,6 +820,8 @@
LPDIRECTSOUNDBUFFER8 iface,LPLONG pan
) {
ICOM_THIS(PrimaryBufferImpl,iface);
+ DWORD ampfactors;
+ DSVOLUMEPAN volpan;
TRACE("(%p,%p)\n",This,pan);
if (!(This->dsound->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
@@ -828,8 +834,11 @@
return DSERR_INVALIDPARAM;
}
- *pan = This->dsound->volpan.lPan;
-
+ waveOutGetVolume(dsound->hwo, &factors);
+ volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
+ volpan.dwTotalRightAmpFactor=ampfactors >> 16;
+ DSOUND_AmpFactorToVolPan(&volpan);
+ *pan = volpan.lPan;
return DS_OK;
}
More information about the wine-patches
mailing list