diff --git a/dlls/dsound/Makefile.in b/dlls/dsound/Makefile.in index 5addc95..ef61d2c 100644 --- a/dlls/dsound/Makefile.in +++ b/dlls/dsound/Makefile.in @@ -6,13 +6,13 @@ C_SRCS = \ buffer.c \ capture.c \ dsound.c \ - dsound_convert.c \ dsound_main.c \ duplex.c \ mixer.c \ primary.c \ propset.c \ regsvr.c \ + resample.c \ sound3d.c RC_SRCS = version.rc diff --git a/dlls/dsound/buffer.c b/dlls/dsound/buffer.c index 0e9096a..c596710 100644 --- a/dlls/dsound/buffer.c +++ b/dlls/dsound/buffer.c @@ -292,10 +292,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency( oldFreq = This->freq; This->freq = freq; if (freq != oldFreq) { - 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, FALSE); } RtlReleaseResource(&This->lock); @@ -316,7 +313,6 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Play( This->playflags = flags; if (This->state == STATE_STOPPED && !This->hwbuf) { - This->leadin = TRUE; This->state = STATE_STARTING; } else if (This->state == STATE_STOPPING) This->state = STATE_PLAYING; @@ -393,7 +389,6 @@ static ULONG WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface) } } - HeapFree(GetProcessHeap(), 0, This->tmp_buffer); HeapFree(GetProcessHeap(), 0, This->notifies); HeapFree(GetProcessHeap(), 0, This->pwfx); HeapFree(GetProcessHeap(), 0, This); @@ -418,7 +413,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition( return hres; } } else { - DWORD pos = This->sec_mixpos; + DWORD pos = This->inpos; /* sanity */ if (pos >= This->buflen){ @@ -569,7 +564,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Lock( } else { if (writecursor+writebytes <= This->buflen) { *(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor; - if (This->sec_mixpos >= writecursor && This->sec_mixpos < writecursor + writebytes && This->state == STATE_PLAYING) + if (This->inpos >= writecursor && This->inpos < writecursor + writebytes && This->state == STATE_PLAYING) WARN("Overwriting mixing position, case 1\n"); *audiobytes1 = writebytes; if (lplpaudioptr2) @@ -583,13 +578,13 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Lock( DWORD remainder = writebytes + writecursor - This->buflen; *(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor; *audiobytes1 = This->buflen-writecursor; - if (This->sec_mixpos >= writecursor && This->sec_mixpos < writecursor + writebytes && This->state == STATE_PLAYING) + if (This->inpos >= writecursor && This->inpos < writecursor + writebytes && This->state == STATE_PLAYING) WARN("Overwriting mixing position, case 2\n"); if (lplpaudioptr2) *(LPBYTE*)lplpaudioptr2 = This->buffer->memory; if (audiobytes2) *audiobytes2 = writebytes-(This->buflen-writecursor); - if (audiobytes2 && This->sec_mixpos < remainder && This->state == STATE_PLAYING) + if (audiobytes2 && This->inpos < remainder && This->state == STATE_PLAYING) WARN("Overwriting mixing position, case 3\n"); TRACE("Locked %p(%i bytes) and %p(%i bytes) writecursor=%d\n", *(LPBYTE*)lplpaudioptr1, *audiobytes1, lplpaudioptr2 ? *(LPBYTE*)lplpaudioptr2 : NULL, audiobytes2 ? *audiobytes2: 0, writecursor); } @@ -612,25 +607,23 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition( /* **** */ RtlAcquireResourceExclusive(&This->lock, TRUE); - oldpos = This->sec_mixpos; + oldpos = This->inpos; /* start mixing from this new location instead */ newpos %= This->buflen; newpos -= newpos%This->pwfx->nBlockAlign; - This->sec_mixpos = newpos; + This->inpos = newpos; + This->infrac = 0; /* at this point, do not attempt to reset buffers, mess with primary mix position, or anything like that to reduce latancy. The data already prebuffered cannot be changed */ /* position HW buffer if applicable, else just start mixing from new location instead */ if (This->hwbuf) { - hres = IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos); + hres = IDsDriverBuffer_SetPosition(This->hwbuf, DSOUND_secpos_to_bufpos(This, newpos)); if (hres != DS_OK) WARN("IDsDriverBuffer_SetPosition failed\n"); } - else if (oldpos != newpos) - /* FIXME: Perhaps add a call to DSOUND_MixToTemporary here? Not sure it's needed */ - This->buf_mixpos = DSOUND_secpos_to_bufpos(This, newpos, 0, NULL); RtlReleaseResource(&This->lock); /* **** */ @@ -702,7 +695,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetPan( static HRESULT WINAPI IDirectSoundBufferImpl_Unlock( LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2 ) { - IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface, *iter; + IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface; HRESULT hres = DS_OK; TRACE("(%p,%p,%d,%p,%d)\n", This,p1,x1,p2,x2); @@ -719,29 +712,6 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Unlock( RtlReleaseResource(&This->lock); /* **** */ - if (!p2) - x2 = 0; - - if (!This->hwbuf && (x1 || x2)) - { - RtlAcquireResourceShared(&This->device->buffer_list_lock, TRUE); - LIST_FOR_EACH_ENTRY(iter, &This->buffer->buffers, IDirectSoundBufferImpl, entry ) - { - RtlAcquireResourceShared(&iter->lock, TRUE); - if (x1) - { - if(x1 + (DWORD_PTR)p1 - (DWORD_PTR)iter->buffer->memory > iter->buflen) - hres = DSERR_INVALIDPARAM; - else - DSOUND_MixToTemporary(iter, (DWORD_PTR)p1 - (DWORD_PTR)iter->buffer->memory, x1, FALSE); - } - if (x2) - DSOUND_MixToTemporary(iter, 0, x2, FALSE); - RtlReleaseResource(&iter->lock); - } - RtlReleaseResource(&This->device->buffer_list_lock); - } - return hres; } @@ -1078,16 +1048,9 @@ HRESULT IDirectSoundBufferImpl_Create( list_add_head(&dsb->buffer->buffers, &dsb->entry); FillMemory(dsb->buffer->memory, dsb->buflen, dsbd->lpwfxFormat->wBitsPerSample == 8 ? 128 : 0); - /* It's not necessary to initialize values to zero since */ - /* we allocated this structure with HEAP_ZERO_MEMORY... */ - dsb->buf_mixpos = dsb->sec_mixpos = 0; dsb->state = STATE_STOPPED; - dsb->freqAdjust = ((DWORD64)dsb->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec; - dsb->nAvgBytesPerSec = dsb->freq * - dsbd->lpwfxFormat->nBlockAlign; - - /* calculate fragment size and write lead */ + /* calculate new format values */ DSOUND_RecalcFormat(dsb); if (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D) { @@ -1212,14 +1175,12 @@ HRESULT IDirectSoundBufferImpl_Duplicate( list_add_head(&dsb->buffer->buffers, &dsb->entry); dsb->ref = 0; dsb->state = STATE_STOPPED; - dsb->buf_mixpos = dsb->sec_mixpos = 0; + dsb->inpos = dsb->infrac = 0; dsb->device = device; dsb->ds3db = NULL; dsb->iks = NULL; /* FIXME? */ dsb->secondary = NULL; - dsb->tmp_buffer = NULL; DSOUND_RecalcFormat(dsb); - DSOUND_MixToTemporary(dsb, 0, dsb->buflen, FALSE); RtlInitializeResource(&dsb->lock); @@ -1227,7 +1188,6 @@ HRESULT IDirectSoundBufferImpl_Duplicate( hres = DirectSoundDevice_AddBuffer(device, dsb); if (hres != DS_OK) { RtlDeleteResource(&dsb->lock); - HeapFree(GetProcessHeap(),0,dsb->tmp_buffer); list_remove(&dsb->entry); dsb->buffer->ref--; HeapFree(GetProcessHeap(),0,dsb->pwfx); diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c index 83636c2..1241db6 100644 --- a/dlls/dsound/dsound.c +++ b/dlls/dsound/dsound.c @@ -1276,8 +1276,6 @@ ULONG DirectSoundDevice_Release(DirectSoundDevice * device) DSOUND_renderer[device->drvdesc.dnDevNode] = NULL; - HeapFree(GetProcessHeap(), 0, device->tmp_buffer); - HeapFree(GetProcessHeap(), 0, device->mix_buffer); if (device->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) HeapFree(GetProcessHeap(), 0, device->buffer); RtlDeleteResource(&device->buffer_list_lock); diff --git a/dlls/dsound/dsound_convert.c b/dlls/dsound/dsound_convert.c deleted file mode 100644 index 0a6e474..0000000 --- a/dlls/dsound/dsound_convert.c +++ /dev/null @@ -1,435 +0,0 @@ -/* DirectSound format conversion and mixing routines - * - * Copyright 2007 Maarten Lankhorst - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -/* 8 bits is unsigned, the rest is signed. - * First I tried to reuse existing stuff from alsa-lib, after that - * didn't work, I gave up and just went for individual hacks. - * - * 24 bit is expensive to do, due to unaligned access. - * In dlls/winex11.drv/dib_convert.c convert_888_to_0888_asis there is a way - * around it, but I'm happy current code works, maybe something for later. - * - * The ^ 0x80 flips the signed bit, this is the conversion from - * signed (-128.. 0.. 127) to unsigned (0...255) - * This is only temporary: All 8 bit data should be converted to signed. - * then when fed to the sound card, it should be converted to unsigned again. - * - * Sound is LITTLE endian - */ - -#include "config.h" - -#include - -#define NONAMELESSSTRUCT -#define NONAMELESSUNION -#include "windef.h" -#include "winbase.h" -#include "mmsystem.h" -#include "winternl.h" -#include "wine/debug.h" -#include "dsound.h" -#include "dsdriver.h" -#include "dsound_private.h" - -WINE_DEFAULT_DEBUG_CHANNEL(dsound); - -#ifdef WORDS_BIGENDIAN -#define le16(x) RtlUshortByteSwap((x)) -#define le32(x) RtlUlongByteSwap((x)) -#else -#define le16(x) (x) -#define le32(x) (x) -#endif - -static inline void src_advance(const void **src, UINT stride, INT *count, UINT *freqAcc, UINT adj) -{ - *freqAcc += adj; - if (*freqAcc >= (1 << DSOUND_FREQSHIFT)) - { - ULONG adv = (*freqAcc >> DSOUND_FREQSHIFT); - *freqAcc &= (1 << DSOUND_FREQSHIFT) - 1; - *(const char **)src += adv * stride; - *count -= adv; - } -} - -static void convert_8_to_8 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - *(BYTE *)dst = *(const BYTE *)src; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_8_to_16 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - WORD dest = *(const BYTE *)src, *dest16 = dst; - *dest16 = le16(dest * 257 - 32768); - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_8_to_24 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - BYTE dest = *(const BYTE *)src; - BYTE *dest24 = dst; - dest24[0] = dest; - dest24[1] = dest; - dest24[2] = dest - 0x80; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_8_to_32 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - DWORD dest = *(const BYTE *)src, *dest32 = dst; - *dest32 = le32(dest * 16843009 - 2147483648U); - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_16_to_8 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - BYTE *dst8 = dst; - *dst8 = (le16(*(const WORD *)src)) / 256; - *dst8 -= 0x80; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_16_to_16 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - *(WORD *)dst = *(const WORD *)src; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_16_to_24 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - WORD dest = le16(*(const WORD *)src); - BYTE *dest24 = dst; - - dest24[0] = dest / 256; - dest24[1] = dest; - dest24[2] = dest / 256; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_16_to_32 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - DWORD dest = *(const WORD *)src, *dest32 = dst; - *dest32 = dest * 65537; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_24_to_8 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - BYTE *dst8 = dst; - *dst8 = ((const BYTE *)src)[2]; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_24_to_16 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - WORD *dest16 = dst; - const BYTE *source = src; - *dest16 = le16(source[2] * 256 + source[1]); - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_24_to_24 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - BYTE *dest24 = dst; - const BYTE *src24 = src; - - dest24[0] = src24[0]; - dest24[1] = src24[1]; - dest24[2] = src24[2]; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_24_to_32 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - DWORD *dest32 = dst; - const BYTE *source = src; - *dest32 = le32(source[2] * 16777217 + source[1] * 65536 + source[0] * 256); - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_32_to_8 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - BYTE *dst8 = dst; - *dst8 = (le32(*(const DWORD *)src) / 16777216); - *dst8 -= 0x80; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_32_to_16 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - WORD *dest16 = dst; - *dest16 = le16(le32(*(const DWORD *)src) / 65536); - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_32_to_24 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - DWORD dest = le32(*(const DWORD *)src); - BYTE *dest24 = dst; - - dest24[0] = dest / 256; - dest24[1] = dest / 65536; - dest24[2] = dest / 16777216; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -static void convert_32_to_32 (const void *src, void *dst, UINT src_stride, - UINT dst_stride, INT count, UINT freqAcc, UINT adj) -{ - while (count > 0) - { - DWORD *dest = dst; - *dest = *(const DWORD *)src; - - dst = (char *)dst + dst_stride; - src_advance(&src, src_stride, &count, &freqAcc, adj); - } -} - -const bitsconvertfunc convertbpp[4][4] = { - { convert_8_to_8, convert_8_to_16, convert_8_to_24, convert_8_to_32 }, - { convert_16_to_8, convert_16_to_16, convert_16_to_24, convert_16_to_32 }, - { convert_24_to_8, convert_24_to_16, convert_24_to_24, convert_24_to_32 }, - { convert_32_to_8, convert_32_to_16, convert_32_to_24, convert_32_to_32 }, -}; - -static void mix8(signed char *src, INT *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - while (len--) - /* 8-bit WAV is unsigned, it's here converted to signed, normalize function will convert it back again */ - *(dst++) += (signed char)((BYTE)*(src++) - (BYTE)0x80); -} - -static void mix16(SHORT *src, INT *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - len /= 2; - while (len--) - { - *dst += le16(*src); - ++dst; ++src; - } -} - -static void mix24(BYTE *src, INT *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - len /= 3; - while (len--) - { - DWORD field; - field = ((DWORD)src[2] << 16) + ((DWORD)src[1] << 8) + (DWORD)src[0]; - if (src[2] & 0x80) - field |= 0xFF000000U; - *(dst++) += field; - ++src; - } -} - -static void mix32(INT *src, LONGLONG *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - len /= 4; - while (len--) - *(dst++) += le32(*(src++)); -} - -const mixfunc mixfunctions[4] = { - (mixfunc)mix8, - (mixfunc)mix16, - (mixfunc)mix24, - (mixfunc)mix32 -}; - -static void norm8(INT *src, signed char *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - while (len--) - { - *dst = (*src) + 0x80; - if (*src < -0x80) - *dst = 0; - else if (*src > 0x7f) - *dst = 0xff; - ++dst; - ++src; - } -} - -static void norm16(INT *src, SHORT *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - len /= 2; - while (len--) - { - *dst = le16(*src); - if (*src <= -0x8000) - *dst = le16(0x8000); - else if (*src > 0x7fff) - *dst = le16(0x7fff); - ++dst; - ++src; - } -} - -static void norm24(INT *src, BYTE *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - len /= 3; - while (len--) - { - if (*src <= -0x800000) - { - dst[0] = 0; - dst[1] = 0; - dst[2] = 0x80; - } - else if (*src > 0x7fffff) - { - dst[0] = 0xff; - dst[1] = 0xff; - dst[2] = 0x7f; - } - else - { - dst[0] = *src; - dst[1] = *src >> 8; - dst[2] = *src >> 16; - } - ++dst; - ++src; - } -} - -static void norm32(LONGLONG *src, INT *dst, unsigned len) -{ - TRACE("%p - %p %d\n", src, dst, len); - len /= 4; - while (len--) - { - *dst = le32(*src); - if (*src <= -(LONGLONG)0x80000000) - *dst = le32(0x80000000); - else if (*src > 0x7fffffff) - *dst = le32(0x7fffffff); - ++dst; - ++src; - } -} - -const normfunc normfunctions[4] = { - (normfunc)norm8, - (normfunc)norm16, - (normfunc)norm24, - (normfunc)norm32, -}; diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index 8e264c7..c81d2aa 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -95,7 +95,7 @@ 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_sample_rate = 44100; +int ds_default_sample_rate = 48000; int ds_default_bits_per_sample = 16; static int ds_default_playback; static int ds_default_capture; @@ -672,9 +672,12 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) DisableThreadLibraryCalls(hInstDLL); /* Increase refcount on dsound by 1 */ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)hInstDLL, &hInstDLL); + /* Create resampler stuff */ + DSOUND_CreateFIR(); break; case DLL_PROCESS_DETACH: TRACE("DLL_PROCESS_DETACH\n"); + DSOUND_DeleteFIR(); break; default: TRACE("UNKNOWN REASON\n"); diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index 1b25ddd..4ab8049 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -63,13 +63,10 @@ typedef struct SecondaryBufferImpl SecondaryBufferImpl; typedef struct DirectSoundDevice DirectSoundDevice; typedef struct DirectSoundCaptureDevice DirectSoundCaptureDevice; -/* dsound_convert.h */ -typedef void (*bitsconvertfunc)(const void *, void *, UINT, UINT, INT, UINT, UINT); -extern const bitsconvertfunc convertbpp[4][4]; -typedef void (*mixfunc)(const void *, void *, unsigned); -extern const mixfunc mixfunctions[4]; -typedef void (*normfunc)(const void *, void *, unsigned); -extern const normfunc normfunctions[4]; +/* resample.c */ +DWORD DSOUND_PullBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen); +void DSOUND_CreateFIR(void); +void DSOUND_DeleteFIR(void); /***************************************************************************** * IDirectSoundDevice implementation structure @@ -98,14 +95,9 @@ struct DirectSoundDevice PrimaryBufferImpl* primary; DSBUFFERDESC dsbd; DWORD speaker_config; - LPBYTE tmp_buffer, mix_buffer; - DWORD tmp_buffer_len, mix_buffer_len; DSVOLUMEPAN volpan; - mixfunc mixfunction; - normfunc normfunction; - /* DirectSound3DListener fields */ IDirectSound3DListenerImpl* listener; DS3DLISTENER ds3dl; @@ -171,17 +163,17 @@ struct IDirectSoundBufferImpl PIDSDRIVERBUFFER hwbuf; PWAVEFORMATEX pwfx; BufferMemory* buffer; - LPBYTE tmp_buffer; - DWORD playflags,state,leadin; + DWORD playflags,state; DWORD writelead,buflen; DWORD nAvgBytesPerSec; - DWORD freq, tmp_buffer_len, max_buffer_len; + DWORD freq; DSVOLUMEPAN volpan; DSBUFFERDESC dsbd; - /* used for frequency conversion (PerfectPitch) */ - ULONG freqneeded, freqAdjust, freqAcc, freqAccNext, resampleinmixer; - /* used for mixing */ - DWORD primary_mixpos, buf_mixpos, sec_mixpos; + + /* resampler fields */ + DWORD outfreq, inpos, infrac; + DOUBLE outfreq_1; + /* IDirectSoundNotifyImpl fields */ IDirectSoundNotifyImpl* notify; @@ -197,7 +189,6 @@ struct IDirectSoundBufferImpl /* IKsPropertySet fields */ IKsBufferPropertySetImpl* iks; - bitsconvertfunc convert; struct list entry; }; @@ -385,13 +376,11 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave); HRESULT DSOUND_FullDuplexCreate(REFIID riid, LPDIRECTSOUNDFULLDUPLEX* ppDSFD); /* mixer.c */ -DWORD DSOUND_bufpos_to_mixpos(const DirectSoundDevice* device, DWORD pos); 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, BOOL inmixer); -DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos, DWORD secmixpos, DWORD* overshot); +DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos); void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c index 185df6e..6b58eab 100644 --- a/dlls/dsound/mixer.c +++ b/dlls/dsound/mixer.c @@ -41,36 +41,40 @@ WINE_DEFAULT_DEBUG_CHANNEL(dsound); void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan) { double temp; - TRACE("(%p)\n",volpan); - TRACE("Vol=%d Pan=%d\n", volpan->lVolume, volpan->lPan); + /* 0.01dB units: vol=100*20*log(amp); amp = 10^(vol/2000) */ + temp = pow(10.0, volpan->lVolume / 2e3); + /* 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 */ + volpan->dwVolAmpFactor = (DWORD)(temp*(DOUBLE)0x10000); + volpan->dwTotalLeftAmpFactor = volpan->dwVolAmpFactor; + volpan->dwTotalRightAmpFactor = volpan->dwVolAmpFactor; - /* FIXME: use calculated vol and pan ampfactors */ - temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0)); - volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff); - temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0)); - volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff); + if (volpan->lPan > 0) /* left channel attenuated */ + volpan->dwTotalLeftAmpFactor = (DWORD)(temp*(DOUBLE)0x10000 + * pow(10.0, -volpan->lPan / 2e3)); - TRACE("left = %x, right = %x\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor); + if (volpan->lPan < 0) /* right channel attenuated */ + volpan->dwTotalRightAmpFactor = (DWORD)(temp*(DOUBLE)0x10000 + * pow(10.0, volpan->lPan / 2e3)); } void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan) { double left,right; - TRACE("(%p)\n",volpan); - TRACE("left=%x, right=%x\n",volpan->dwTotalLeftAmpFactor,volpan->dwTotalRightAmpFactor); - if (volpan->dwTotalLeftAmpFactor==0) - left=-10000; +#define LOG10(x) log(x)/log(10) + + if (volpan->dwTotalLeftAmpFactor == 0) + left = -10000; else - left=600 * log(((double)volpan->dwTotalLeftAmpFactor) / 0xffff) / log(2); - if (volpan->dwTotalRightAmpFactor==0) - right=-10000; + left = 2e3 * LOG10((DOUBLE)volpan->dwTotalLeftAmpFactor / 0x10000); + + if (volpan->dwTotalRightAmpFactor == 0) + right =- 10000; else - right=600 * log(((double)volpan->dwTotalRightAmpFactor) / 0xffff) / log(2); + right= 2e3 * LOG10((DOUBLE)volpan->dwTotalRightAmpFactor / 0x10000); + if (leftlVolume=right; @@ -90,127 +94,35 @@ void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan) TRACE("Vol=%d Pan=%d\n", volpan->lVolume, volpan->lPan); } -/** Convert a primary buffer position to a pointer position for device->mix_buffer - * device: DirectSoundDevice for which to calculate - * pos: Primary buffer position to converts - * Returns: Offset for mix_buffer - */ -DWORD DSOUND_bufpos_to_mixpos(const DirectSoundDevice* device, DWORD pos) -{ - DWORD ret = pos * 32 / device->pwfx->wBitsPerSample; - if (device->pwfx->wBitsPerSample == 32) - ret *= 2; - return ret; -} - -/* NOTE: Not all secpos have to always be mapped to a bufpos, other way around is always the case - * DWORD64 is used here because a single DWORD wouldn't be big enough to fit the freqAcc for big buffers - */ /** This function converts a 'native' sample pointer to a resampled pointer that fits for primary - * secmixpos is used to decide which freqAcc is needed - * overshot tells what the 'actual' secpos is now (optional) */ -DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos, DWORD secmixpos, DWORD* overshot) +DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos) { - DWORD64 framelen = secpos / dsb->pwfx->nBlockAlign; - DWORD64 freqAdjust = dsb->freqAdjust; - DWORD64 acc, freqAcc; - - if (secpos < secmixpos) - freqAcc = dsb->freqAccNext; - else freqAcc = dsb->freqAcc; - acc = (framelen << DSOUND_FREQSHIFT) + (freqAdjust - 1 - freqAcc); - acc /= freqAdjust; - if (overshot) - { - DWORD64 oshot = acc * freqAdjust + freqAcc; - assert(oshot >= framelen << DSOUND_FREQSHIFT); - oshot -= framelen << DSOUND_FREQSHIFT; - *overshot = (DWORD)oshot; - assert(*overshot < dsb->freqAdjust); - } - return (DWORD)acc * dsb->device->pwfx->nBlockAlign; -} + DWORD insample = secpos / dsb->pwfx->nBlockAlign; + DWORD outsample = insample * dsb->outfreq / dsb->freq; + DWORD bufpos = outsample * dsb->device->pwfx->nBlockAlign; -/** Convert a resampled pointer that fits for primary to a 'native' sample pointer - * freqAccNext is used here rather than freqAcc: In case the app wants to fill up to - * the play position it won't overwrite it - */ -static DWORD DSOUND_bufpos_to_secpos(const IDirectSoundBufferImpl *dsb, DWORD bufpos) -{ - DWORD oAdv = dsb->device->pwfx->nBlockAlign, iAdv = dsb->pwfx->nBlockAlign, pos; - DWORD64 framelen; - DWORD64 acc; - - framelen = bufpos/oAdv; - acc = framelen * (DWORD64)dsb->freqAdjust + (DWORD64)dsb->freqAccNext; - acc = acc >> DSOUND_FREQSHIFT; - pos = (DWORD)acc * iAdv; - if (pos >= dsb->buflen) - /* Because of differences between freqAcc and freqAccNext, this might happen */ - pos = dsb->buflen - iAdv; - TRACE("Converted %d/%d to %d/%d\n", bufpos, dsb->tmp_buffer_len, pos, dsb->buflen); - return pos; -} + bufpos %= dsb->device->buflen; -/** - * Move freqAccNext to freqAcc, and find new values for buffer length and freqAccNext - */ -static void DSOUND_RecalcFreqAcc(IDirectSoundBufferImpl *dsb) -{ - if (!dsb->freqneeded) return; - dsb->freqAcc = dsb->freqAccNext; - dsb->tmp_buffer_len = DSOUND_secpos_to_bufpos(dsb, dsb->buflen, 0, &dsb->freqAccNext); - TRACE("New freqadjust: %04x, new buflen: %d\n", dsb->freqAccNext, dsb->tmp_buffer_len); + return bufpos; } /** - * Recalculate the size for temporary buffer, and new writelead * Should be called when one of the following things occur: * - Primary buffer format is changed * - This buffer format (frequency) is changed - * - * After this, DSOUND_MixToTemporary(dsb, 0, dsb->buflen) should - * be called to refill the temporary buffer with data. */ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb) { - BOOL needremix = TRUE, needresample = (dsb->freq != dsb->device->pwfx->nSamplesPerSec); - DWORD bAlign = dsb->pwfx->nBlockAlign, pAlign = dsb->device->pwfx->nBlockAlign; + dsb->nAvgBytesPerSec = dsb->freq * dsb->pwfx->nBlockAlign; - TRACE("(%p)\n",dsb); + dsb->outfreq = dsb->device->pwfx->nSamplesPerSec; + dsb->outfreq_1 = 1.0 / dsb->outfreq; /* calculate the 10ms write lead */ dsb->writelead = (dsb->freq / 100) * dsb->pwfx->nBlockAlign; - if ((dsb->pwfx->wBitsPerSample == dsb->device->pwfx->wBitsPerSample) && - (dsb->pwfx->nChannels == dsb->device->pwfx->nChannels) && !needresample) - needremix = FALSE; - HeapFree(GetProcessHeap(), 0, dsb->tmp_buffer); - dsb->tmp_buffer = NULL; - dsb->max_buffer_len = dsb->freqAcc = dsb->freqAccNext = 0; - dsb->freqneeded = needresample; - - dsb->convert = convertbpp[dsb->pwfx->wBitsPerSample/8 - 1][dsb->device->pwfx->wBitsPerSample/8 - 1]; - - dsb->resampleinmixer = FALSE; - - if (needremix) - { - if (needresample) - DSOUND_RecalcFreqAcc(dsb); - else - dsb->tmp_buffer_len = dsb->buflen / bAlign * pAlign; - dsb->max_buffer_len = dsb->tmp_buffer_len; - 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); + dsb->inpos = dsb->infrac = 0; /* reset resampler pointer */ } /** @@ -267,34 +179,6 @@ void DSOUND_CheckEvent(const IDirectSoundBufferImpl *dsb, DWORD playpos, int len } } -/** - * Copy a single frame from the given input buffer to the given output buffer. - * Translate 8 <-> 16 bits and mono <-> stereo - */ -static inline void cp_fields(const IDirectSoundBufferImpl *dsb, const BYTE *ibuf, BYTE *obuf, - UINT istride, UINT ostride, UINT count, UINT freqAcc, UINT adj) -{ - DirectSoundDevice *device = dsb->device; - INT istep = dsb->pwfx->wBitsPerSample / 8, ostep = device->pwfx->wBitsPerSample / 8; - - if (device->pwfx->nChannels == dsb->pwfx->nChannels || - (device->pwfx->nChannels == 2 && dsb->pwfx->nChannels == 6)) { - dsb->convert(ibuf, obuf, istride, ostride, count, freqAcc, adj); - if (device->pwfx->nChannels == 2) - dsb->convert(ibuf + istep, obuf + ostep, istride, ostride, count, freqAcc, adj); - } - - if (device->pwfx->nChannels == 1 && dsb->pwfx->nChannels == 2) - { - dsb->convert(ibuf, obuf, istride, ostride, count, freqAcc, adj); - } - - if (device->pwfx->nChannels == 2 && dsb->pwfx->nChannels == 1) - { - dsb->convert(ibuf, obuf, istride, ostride, count, freqAcc, adj); - dsb->convert(ibuf, obuf + ostep, istride, ostride, count, freqAcc, adj); - } -} /** * Calculate the distance between two buffer offsets, taking wraparound @@ -311,335 +195,6 @@ static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2) return buflen + ptr1 - ptr2; } } -/** - * Mix at most the given amount of data into the allocated temporary buffer - * of the given secondary buffer, starting from the dsb's first currently - * unsampled frame (writepos), translating frequency (pitch), stereo/mono - * and bits-per-sample so that it is ideal for the primary buffer. - * Doesn't perform any mixing - this is a straight copy/convert operation. - * - * dsb = the secondary buffer - * writepos = Starting position of changed buffer - * len = number of bytes to resample from writepos - * - * NOTE: writepos + len <= buflen. When called by mixer, MixOne makes sure of this. - */ -void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len, BOOL inmixer) -{ - INT size; - BYTE *ibp, *obp, *obp_begin; - INT iAdvance = dsb->pwfx->nBlockAlign; - INT oAdvance = dsb->device->pwfx->nBlockAlign; - DWORD freqAcc, target_writepos = 0, overshot, maxlen; - - /* 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; - 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); - size = len / iAdvance; - - /* 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 = obp_begin; - if (!inmixer) - obp += writepos/iAdvance*oAdvance; - - cp_fields(dsb, ibp, obp, iAdvance, oAdvance, size, 0, 1 << DSOUND_FREQSHIFT); - return; - } - - /* Mix in different sample rates */ - TRACE("(%p) Adjusting frequency: %d -> %d\n", dsb, dsb->freq, dsb->device->pwfx->nSamplesPerSec); - - target_writepos = DSOUND_secpos_to_bufpos(dsb, writepos, dsb->sec_mixpos, &freqAcc); - overshot = freqAcc >> DSOUND_FREQSHIFT; - if (overshot) - { - if (overshot >= size) - return; - size -= overshot; - writepos += overshot * iAdvance; - if (writepos >= dsb->buflen) - return; - ibp = dsb->buffer->memory + writepos; - freqAcc &= (1 << DSOUND_FREQSHIFT) - 1; - TRACE("Overshot: %d, freqAcc: %04x\n", overshot, freqAcc); - } - - 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 */ - cp_fields(dsb, ibp, obp, iAdvance, oAdvance, size, freqAcc, dsb->freqAdjust); -} - -/** 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, 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) + 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, - dsb->volpan.dwTotalRightAmpFactor); - - if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) && - (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) && - !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D)) - return NULL; /* Nothing to do */ - - if (nChannels != 1 && nChannels != 2) - { - FIXME("There is no support for %d channels\n", nChannels); - return NULL; - } - - if (dsb->device->pwfx->wBitsPerSample != 8 && dsb->device->pwfx->wBitsPerSample != 16) - { - FIXME("There is no support for %d bpp\n", dsb->device->pwfx->wBitsPerSample); - return NULL; - } - - 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; - vLeft = dsb->volpan.dwTotalLeftAmpFactor; - if (nChannels > 1) - vRight = dsb->volpan.dwTotalRightAmpFactor; - else - vRight = vLeft; - - switch (dsb->device->pwfx->wBitsPerSample) { - case 8: - /* 8-bit WAV is unsigned, but we need to operate */ - /* on signed data for this to work properly */ - for (i = 0; i < len-1; i+=2) { - *(bpc++) = (((*(mem++) - 128) * vLeft) >> 16) + 128; - *(bpc++) = (((*(mem++) - 128) * vRight) >> 16) + 128; - } - if (len % 2 == 1 && nChannels == 1) - *(bpc++) = (((*(mem++) - 128) * vLeft) >> 16) + 128; - break; - case 16: - /* 16-bit WAV is signed -- much better */ - for (i = 0; i < len-3; i += 4) { - *(bps++) = (*(mems++) * vLeft) >> 16; - *(bps++) = (*(mems++) * vRight) >> 16; - } - if (len % 4 == 2 && nChannels == 1) - *(bps++) = ((INT)*(mems++) * vLeft) >> 16; - break; - } - return dsb->device->tmp_buffer; -} - -/** - * Mix (at most) the given number of bytes into the given position of the - * device buffer, from the secondary buffer "dsb" (starting at the current - * mix position for that buffer). - * - * Returns the number of bytes actually mixed into the device buffer. This - * will match fraglen unless the end of the secondary buffer is reached - * (and it is not looping). - * - * dsb = the secondary buffer to mix from - * writepos = position (offset) in device buffer to write at - * fraglen = number of bytes to mix - */ -static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen) -{ - INT len = fraglen, ilen; - BYTE *ibuf = (dsb->tmp_buffer ? dsb->tmp_buffer : dsb->buffer->memory) + dsb->buf_mixpos, *volbuf; - DWORD oldpos, mixbufpos; - - TRACE("buf_mixpos=%d/%d sec_mixpos=%d/%d\n", dsb->buf_mixpos, dsb->tmp_buffer_len, dsb->sec_mixpos, dsb->buflen); - TRACE("(%p,%d,%d)\n",dsb,writepos,fraglen); - - assert(dsb->buf_mixpos + len <= dsb->tmp_buffer_len); - - if (len % dsb->device->pwfx->nBlockAlign) { - INT nBlockAlign = dsb->device->pwfx->nBlockAlign; - ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign); - 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, len); - if (volbuf) - ibuf = volbuf; - - mixbufpos = DSOUND_bufpos_to_mixpos(dsb->device, writepos); - /* Now mix the temporary buffer into the devices main buffer */ - if ((writepos + len) <= dsb->device->buflen) - dsb->device->mixfunction(ibuf, dsb->device->mix_buffer + mixbufpos, len); - else - { - DWORD todo = dsb->device->buflen - writepos; - dsb->device->mixfunction(ibuf, dsb->device->mix_buffer + mixbufpos, todo); - dsb->device->mixfunction(ibuf + todo, dsb->device->mix_buffer, len - todo); - } - - oldpos = dsb->sec_mixpos; - dsb->buf_mixpos += len; - - if (dsb->buf_mixpos >= dsb->tmp_buffer_len) { - if (dsb->buf_mixpos > dsb->tmp_buffer_len) - ERR("Mixpos (%u) past buflen (%u), capping...\n", dsb->buf_mixpos, dsb->tmp_buffer_len); - if (dsb->playflags & DSBPLAY_LOOPING) { - dsb->buf_mixpos -= dsb->tmp_buffer_len; - } else if (dsb->buf_mixpos >= dsb->tmp_buffer_len) { - dsb->buf_mixpos = dsb->sec_mixpos = 0; - dsb->state = STATE_STOPPED; - } - DSOUND_RecalcFreqAcc(dsb); - } - - dsb->sec_mixpos = DSOUND_bufpos_to_secpos(dsb, dsb->buf_mixpos); - ilen = DSOUND_BufPtrDiff(dsb->buflen, dsb->sec_mixpos, oldpos); - /* check for notification positions */ - if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY && - dsb->state != STATE_STARTING) { - DSOUND_CheckEvent(dsb, oldpos, ilen); - } - - /* increase mix position */ - dsb->primary_mixpos += len; - if (dsb->primary_mixpos >= dsb->device->buflen) - dsb->primary_mixpos -= dsb->device->buflen; - return len; -} - -/** - * Mix some frames from the given secondary buffer "dsb" into the device - * primary buffer. - * - * dsb = the secondary buffer - * playpos = the current play position in the device buffer (primary buffer) - * writepos = the current safe-to-write position in the device buffer - * mixlen = the maximum number of bytes in the primary buffer to mix, from the - * current writepos. - * - * Returns: the number of bytes beyond the writepos that were mixed. - */ -static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen) -{ - /* The buffer's primary_mixpos may be before or after the device - * buffer's mixpos, but both must be ahead of writepos. */ - DWORD primary_done; - - TRACE("(%p,%d,%d)\n",dsb,writepos,mixlen); - TRACE("writepos=%d, buf_mixpos=%d, primary_mixpos=%d, mixlen=%d\n", writepos, dsb->buf_mixpos, dsb->primary_mixpos, mixlen); - TRACE("looping=%d, leadin=%d, buflen=%d\n", dsb->playflags, dsb->leadin, dsb->tmp_buffer_len); - - /* If leading in, only mix about 20 ms, and 'skip' mixing the rest, for more fluid pointer advancement */ - if (dsb->leadin && dsb->state == STATE_STARTING) - { - if (mixlen > 2 * dsb->device->fraglen) - { - dsb->primary_mixpos += mixlen - 2 * dsb->device->fraglen; - dsb->primary_mixpos %= dsb->device->buflen; - } - } - dsb->leadin = FALSE; - - /* calculate how much pre-buffering has already been done for this buffer */ - primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos); - - /* sanity */ - if(mixlen < primary_done) - { - /* Should *NEVER* happen */ - ERR("Fatal error. Under/Overflow? primary_done=%d, mixpos=%d/%d (%d/%d), primary_mixpos=%d, writepos=%d, mixlen=%d\n", primary_done,dsb->buf_mixpos,dsb->tmp_buffer_len,dsb->sec_mixpos, dsb->buflen, dsb->primary_mixpos, writepos, mixlen); - dsb->primary_mixpos = writepos + mixlen; - dsb->primary_mixpos %= dsb->device->buflen; - return mixlen; - } - - /* take into account already mixed data */ - mixlen -= primary_done; - - TRACE("primary_done=%d, mixlen (primary) = %i\n", primary_done, mixlen); - - if (!mixlen) - return primary_done; - - /* First try to mix to the end of the buffer if possible - * Theoretically it would allow for better optimization - */ - if (mixlen + dsb->buf_mixpos >= dsb->tmp_buffer_len) - { - DWORD newmixed, mixfirst = dsb->tmp_buffer_len - dsb->buf_mixpos; - newmixed = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixfirst); - mixlen -= newmixed; - - if (dsb->playflags & DSBPLAY_LOOPING) - while (newmixed && mixlen) - { - mixfirst = (dsb->tmp_buffer_len < mixlen ? dsb->tmp_buffer_len : mixlen); - newmixed = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixfirst); - mixlen -= newmixed; - } - } - else DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixlen); - - /* re-calculate the primary done */ - primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos); - - TRACE("new primary_mixpos=%d, total mixed data=%d\n", dsb->primary_mixpos, primary_done); - - /* Report back the total prebuffered amount for this buffer */ - return primary_done; -} /** * For a DirectSoundDevice, go through all the currently playing buffers and @@ -685,17 +240,15 @@ static DWORD DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos DSOUND_CheckEvent(dsb, 0, 0); } else if (dsb->state != STATE_STOPPED) { - /* if recovering, reset the mix position */ - if ((dsb->state == STATE_STARTING) || recover) { - dsb->primary_mixpos = writepos; - } - /* if the buffer was starting, it must be playing now */ if (dsb->state == STATE_STARTING) dsb->state = STATE_PLAYING; /* mix next buffer into the main buffer */ - len = DSOUND_MixOne(dsb, writepos, mixlen); + len = DSOUND_PullBuffer(dsb, writepos, mixlen); + if (len != mixlen) + ERR("Only %d/%d bytes from buffer %p\n", len, mixlen, dsb); + if (!minlen) minlen = len; @@ -795,7 +348,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) if (device->priolevel != DSSCL_WRITEPRIMARY) { BOOL recover = FALSE, all_stopped = FALSE; - DWORD playpos, writepos, writelead, maxq, frag, prebuff_max, prebuff_left, size1, size2, mixplaypos, mixplaypos2; + DWORD playpos, writepos, writelead, maxq, frag, prebuff_max, prebuff_left, size1, size2; LPVOID buf1, buf2; BOOL lock = (device->hwbuf && !(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK)); BOOL mustlock = FALSE; @@ -814,9 +367,6 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) playpos,writepos,device->playpos,device->mixpos,device->buflen); assert(device->playpos < device->buflen); - mixplaypos = DSOUND_bufpos_to_mixpos(device, device->playpos); - mixplaypos2 = DSOUND_bufpos_to_mixpos(device, playpos); - /* calc maximum prebuff */ prebuff_max = (device->prebuf * device->fraglen); if (!device->hwbuf && playpos + prebuff_max >= device->helfrags * device->fraglen) @@ -839,15 +389,12 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) /* reset mix position to write position */ device->mixpos = writepos; - ZeroMemory(device->mix_buffer, device->mix_buffer_len); - ZeroMemory(device->buffer, device->buflen); + FillMemory(device->buffer, device->buflen, nfiller); } else if (playpos < device->playpos) { buf1 = device->buffer + device->playpos; buf2 = device->buffer; size1 = device->buflen - device->playpos; size2 = playpos; - FillMemory(device->mix_buffer + mixplaypos, device->mix_buffer_len - mixplaypos, 0); - FillMemory(device->mix_buffer, mixplaypos2, 0); if (lock) IDsDriverBuffer_Lock(device->hwbuf, &buf1, &size1, &buf2, &size2, device->playpos, size1+size2, 0); FillMemory(buf1, size1, nfiller); @@ -861,7 +408,6 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) buf2 = NULL; size1 = playpos - device->playpos; size2 = 0; - FillMemory(device->mix_buffer + mixplaypos, mixplaypos2 - mixplaypos, 0); if (lock) IDsDriverBuffer_Lock(device->hwbuf, &buf1, &size1, &buf2, &size2, device->playpos, size1+size2, 0); FillMemory(buf1, size1, nfiller); @@ -891,15 +437,6 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) /* do the mixing */ frag = DSOUND_MixToPrimary(device, writepos, maxq, mustlock, recover, &all_stopped); - if (frag + writepos > device->buflen) - { - DWORD todo = device->buflen - writepos; - device->normfunction(device->mix_buffer + DSOUND_bufpos_to_mixpos(device, writepos), device->buffer + writepos, todo); - device->normfunction(device->mix_buffer, device->buffer, frag - todo); - } - else - device->normfunction(device->mix_buffer + DSOUND_bufpos_to_mixpos(device, writepos), device->buffer + writepos, frag); - /* update the mix position, taking wrap-around into account */ device->mixpos = writepos + frag; device->mixpos %= device->buflen; diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c index aa8450f..11eb003 100644 --- a/dlls/dsound/primary.c +++ b/dlls/dsound/primary.c @@ -200,16 +200,6 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) device->prebuf = device->helfrags; } - device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen); - device->mix_buffer = HeapAlloc(GetProcessHeap(), 0, device->mix_buffer_len); - if (!device->mix_buffer) - { - if (device->hwbuf) - IDsDriverBuffer_Release(device->hwbuf); - device->hwbuf = NULL; - return DSERR_OUTOFMEMORY; - } - if (device->state == STATE_PLAYING) device->state = STATE_STARTING; else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED; @@ -278,10 +268,7 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot); } - device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1]; - device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1]; FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0); - FillMemory(device->mix_buffer, device->mix_buffer_len, 0); device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0; return err; } @@ -581,22 +568,13 @@ static HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATE } } - device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen); - device->mix_buffer = HeapReAlloc(GetProcessHeap(), 0, device->mix_buffer, device->mix_buffer_len); - FillMemory(device->mix_buffer, device->mix_buffer_len, 0); - device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1]; - device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1]; - if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) { IDirectSoundBufferImpl** dsb = device->buffers; for (i = 0; i < device->nrofbuffers; i++, dsb++) { /* **** */ RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE); - (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec; DSOUND_RecalcFormat((*dsb)); - DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen, FALSE); - (*dsb)->primary_mixpos = 0; RtlReleaseResource(&(*dsb)->lock); /* **** */ diff --git a/dlls/dsound/resample.c b/dlls/dsound/resample.c new file mode 100644 index 0000000..d69292d --- /dev/null +++ b/dlls/dsound/resample.c @@ -0,0 +1,428 @@ +/* DirectSound + * + * Resample core + * Copyright 2010 Krzysztof Nikiel + * + * Initially based on resample: + * http://www-ccrma.stanford.edu/~jos/resample/ + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include /* lrint() */ + +#define NONAMELESSSTRUCT +#define NONAMELESSUNION +#include "windef.h" +#include "winbase.h" +#include "mmsystem.h" +#include "winternl.h" +#include "wine/debug.h" +#include "dsound.h" +#include "dsdriver.h" +#include "dsound_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(dsound); + + +#define CLIPSAMPLE(x,max) if(x>max)x=max;if(x<-max)x=-max; +#define MAXCHAN 16 + +typedef double FIRfloat; /* resampling data type */ + +/* lowpass filter Finite Impulse Response */ +static struct +{ + FIRfloat *data; + INT width; + INT res; /* impulse table resolution */ +} FIR = +{ +.data = NULL,.width = 0,.res = 50}; + + +static inline FIRfloat getsample(LPBYTE buf, INT bps) +{ + FIRfloat tmp; + + switch (bps) + { + case 1: + tmp = ((FIRfloat) (*((BYTE *) buf)) - 128.0); + tmp *= 256.0; + break; + case 2: + tmp = *((SHORT *) buf); + break; + case 3: + tmp = *((BYTE *) buf) | *((BYTE *) (buf + 1)) << 8 | + *((BYTE *) (buf + 2)) << 16; + tmp *= (1.0 / 256.0); + break; + case 4: + tmp = *((INT *) buf); + tmp *= (1.0 / 65536.0); + break; + } + return tmp; +} + +static inline void putsample(LPBYTE buf, INT bps, FIRfloat smp) +{ + INT ismp; + + switch (bps) + { + case 1: + ismp = lrint(smp); + ismp /= 0x100; + ismp += (INT) * ((BYTE *) buf) - 0x80; + CLIPSAMPLE(ismp, 0x7f); + *((BYTE *) buf) = ismp + 0x80; + break; + case 2: + ismp = lrint(smp); + ismp += *((SHORT *) buf); + CLIPSAMPLE(ismp, 0x7fff); + *((SHORT *) buf) = ismp; + break; + case 3: + CLIPSAMPLE(smp, (double) 0x7fff); + ismp = lrint(smp * 256.0); + ismp += *((BYTE *) buf) | *((BYTE *) (buf + 1)) << 8 | + *((BYTE *) (buf + 2)) << 16; + *((BYTE *) buf) = ismp & 0xff; + ismp >>= 8; + *((BYTE *) (buf + 1)) = ismp & 0xff; + ismp >>= 8; + *((BYTE *) (buf + 2)) = ismp & 0xff; + break; + case 4: + CLIPSAMPLE(smp, (double) 0x7fff); + ismp = lrint(smp * 65536.0); + *((INT *) buf) += ismp; + break; + } +} + + +/* + * Takes buffer data starting from current position and mixes new samples + * with primary buffer, starting at writepos. + * At most mixlen bytes can be mixed. + * 'writepos' and 'mixlen' should be block aligned. + */ +DWORD DSOUND_PullBuffer(IDirectSoundBufferImpl * dsb, DWORD writepos, + DWORD mixlen) +{ + INT iAdvance, oAdvance, iBPS, oBPS; + FIRfloat rsum[MAXCHAN], smp[MAXCHAN]; + BYTE *bufptr; + DWORD outlen; + INT iChans, oChans; + INT chan; + INT firstep; + DOUBLE amp[3]; + + if (!FIR.data) + { + ERR("FIR data uninitialized\n"); + return 0; + } + + iAdvance = dsb->pwfx->nBlockAlign; + oAdvance = dsb->device->pwfx->nBlockAlign; + iBPS = dsb->pwfx->wBitsPerSample >> 3; + oBPS = dsb->device->pwfx->wBitsPerSample >> 3; + outlen = 0; + iChans = dsb->pwfx->nChannels; + oChans = dsb->device->pwfx->nChannels; + + if (iChans >= MAXCHAN) + iChans = MAXCHAN - 1; + if (oChans >= MAXCHAN) + oChans = MAXCHAN - 1; + + for (chan = 0; chan < iChans; chan++) + smp[chan] = 0.0; + + firstep = FIR.res; + + amp[0] = (DOUBLE) dsb->volpan.dwTotalLeftAmpFactor * (1.0 / 65536.0); + if (oChans > 1) + { + amp[1] = (DOUBLE) dsb->volpan.dwTotalRightAmpFactor * (1.0 / 65536.0); + if (oChans > 2) + amp[2] = (DOUBLE) dsb->volpan.dwVolAmpFactor * (1.0 / 65536.0); + } + + if (dsb->freq > dsb->outfreq) /* downsampling */ + { + /* move transition band below output nuquist */ + firstep = (FIR.res * dsb->outfreq * 9) / (dsb->freq * 10); + /* + * If firstep==0 the resample loop will hang. + * Maybe it would be a better idea to return some error if downsample + * ratio is too high. + */ + if (firstep < 1) + firstep = 1; + + /* fix gain */ + amp[0] *= (double) firstep / FIR.res; + amp[1] *= (double) firstep / FIR.res; + amp[2] *= (double) firstep / FIR.res; + } + + while (1) + { + double firpos, frac; + int firpos_i; + + outlen += oAdvance; + if (outlen > mixlen) + { + outlen -= oAdvance; + break; + } + + /* just in case: check buffer alignment */ + if ((dsb->inpos + iAdvance) > dsb->buflen) + { + TRACE("Buffer not sample aligned (%p,%d,%d)\n", + dsb, dsb->buflen, iAdvance); + dsb->inpos = 0; + } + + /* input pointer */ + bufptr = dsb->buffer->memory + dsb->inpos; + + if (dsb->freq != dsb->outfreq) + { + firpos = (double) FIR.res * dsb->infrac * dsb->outfreq_1; + frac = firpos - floor(firpos); + firpos_i = firpos; + + for (chan = 0; chan < iChans; chan++) + rsum[chan] = 0.0; + } + else + { + firpos_i = FIR.width - 1; + frac = 0.0; /* avoid compiler warning */ + } + + /* apply the lowpass impulse response to input samples */ + while (1) + { + if (firpos_i >= (FIR.width - 1)) + break; + + /* going backward here */ + for (chan = iChans - 1; chan >= 0; chan--) + { + bufptr -= iBPS; + if (bufptr < dsb->buffer->memory) + bufptr += dsb->buflen; + + smp[chan] = getsample(bufptr, iBPS); + } + + if (dsb->freq != dsb->outfreq) + { + double ifir; + + ifir = + (1.0 - frac) * FIR.data[firpos_i] + frac * FIR.data[firpos_i + 1]; + + for (chan = 0; chan < iChans; chan++) + rsum[chan] += ifir * smp[chan]; + + firpos_i += firstep; + } + else + { + for (chan = 0; chan < iChans; chan++) + rsum[chan] = smp[chan]; + break; + } + } + + /* just in case: check buffer alignment */ + if ((writepos + oAdvance) > dsb->device->buflen) + { + TRACE("Device buffer not sample aligned (%p,%d,%d)\n", + dsb, dsb->device->buflen, oAdvance); + writepos = 0; + } + + /* output pointer */ + bufptr = dsb->device->buffer + writepos; + + for (chan = 0; chan < oChans; chan++) + { + if (chan >= iChans) + { + if (iChans > 1) + break; + if (chan > 1) + break; + + /* convert mono input to stereo output */ + rsum[chan] = rsum[0]; + } + + /* volume control */ + if (chan < 2) + rsum[chan] *= amp[chan]; + else + rsum[chan] *= amp[2]; + + putsample(bufptr, oBPS, rsum[chan]); + + bufptr += oBPS; + } + + /* advance pointers */ + + dsb->infrac += dsb->freq; + while (dsb->infrac >= dsb->outfreq) + { + dsb->infrac -= dsb->outfreq; + dsb->inpos += iAdvance; + if (dsb->inpos >= dsb->buflen) + { + if (dsb->playflags & DSBPLAY_LOOPING) + dsb->inpos -= dsb->buflen; + else + { + dsb->inpos = dsb->infrac = 0; + dsb->state = STATE_STOPPED; + } + } + } + + writepos += oAdvance; + if (writepos >= dsb->device->buflen) + writepos -= dsb->device->buflen; + } + + return outlen; +} + + +/** + * Kaiser windowed filter generator + */ +static double Izero(double x) +{ + const double IzeroEPSILON = 1e-30; /* Max error acceptable in Izero */ + double sum, u, halfx, temp; + int n; + + sum = u = n = 1; + halfx = x / 2.0; + do + { + temp = halfx / (double) n; + n += 1; + temp *= temp; + u *= temp; + sum += u; + } + while (u >= IzeroEPSILON * sum); + + return (sum); +} + + +/* + * Creates new filter table if not already present. + * All buffers share single table. + */ +void DSOUND_CreateFIR(void) +{ + /* passband width, relative to Nyquist frequency */ + const double passband = 0.9; + const static double Beta = 8; + const static double width0 = 32; + int cnt; + double IBeta; + double tmp, tmp2; + int half; + double cutoff; + double resamp_ratio; + + if (FIR.data) + { + ERR("FIR already created?\n"); + return; + } + + resamp_ratio = 1.0 / FIR.res; + + FIR.width = width0 / resamp_ratio; + FIR.width |= 1; /* odd symmetry */ + FIR.data = HeapAlloc(GetProcessHeap(), 0, FIR.width * sizeof(FIRfloat)); + + half = FIR.width / 2; + + IBeta = 1.0 / Izero(Beta); + + cutoff = passband * resamp_ratio; + + FIR.data[half] = 1.0; + + for (cnt = 1; cnt <= half; cnt++) + { + tmp = (double) cnt / half; + tmp2 = M_PI * cnt * cutoff; + + /* Kaiser windowed sinc */ + FIR.data[half + cnt] = FIR.data[half - cnt] = + sin(tmp2) / tmp2 * Izero(Beta * sqrt(1.0 - tmp * tmp)) * IBeta; + } + + /* normalize the table */ + tmp = 0.0; + for (cnt = 0; cnt < FIR.width; cnt++) + tmp += FIR.data[cnt]; + tmp = 1.0 * FIR.res / tmp; + for (cnt = 0; cnt < FIR.width; cnt++) + FIR.data[cnt] *= tmp; + + TRACE("FIR created: %d samples\n", FIR.width); +} + + +/* + * Deletes FIR table. + */ +void DSOUND_DeleteFIR(void) +{ + if (!FIR.data) + { + ERR("FIR already deleted?\n"); + return; + } + + FIR.width = 0; + HeapFree(GetProcessHeap(), 0, FIR.data); + FIR.data = NULL; + + TRACE("FIR deleted\n"); +}