From f2983c79e5d742ef711a026179277c6d9cde7f39 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 3 Mar 2008 20:18:04 -0800 Subject: [PATCH] dsound: Add an option to mix sound buffers in the mixer thread, and do it automatically for big buffers There was no upper limit on the memory allocated for shadow buffers, this would cause some applications to use more then a gigabyte of memory on shadow buffers, this fixes it by resampling either in application context or mixer context. --- dlls/dsound/buffer.c | 8 ++-- dlls/dsound/dsound_main.c | 61 +++++++++++++++------------------ dlls/dsound/dsound_private.h | 5 ++- dlls/dsound/mixer.c | 77 +++++++++++++++++++++++++++++------------ dlls/dsound/primary.c | 2 +- 5 files changed, 89 insertions(+), 64 deletions(-) diff --git a/dlls/dsound/buffer.c b/dlls/dsound/buffer.c index 02b763e..bb08f4a 100644 --- a/dlls/dsound/buffer.c +++ b/dlls/dsound/buffer.c @@ -296,7 +296,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency( This->freqAdjust = ((DWORD64)This->freq << DSOUND_FREQSHIFT) / This->device->pwfx->nSamplesPerSec; This->nAvgBytesPerSec = freq * This->pwfx->nBlockAlign; DSOUND_RecalcFormat(This); - DSOUND_MixToTemporary(This, 0, This->buflen); + DSOUND_MixToTemporary(This, 0, This->buflen, FALSE); } RtlReleaseResource(&This->lock); @@ -733,9 +733,9 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Unlock( { RtlAcquireResourceShared(&iter->lock, TRUE); if (x1) - DSOUND_MixToTemporary(iter, (DWORD_PTR)p1 - (DWORD_PTR)iter->buffer->memory, x1); + DSOUND_MixToTemporary(iter, (DWORD_PTR)p1 - (DWORD_PTR)iter->buffer->memory, x1, FALSE); if (x2) - DSOUND_MixToTemporary(iter, 0, x2); + DSOUND_MixToTemporary(iter, 0, x2, FALSE); RtlReleaseResource(&iter->lock); } RtlReleaseResource(&This->device->buffer_list_lock); @@ -1223,7 +1223,7 @@ HRESULT IDirectSoundBufferImpl_Duplicate( dsb->secondary = NULL; dsb->tmp_buffer = NULL; DSOUND_RecalcFormat(dsb); - DSOUND_MixToTemporary(dsb, 0, dsb->buflen); + DSOUND_MixToTemporary(dsb, 0, dsb->buflen, FALSE); /* variable sized struct so calculate size based on format */ size = sizeof(WAVEFORMATEX) + pdsb->pwfx->cbSize; diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index 53f27a7..2a1d1ca 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -57,10 +57,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(dsound); -#define DS_HEL_BUFLEN 0x8000 /* HEL: The buffer length of the emulated buffer */ -#define DS_SND_QUEUE_MAX 10 /* max number of fragments to prebuffer, each fragment is approximately 10 ms long */ -#define DS_SND_QUEUE_MIN 6 /* If the minimum of prebuffered fragments go below this, forcibly take all locks to prevent underruns */ - DirectSoundDevice* DSOUND_renderer[MAXWAVEDRIVERS]; GUID DSOUND_renderer_guids[MAXWAVEDRIVERS]; GUID DSOUND_capture_guids[MAXWAVEDRIVERS]; @@ -92,10 +88,12 @@ HRESULT mmErr(UINT err) } } +/* All default settings, you most likely don't want to touch these, see wiki on UsefulRegistryKeys */ int ds_emuldriver = 0; -int ds_hel_buflen = DS_HEL_BUFLEN; -int ds_snd_queue_max = DS_SND_QUEUE_MAX; -int ds_snd_queue_min = DS_SND_QUEUE_MIN; +int ds_hel_buflen = 32768; +int ds_snd_queue_max = 10; +int ds_snd_queue_min = 6; +int ds_snd_shadow_maxsize = 2; int ds_hw_accel = DS_HW_ACCEL_FULL; int ds_default_playback = 0; int ds_default_capture = 0; @@ -173,43 +171,38 @@ void setup_dsound_options(void) } if (!get_config_key( hkey, appkey, "DefaultPlayback", buffer, MAX_PATH )) - ds_default_playback = atoi(buffer); + ds_default_playback = atoi(buffer); + + if (!get_config_key( hkey, appkey, "MaxShadowSize", buffer, MAX_PATH )) + ds_snd_shadow_maxsize = atoi(buffer); if (!get_config_key( hkey, appkey, "DefaultCapture", buffer, MAX_PATH )) - ds_default_capture = atoi(buffer); + ds_default_capture = atoi(buffer); if (!get_config_key( hkey, appkey, "DefaultSampleRate", buffer, MAX_PATH )) - ds_default_sample_rate = atoi(buffer); + ds_default_sample_rate = atoi(buffer); if (!get_config_key( hkey, appkey, "DefaultBitsPerSample", buffer, MAX_PATH )) - ds_default_bits_per_sample = atoi(buffer); + ds_default_bits_per_sample = atoi(buffer); if (appkey) RegCloseKey( appkey ); if (hkey) RegCloseKey( hkey ); - if (ds_emuldriver) - WARN("ds_emuldriver = %d (default=0)\n",ds_emuldriver); - if (ds_hel_buflen != DS_HEL_BUFLEN) - WARN("ds_hel_buflen = %d (default=%d)\n",ds_hel_buflen ,DS_HEL_BUFLEN); - if (ds_snd_queue_max != DS_SND_QUEUE_MAX) - WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX); - if (ds_snd_queue_min != DS_SND_QUEUE_MIN) - WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN); - if (ds_hw_accel != DS_HW_ACCEL_FULL) - WARN("ds_hw_accel = %s (default=Full)\n", - ds_hw_accel==DS_HW_ACCEL_FULL ? "Full" : - ds_hw_accel==DS_HW_ACCEL_STANDARD ? "Standard" : - ds_hw_accel==DS_HW_ACCEL_BASIC ? "Basic" : - ds_hw_accel==DS_HW_ACCEL_EMULATION ? "Emulation" : - "Unknown"); - if (ds_default_playback != 0) - WARN("ds_default_playback = %d (default=0)\n",ds_default_playback); - if (ds_default_capture != 0) - WARN("ds_default_capture = %d (default=0)\n",ds_default_playback); - if (ds_default_sample_rate != 44100) - WARN("ds_default_sample_rate = %d (default=44100)\n",ds_default_sample_rate); - if (ds_default_bits_per_sample != 16) - WARN("ds_default_bits_per_sample = %d (default=16)\n",ds_default_bits_per_sample); + TRACE("ds_emuldriver = %d\n", ds_emuldriver); + TRACE("ds_hel_buflen = %d\n", ds_hel_buflen); + TRACE("ds_snd_queue_max = %d\n", ds_snd_queue_max); + TRACE("ds_snd_queue_min = %d\n", ds_snd_queue_min); + TRACE("ds_hw_accel = %s\n", + ds_hw_accel==DS_HW_ACCEL_FULL ? "Full" : + ds_hw_accel==DS_HW_ACCEL_STANDARD ? "Standard" : + ds_hw_accel==DS_HW_ACCEL_BASIC ? "Basic" : + ds_hw_accel==DS_HW_ACCEL_EMULATION ? "Emulation" : + "Unknown"); + TRACE("ds_default_playback = %d\n", ds_default_playback); + TRACE("ds_default_capture = %d\n", ds_default_playback); + TRACE("ds_default_sample_rate = %d\n", ds_default_sample_rate); + TRACE("ds_default_bits_per_sample = %d\n", ds_default_bits_per_sample); + TRACE("ds_snd_shadow_maxsize = %d\n", ds_snd_shadow_maxsize); } static const char * get_device_id(LPCGUID pGuid) diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index 004ed45..262e9ec 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -35,6 +35,7 @@ extern int ds_emuldriver; extern int ds_hel_buflen; extern int ds_snd_queue_max; extern int ds_snd_queue_min; +extern int ds_snd_shadow_maxsize; extern int ds_hw_accel; extern int ds_default_playback; extern int ds_default_capture; @@ -181,7 +182,7 @@ struct IDirectSoundBufferImpl DSVOLUMEPAN volpan; DSBUFFERDESC dsbd; /* used for frequency conversion (PerfectPitch) */ - ULONG freqneeded, freqAdjust, freqAcc, freqAccNext; + ULONG freqneeded, freqAdjust, freqAcc, freqAccNext, resampleinmixer; /* used for mixing */ DWORD primary_mixpos, buf_mixpos, sec_mixpos; @@ -449,7 +450,7 @@ void DSOUND_CheckEvent(const IDirectSoundBufferImpl *dsb, DWORD playpos, int len void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan); void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan); void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb); -void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen); +void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen, BOOL inmixer); DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos, DWORD secmixpos, DWORD* overshot); void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c index 384709a..b01f989 100644 --- a/dlls/dsound/mixer.c +++ b/dlls/dsound/mixer.c @@ -193,6 +193,8 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb) dsb->convert = convertbpp[dsb->pwfx->wBitsPerSample/8 - 1][dsb->device->pwfx->wBitsPerSample/8 - 1]; + dsb->resampleinmixer = FALSE; + if (needremix) { if (needresample) @@ -200,8 +202,12 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb) else dsb->tmp_buffer_len = dsb->buflen / bAlign * pAlign; dsb->max_buffer_len = dsb->tmp_buffer_len; - dsb->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, dsb->max_buffer_len); - FillMemory(dsb->tmp_buffer, dsb->tmp_buffer_len, dsb->device->pwfx->wBitsPerSample == 8 ? 128 : 0); + if ((dsb->max_buffer_len <= dsb->device->buflen || dsb->max_buffer_len < ds_snd_shadow_maxsize * 1024 * 1024) && ds_snd_shadow_maxsize >= 0) + dsb->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, dsb->max_buffer_len); + if (dsb->tmp_buffer) + FillMemory(dsb->tmp_buffer, dsb->tmp_buffer_len, dsb->device->pwfx->wBitsPerSample == 8 ? 128 : 0); + else + dsb->resampleinmixer = TRUE; } else dsb->max_buffer_len = dsb->tmp_buffer_len = dsb->buflen; dsb->buf_mixpos = DSOUND_secpos_to_bufpos(dsb, dsb->sec_mixpos, 0, NULL); @@ -313,41 +319,52 @@ static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2) * writepos = Starting position of changed buffer * len = number of bytes to resample from writepos * - * NOTE: writepos + len <= buflen, This function doesn't loop! + * NOTE: writepos + len <= buflen. When called by mixer, MixOne makes sure of this. */ -void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len) +void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len, BOOL inmixer) { INT i, size; BYTE *ibp, *obp, *ibp_begin, *obp_begin; INT iAdvance = dsb->pwfx->nBlockAlign; INT oAdvance = dsb->device->pwfx->nBlockAlign; - DWORD freqAcc, target_writepos, overshot; + DWORD freqAcc, target_writepos = 0, overshot, maxlen; - if (!dsb->tmp_buffer) - /* Nothing to do, already ideal format */ + /* We resample only when needed */ + if ((dsb->tmp_buffer && inmixer) || (!dsb->tmp_buffer && !inmixer) || dsb->resampleinmixer != inmixer) return; + assert(writepos + len <= dsb->buflen); + if (inmixer && writepos + len < dsb->buflen) + len += dsb->pwfx->nBlockAlign; + + maxlen = DSOUND_secpos_to_bufpos(dsb, len, 0, NULL); + ibp = dsb->buffer->memory + writepos; ibp_begin = dsb->buffer->memory; - obp_begin = dsb->tmp_buffer; + if (!inmixer) + obp_begin = dsb->tmp_buffer; + else if (dsb->device->tmp_buffer_len < maxlen || !dsb->device->tmp_buffer) + { + dsb->device->tmp_buffer_len = maxlen; + if (dsb->device->tmp_buffer) + dsb->device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsb->device->tmp_buffer, maxlen); + else + dsb->device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, maxlen); + obp_begin = dsb->device->tmp_buffer; + } + else + obp_begin = dsb->device->tmp_buffer; TRACE("(%p, %p)\n", dsb, ibp); - /* Check for the best case */ - if ((dsb->freq == dsb->device->pwfx->nSamplesPerSec) && - (dsb->pwfx->wBitsPerSample == dsb->device->pwfx->wBitsPerSample) && - (dsb->pwfx->nChannels == dsb->device->pwfx->nChannels)) { - obp = dsb->tmp_buffer + writepos; - /* Why would we need a temporary buffer if we do best case? */ - FIXME("(%p) Why do we resample for best case??? Bad!!\n", dsb); - CopyMemory(obp, ibp, len); - return; - } /* Check for same sample rate */ if (dsb->freq == dsb->device->pwfx->nSamplesPerSec) { TRACE("(%p) Same sample rate %d = primary %d\n", dsb, dsb->freq, dsb->device->pwfx->nSamplesPerSec); - obp = dsb->tmp_buffer + writepos/iAdvance*oAdvance; + obp = obp_begin; + if (!inmixer) + obp += writepos/iAdvance*oAdvance; + for (i = 0; i < len; i += iAdvance) { cp_fields(dsb, ibp, obp); ibp += iAdvance; @@ -375,7 +392,10 @@ void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DW TRACE("Overshot: %d, freqAcc: %04x\n", overshot, freqAcc); } - obp = dsb->tmp_buffer + target_writepos; + if (!inmixer) + obp = obp_begin + target_writepos; + else obp = obp_begin; + /* FIXME: Small problem here when we're overwriting buf_mixpos, it then STILL uses old freqAcc, not sure if it matters or not */ while (size > 0) { cp_fields(dsb, ibp, obp); @@ -393,14 +413,17 @@ void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DW /** Apply volume to the given soundbuffer from (primary) position writepos and length len * Returns: NULL if no volume needs to be applied * or else a memory handle that holds 'len' volume adjusted buffer */ -static LPBYTE DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, DWORD writepos, INT len) +static LPBYTE DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, INT len) { INT i; BYTE *bpc; INT16 *bps, *mems; DWORD vLeft, vRight; INT nChannels = dsb->device->pwfx->nChannels; - LPBYTE mem = (dsb->tmp_buffer ? dsb->tmp_buffer : dsb->buffer->memory)+writepos; + LPBYTE mem = (dsb->tmp_buffer ? dsb->tmp_buffer : dsb->buffer->memory) + dsb->buf_mixpos; + + if (dsb->resampleinmixer) + mem = dsb->device->tmp_buffer; TRACE("(%p,%d)\n",dsb,len); TRACE("left = %x, right = %x\n", dsb->volpan.dwTotalLeftAmpFactor, @@ -425,12 +448,15 @@ static LPBYTE DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, DWORD writepos, if (dsb->device->tmp_buffer_len < len || !dsb->device->tmp_buffer) { + /* If we just resampled in DSOUND_MixToTemporary, we shouldn't need to resize here */ + assert(!dsb->resampleinmixer); dsb->device->tmp_buffer_len = len; if (dsb->device->tmp_buffer) dsb->device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsb->device->tmp_buffer, len); else dsb->device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, len); } + bpc = dsb->device->tmp_buffer; bps = (INT16 *)bpc; mems = (INT16 *)mem; @@ -494,8 +520,13 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO len -= len % nBlockAlign; /* data alignment */ } + /* Resample buffer to temporary buffer specifically allocated for this purpose, if needed */ + DSOUND_MixToTemporary(dsb, dsb->sec_mixpos, DSOUND_bufpos_to_secpos(dsb, dsb->buf_mixpos+len) - dsb->sec_mixpos, TRUE); + if (dsb->resampleinmixer) + ibuf = dsb->device->tmp_buffer; + /* Apply volume if needed */ - volbuf = DSOUND_MixerVol(dsb, dsb->buf_mixpos, len); + volbuf = DSOUND_MixerVol(dsb, len); if (volbuf) ibuf = volbuf; diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c index eec681c..e5d31c6 100644 --- a/dlls/dsound/primary.c +++ b/dlls/dsound/primary.c @@ -547,7 +547,7 @@ HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec; DSOUND_RecalcFormat((*dsb)); - DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen); + DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen, FALSE); (*dsb)->primary_mixpos = 0; RtlReleaseResource(&(*dsb)->lock); -- 1.5.4.1