dsound: Move resampling away from mixer thread.

This commit is contained in:
Maarten Lankhorst 2007-07-29 23:47:01 +02:00 committed by Alexandre Julliard
parent 1ad6abfbbc
commit 36e9054629
5 changed files with 392 additions and 312 deletions

View File

@ -293,9 +293,10 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
oldFreq = This->freq;
This->freq = freq;
if (freq != oldFreq) {
This->freqAdjust = (freq << DSOUND_FREQSHIFT) / This->device->pwfx->nSamplesPerSec;
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);
}
RtlReleaseResource(&This->lock);
@ -315,9 +316,8 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Play(
RtlAcquireResourceExclusive(&This->lock, TRUE);
This->playflags = flags;
if (This->state == STATE_STOPPED) {
if (This->state == STATE_STOPPED && !This->hwbuf) {
This->leadin = TRUE;
This->startpos = This->buf_mixpos;
This->state = STATE_STARTING;
} else if (This->state == STATE_STOPPING)
This->state = STATE_PLAYING;
@ -347,7 +347,10 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
if (This->state == STATE_PLAYING)
This->state = STATE_STOPPING;
else if (This->state == STATE_STARTING)
{
This->state = STATE_STOPPED;
DSOUND_CheckEvent(This, 0, 0);
}
if (This->hwbuf) {
hres = IDsDriverBuffer_Stop(This->hwbuf);
if (hres != DS_OK)
@ -355,7 +358,6 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER8 iface)
else
This->state = STATE_STOPPED;
}
DSOUND_CheckEvent(This, 0, 0);
RtlReleaseResource(&This->lock);
/* **** */
@ -379,13 +381,13 @@ static ULONG WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface)
if (!ref) {
DirectSoundDevice_RemoveBuffer(This->device, This);
RtlDeleteResource(&This->lock);
if (This->hwbuf) {
IDsDriverBuffer_Release(This->hwbuf);
if (This->device->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) {
This->buffer->ref--;
list_remove(&This->entry);
if (This->buffer->ref==0) {
HeapFree(GetProcessHeap(),0,This->buffer->memory);
HeapFree(GetProcessHeap(),0,This->buffer);
@ -393,12 +395,14 @@ static ULONG WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface)
}
} else {
This->buffer->ref--;
list_remove(&This->entry);
if (This->buffer->ref==0) {
HeapFree(GetProcessHeap(),0,This->buffer->memory);
HeapFree(GetProcessHeap(),0,This->buffer);
}
}
HeapFree(GetProcessHeap(), 0, This->tmp_buffer);
HeapFree(GetProcessHeap(), 0, This->notifies);
HeapFree(GetProcessHeap(), 0, This->pwfx);
HeapFree(GetProcessHeap(), 0, This);
@ -408,33 +412,14 @@ static ULONG WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER8 iface)
return ref;
}
static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This)
{
DWORD bplay = This->buf_mixpos;
/* check for lead-in */
if (This->leadin && ((bplay < This->startpos) || (bplay > This->buf_mixpos))) {
/* seems we haven't started playing yet */
TRACE("this still in lead-in phase\n");
bplay = This->startpos;
}
/* sanity */
if (bplay >= This->buflen){
FIXME("Bad play position. bplay: %d, buflen: %d\n", bplay, This->buflen);
bplay %= This->buflen;
}
/* return the result */
return bplay;
}
static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
LPDIRECTSOUNDBUFFER8 iface,LPDWORD playpos,LPDWORD writepos
) {
HRESULT hres;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
TRACE("(%p,%p,%p)\n",This,playpos,writepos);
RtlAcquireResourceShared(&This->lock, TRUE);
if (This->hwbuf) {
hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
if (hres != DS_OK) {
@ -442,21 +427,25 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
return hres;
}
} else {
if (playpos && (This->state != STATE_PLAYING))
/* we haven't been merged into the primary buffer (yet) */
*playpos = This->buf_mixpos;
else if (playpos)
*playpos = DSOUND_CalcPlayPosition(This);
if (writepos)
*writepos = (playpos ? *playpos : This->buf_mixpos);
}
if (writepos) {
if (This->state != STATE_STOPPED) {
/* apply the documented 10ms lead to writepos */
*writepos += This->writelead;
DWORD pos = This->sec_mixpos;
/* sanity */
if (pos >= This->buflen){
FIXME("Bad play position. playpos: %d, buflen: %d\n", pos, This->buflen);
pos %= This->buflen;
}
if (playpos)
*playpos = pos;
if (writepos)
*writepos = pos;
}
if (writepos && This->state != STATE_STOPPED && (!This->hwbuf || !(This->device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD))) {
/* apply the documented 10ms lead to writepos */
*writepos += This->writelead;
*writepos %= This->buflen;
}
RtlReleaseResource(&This->lock);
TRACE("playpos = %d, writepos = %d, buflen=%d (%p, time=%d)\n",
playpos?*playpos:-1, writepos?*writepos:-1, This->buflen, This, GetTickCount());
@ -583,6 +572,8 @@ 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)
WARN("Overwriting mixing position, case 1\n");
*audiobytes1 = writebytes;
if (lplpaudioptr2)
*(LPBYTE*)lplpaudioptr2 = NULL;
@ -592,12 +583,17 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
*(LPBYTE*)lplpaudioptr1, *audiobytes1, lplpaudioptr2 ? *(LPBYTE*)lplpaudioptr2 : NULL, audiobytes2 ? *audiobytes2: 0, writecursor);
TRACE("->%d.0\n",writebytes);
} else {
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)
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)
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);
}
}
@ -613,25 +609,31 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
) {
HRESULT hres = DS_OK;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
DWORD oldpos;
TRACE("(%p,%d)\n",This,newpos);
/* **** */
RtlAcquireResourceExclusive(&This->lock, TRUE);
oldpos = This->sec_mixpos;
/* start mixing from this new location instead */
newpos %= This->buflen;
newpos -= newpos%This->pwfx->nBlockAlign;
This->buf_mixpos = newpos;
This->sec_mixpos = newpos;
/* 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 */
/* position HW buffer if applicable, else just start mixing from new location instead */
if (This->hwbuf) {
hres = IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
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);
/* **** */
@ -703,7 +705,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;
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface, *iter;
HRESULT hres = DS_OK;
TRACE("(%p,%p,%d,%p,%d)\n", This,p1,x1,p2,x2);
@ -720,6 +722,24 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
RtlReleaseResource(&This->lock);
/* **** */
if (!p2)
x2 = 0;
if (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)
DSOUND_MixToTemporary(iter, (DWORD_PTR)p1 - (DWORD_PTR)iter->buffer->memory, x1);
if (x2)
DSOUND_MixToTemporary(iter, 0, x2);
RtlReleaseResource(&iter->lock);
}
RtlReleaseResource(&This->device->buffer_list_lock);
}
return hres;
}
@ -1031,6 +1051,8 @@ HRESULT IDirectSoundBufferImpl_Create(
return DSERR_OUTOFMEMORY;
}
dsb->buffer->ref = 1;
list_init(&dsb->buffer->buffers);
list_add_head(&dsb->buffer->buffers, &dsb->entry);
FillMemory(dsb->buffer->memory, dsb->buflen, dsbd->lpwfxFormat->wBitsPerSample == 8 ? 128 : 0);
}
@ -1054,25 +1076,26 @@ HRESULT IDirectSoundBufferImpl_Create(
return DSERR_OUTOFMEMORY;
}
dsb->buffer->ref = 1;
list_init(&dsb->buffer->buffers);
list_add_head(&dsb->buffer->buffers, &dsb->entry);
FillMemory(dsb->buffer->memory, dsb->buflen, dsbd->lpwfxFormat->wBitsPerSample == 8 ? 128 : 0);
}
err = DS_OK;
}
}
/* calculate fragment size and write lead */
DSOUND_RecalcFormat(dsb);
/* It's not necessary to initialize values to zero since */
/* we allocated this structure with HEAP_ZERO_MEMORY... */
dsb->buf_mixpos = 0;
dsb->buf_mixpos = dsb->sec_mixpos = 0;
dsb->state = STATE_STOPPED;
dsb->freqAdjust = (dsb->freq << DSOUND_FREQSHIFT) /
device->pwfx->nSamplesPerSec;
dsb->freqAdjust = ((DWORD64)dsb->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
dsb->nAvgBytesPerSec = dsb->freq *
dsbd->lpwfxFormat->nBlockAlign;
/* calculate fragment size and write lead */
DSOUND_RecalcFormat(dsb);
if (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D) {
dsb->ds3db_ds3db.dwSize = sizeof(DS3DBUFFER);
dsb->ds3db_ds3db.vPosition.x = 0.0;
@ -1201,22 +1224,27 @@ HRESULT IDirectSoundBufferImpl_Duplicate(
return DSERR_OUTOFMEMORY;
}
dsb->buffer->ref = 1;
list_init(&dsb->buffer->buffers);
list_add_head(&dsb->buffer->buffers, &dsb->entry);
/* FIXME: copy buffer ? */
}
}
} else {
dsb->hwbuf = NULL;
dsb->buffer->ref++;
list_add_head(&dsb->buffer->buffers, &dsb->entry);
}
dsb->ref = 0;
dsb->state = STATE_STOPPED;
dsb->buf_mixpos = 0;
dsb->buf_mixpos = dsb->sec_mixpos = 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);
/* variable sized struct so calculate size based on format */
size = sizeof(WAVEFORMATEX) + pdsb->pwfx->cbSize;
@ -1238,6 +1266,7 @@ HRESULT IDirectSoundBufferImpl_Duplicate(
hres = DirectSoundDevice_AddBuffer(device, dsb);
if (hres != DS_OK) {
RtlDeleteResource(&dsb->lock);
HeapFree(GetProcessHeap(),0,dsb->tmp_buffer);
HeapFree(GetProcessHeap(),0,dsb->buffer);
HeapFree(GetProcessHeap(),0,dsb->pwfx);
HeapFree(GetProcessHeap(),0,dsb);

View File

@ -23,6 +23,8 @@
#define DS_TIME_RES 2 /* Resolution of multimedia timer */
#define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
#include "wine/list.h"
/* direct sound hardware acceleration levels */
#define DS_HW_ACCEL_FULL 0 /* default on Windows 98 */
#define DS_HW_ACCEL_STANDARD 1 /* default on Windows 2000 */
@ -108,6 +110,7 @@ typedef struct BufferMemory
{
LONG ref;
LPBYTE memory;
struct list buffers;
} BufferMemory;
ULONG DirectSoundDevice_Release(DirectSoundDevice * device);
@ -159,16 +162,17 @@ struct IDirectSoundBufferImpl
PIDSDRIVERBUFFER hwbuf;
PWAVEFORMATEX pwfx;
BufferMemory* buffer;
LPBYTE tmp_buffer;
DWORD playflags,state,leadin;
DWORD startpos,writelead,buflen;
DWORD writelead,buflen;
DWORD nAvgBytesPerSec;
DWORD freq;
DWORD freq, tmp_buffer_len, max_buffer_len;
DSVOLUMEPAN volpan;
DSBUFFERDESC dsbd;
/* used for frequency conversion (PerfectPitch) */
ULONG freqAdjust, freqAcc;
/* used for intelligent (well, sort of) prebuffering */
DWORD primary_mixpos, buf_mixpos;
ULONG freqneeded, freqAdjust, freqAcc, freqAccNext;
/* used for mixing */
DWORD primary_mixpos, buf_mixpos, sec_mixpos;
/* IDirectSoundNotifyImpl fields */
IDirectSoundNotifyImpl* notify;
@ -184,6 +188,8 @@ struct IDirectSoundBufferImpl
/* IKsPropertySet fields */
IKsBufferPropertySetImpl* iks;
struct list entry;
};
HRESULT IDirectSoundBufferImpl_Create(
@ -431,6 +437,9 @@ 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);
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);
void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2);
@ -460,7 +469,7 @@ HRESULT WINAPI IDirectSoundCaptureImpl_Initialize(
#define STATE_CAPTURING 2
#define STATE_STOPPING 3
#define DSOUND_FREQSHIFT (14)
#define DSOUND_FREQSHIFT (20)
extern DirectSoundDevice* DSOUND_renderer[MAXWAVEDRIVERS];
extern GUID DSOUND_renderer_guids[MAXWAVEDRIVERS];

View File

@ -4,6 +4,7 @@
* Copyright 1998 Rob Riggs
* Copyright 2000-2002 TransGaming Technologies, Inc.
* Copyright 2007 Peter Dons Tychsen
* 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
@ -90,12 +91,106 @@ void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan)
TRACE("Vol=%d Pan=%d\n", volpan->lVolume, volpan->lPan);
}
/* 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)
{
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;
}
/** Convert a resampled pointer that fits for primary to a 'native' sample pointer
* freqAccNext is used here rather then 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;
}
/**
* 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);
}
/**
* 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;
TRACE("(%p)\n",dsb);
/* 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;
if (needremix)
{
if (needresample)
DSOUND_RecalcFreqAcc(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);
}
else dsb->max_buffer_len = dsb->tmp_buffer_len = dsb->buflen;
dsb->buf_mixpos = DSOUND_secpos_to_bufpos(dsb, dsb->sec_mixpos, 0, NULL);
}
/**
@ -222,187 +317,180 @@ static inline void cp_fields(const IDirectSoundBufferImpl *dsb, const BYTE *ibuf
}
/**
* Mix at most the given amount of data into the given device buffer from the
* given secondary buffer, starting from the dsb's first currently unmixed
* frame (buf_mixpos), translating frequency (pitch), stereo/mono and
* bits-per-sample. The secondary buffer sample is looped if it is not
* long enough and it is a looping buffer.
* (Doesn't perform any mixing - this is a straight copy operation).
*
* Now with PerfectPitch (tm) technology
* Calculate the distance between two buffer offsets, taking wraparound
* into account.
*/
static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2)
{
/* If these asserts fail, the problem is not here, but in the underlying code */
assert(ptr1 < buflen);
assert(ptr2 < buflen);
if (ptr1 >= ptr2) {
return ptr1 - ptr2;
} else {
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
* buf = the device buffer
* len = number of bytes to store in the device buffer
* writepos = Starting position of changed buffer
* len = number of bytes to resample from writepos
*
* Returns: the number of bytes read from the secondary buffer
* (ie. len, adjusted for frequency, number of channels and sample size,
* and limited by buffer length for non-looping buffers)
* NOTE: writepos + len <= buflen, This function doesn't loop!
*/
static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
{
INT i, size, ipos, ilen;
BYTE *ibp, *obp;
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;
ibp = dsb->buffer->memory + dsb->buf_mixpos;
obp = buf;
if (!dsb->tmp_buffer)
/* Nothing to do, already ideal format */
return;
TRACE("(%p, %p, %p), buf_mixpos=%d\n", dsb, ibp, obp, dsb->buf_mixpos);
ibp = dsb->buffer->memory + writepos;
ibp_begin = dsb->buffer->memory;
obp_begin = dsb->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)) {
INT bytesleft = dsb->buflen - dsb->buf_mixpos;
TRACE("(%p) Best case\n", dsb);
if (len <= bytesleft )
CopyMemory(obp, ibp, len);
else { /* wrap */
CopyMemory(obp, ibp, bytesleft);
CopyMemory(obp + bytesleft, dsb->buffer->memory, len - bytesleft);
}
return len;
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);
ilen = 0;
for (i = 0; i < len; i += oAdvance) {
cp_fields(dsb, ibp, obp );
obp = dsb->tmp_buffer + writepos/iAdvance*oAdvance;
for (i = 0; i < len; i += iAdvance) {
cp_fields(dsb, ibp, obp);
ibp += iAdvance;
ilen += iAdvance;
obp += oAdvance;
if (ibp >= (BYTE *)(dsb->buffer->memory + dsb->buflen))
ibp = dsb->buffer->memory; /* wrap */
}
return (ilen);
return;
}
/* Mix in different sample rates */
/* */
/* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
/* Patent Pending :-] */
TRACE("(%p) Adjusting frequency: %d -> %d\n", dsb, dsb->freq, dsb->device->pwfx->nSamplesPerSec);
size = len / iAdvance;
/* Patent enhancements (c) 2000 Ove K<>ven,
* TransGaming Technologies Inc. */
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);
}
/* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
dsb, dsb->freq, dsb->device->pwfx->nSamplesPerSec); */
size = len / oAdvance;
ilen = 0;
ipos = dsb->buf_mixpos;
for (i = 0; i < size; i++) {
cp_fields(dsb, (dsb->buffer->memory + ipos), obp);
obp = dsb->tmp_buffer + target_writepos;
/* 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);
obp += oAdvance;
dsb->freqAcc += dsb->freqAdjust;
if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
ipos += adv; ilen += adv;
ipos %= dsb->buflen;
freqAcc += dsb->freqAdjust;
if (freqAcc >= (1<<DSOUND_FREQSHIFT)) {
ULONG adv = (freqAcc>>DSOUND_FREQSHIFT);
freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
ibp += adv * iAdvance;
size -= adv;
}
}
return ilen;
}
static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
/** 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)
{
INT i;
BYTE *bpc = buf;
INT16 *bps = (INT16 *) buf;
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;
TRACE("(%p,%p,%d)\n",dsb,buf,len);
TRACE("(%p,%d)\n",dsb,len);
TRACE("left = %x, right = %x\n", dsb->volpan.dwTotalLeftAmpFactor,
dsb->volpan.dwTotalRightAmpFactor);
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->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
(!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
!(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
return; /* Nothing to do */
!(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
return NULL; /* Nothing to do */
/* If we end up with some bozo coder using panning or 3D sound */
/* with a mono primary buffer, it could sound very weird using */
/* this method. Oh well, tough patooties. */
if (dsb->device->tmp_buffer_len < len || !dsb->device->tmp_buffer)
{
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 */
switch (dsb->device->pwfx->nChannels) {
case 1:
for (i = 0; i < len; i++) {
INT val = *bpc - 128;
val = (val * dsb->volpan.dwTotalLeftAmpFactor) >> 16;
*bpc = val + 128;
bpc++;
}
break;
case 2:
for (i = 0; i < len; i+=2) {
INT val = *bpc - 128;
val = (val * dsb->volpan.dwTotalLeftAmpFactor) >> 16;
*bpc++ = val + 128;
val = *bpc - 128;
val = (val * dsb->volpan.dwTotalRightAmpFactor) >> 16;
*bpc = val + 128;
bpc++;
}
break;
default:
FIXME("doesn't support %d channels\n", dsb->device->pwfx->nChannels);
break;
for (i = 0; i < len; i+=2) {
*(bpc++) = (((INT)(*(mem++) - 128) * vLeft) >> 16) + 128;
*(bpc++) = (((INT)(*(mem++) - 128) * vRight) >> 16) + 128;
}
if (len % 2 == 1 && nChannels == 1)
*(bpc++) = (((INT)(*(mem++) - 128) * vLeft) >> 16) + 128;
break;
case 16:
/* 16-bit WAV is signed -- much better */
switch (dsb->device->pwfx->nChannels) {
case 1:
for (i = 0; i < len; i += 2) {
*bps = (*bps * dsb->volpan.dwTotalLeftAmpFactor) >> 16;
bps++;
}
break;
case 2:
for (i = 0; i < len; i += 4) {
*bps = (*bps * dsb->volpan.dwTotalLeftAmpFactor) >> 16;
bps++;
*bps = (*bps * dsb->volpan.dwTotalRightAmpFactor) >> 16;
bps++;
}
break;
default:
FIXME("doesn't support %d channels\n", dsb->device->pwfx->nChannels);
break;
for (i = 0; i < len; i += 4) {
*(bps++) = (*(mems++) * vLeft) >> 16;
*(bps++) = (*(mems++) * vRight) >> 16;
}
break;
default:
FIXME("doesn't support %d bit samples\n", dsb->device->pwfx->wBitsPerSample);
if (len % 4 == 2 && nChannels == 1)
*(bps++) = ((INT)*(mems++) * vLeft) >> 16;
break;
}
}
/**
* Make sure the device's tmp_buffer is at least the given size. Return a
* pointer to it.
*/
static LPBYTE DSOUND_tmpbuffer(DirectSoundDevice *device, DWORD len)
{
TRACE("(%p,%d)\n", device, len);
if (len > device->tmp_buffer_len) {
if (device->tmp_buffer)
device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, device->tmp_buffer, len);
else
device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, len);
device->tmp_buffer_len = len;
}
return device->tmp_buffer;
return dsb->device->tmp_buffer;
}
/**
@ -420,31 +508,14 @@ static LPBYTE DSOUND_tmpbuffer(DirectSoundDevice *device, DWORD len)
*/
static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
{
INT i, len, ilen, field, todo;
BYTE *buf, *ibuf;
INT i, len = fraglen, field, todo, ilen;
BYTE *ibuf = (dsb->tmp_buffer ? dsb->tmp_buffer : dsb->buffer->memory) + dsb->buf_mixpos, *volbuf;
DWORD oldpos;
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);
len = fraglen;
if (!(dsb->playflags & DSBPLAY_LOOPING)) {
/* This buffer is not looping, so make sure the requested
* length will not take us past the end of the buffer */
int secondary_remainder = dsb->buflen - dsb->buf_mixpos;
int adjusted_remainder = MulDiv(dsb->device->pwfx->nAvgBytesPerSec, secondary_remainder, dsb->nAvgBytesPerSec);
assert(adjusted_remainder >= 0);
adjusted_remainder -= adjusted_remainder % dsb->device->pwfx->nBlockAlign; /* data alignment */
/* The adjusted remainder must be at least one sample,
* otherwise we will never reach the end of the
* secondary buffer, as there will perpetually be a
* fractional remainder */
TRACE("secondary_remainder = %d, adjusted_remainder = %d, len = %d\n", secondary_remainder, adjusted_remainder, len);
if (adjusted_remainder < len) {
TRACE("clipping len to remainder of secondary buffer\n");
len = adjusted_remainder;
}
if (len == 0)
return 0;
}
assert(dsb->buf_mixpos + len <= dsb->tmp_buffer_len);
if (len % dsb->device->pwfx->nBlockAlign) {
INT nBlockAlign = dsb->device->pwfx->nBlockAlign;
@ -452,22 +523,10 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
len -= len % nBlockAlign; /* data alignment */
}
/* Create temp buffer to hold actual resulting data */
if ((buf = ibuf = DSOUND_tmpbuffer(dsb->device, len)) == NULL)
return 0;
TRACE("MixInBuffer (%p) len = %d, dest = %d\n", dsb, len, writepos);
/* first, copy the data from the DirectSoundBuffer into the temporary
buffer, translating frequency/bits-per-sample/number-of-channels
to match the device settings */
ilen = DSOUND_MixerNorm(dsb, ibuf, len);
/* then apply the correct volume, if necessary */
if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
DSOUND_MixerVol(dsb, ibuf, len);
/* Apply volume if needed */
volbuf = DSOUND_MixerVol(dsb, dsb->buf_mixpos, len);
if (volbuf)
ibuf = volbuf;
/* Now mix the temporary buffer into the devices main buffer */
if (dsb->device->pwfx->wBitsPerSample == 8) {
@ -500,7 +559,7 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
*obuf++ = field + 128;
}
}
} else {
} else {
INT16 *ibufs, *obufs;
ibufs = (INT16 *) ibuf;
@ -514,6 +573,7 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
for (i = 0; i < todo; i++) {
/* 16-bit WAV is signed */
field = *ibufs++;
field += *obufs;
if (field > 32767) field = 32767;
else if (field < -32768) field = -32768;
@ -533,60 +593,44 @@ static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWO
*obufs++ = field;
}
}
}
if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
/* HACK... leadin should be reset when the PLAY position reaches the startpos,
* not the MIX position... but if the sound buffer is bigger than our prebuffering
* (which must be the case for the streaming buffers that need this hack anyway)
* plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
dsb->leadin = FALSE;
}
oldpos = dsb->sec_mixpos;
dsb->buf_mixpos += len;
if (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) {
if (dsb->buf_mixpos > dsb->tmp_buffer_len)
ERR("Mixpos (%u) past buflen (%u), capping...\n", 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, dsb->buf_mixpos, ilen);
}
dsb->buf_mixpos += ilen;
if (dsb->buf_mixpos >= dsb->buflen) {
if (dsb->playflags & DSBPLAY_LOOPING) {
/* wrap */
dsb->buf_mixpos %= dsb->buflen;
if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
dsb->leadin = FALSE; /* HACK: see above */
} else if (dsb->buf_mixpos > dsb->buflen) {
ERR("Mixpos (%u) past buflen (%u), capping...\n", dsb->buf_mixpos, dsb->buflen);
dsb->buf_mixpos = dsb->buflen;
}
DSOUND_CheckEvent(dsb, oldpos, ilen);
}
/* increase mix position */
dsb->primary_mixpos += len;
dsb->primary_mixpos %= dsb->device->buflen;
if (dsb->primary_mixpos >= dsb->device->buflen)
dsb->primary_mixpos -= dsb->device->buflen;
return len;
}
/**
* Calculate the distance between two buffer offsets, taking wraparound
* into account.
*/
static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2)
{
if (ptr1 >= ptr2) {
return ptr1 - ptr2;
} else {
return buflen + ptr1 - ptr2;
}
}
/**
* 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.
@ -597,11 +641,22 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mi
{
/* The buffer's primary_mixpos may be before or after the the device
* buffer's mixpos, but both must be ahead of writepos. */
DWORD primary_done, buflen = dsb->buflen / dsb->pwfx->nBlockAlign * dsb->device->pwfx->nBlockAlign;
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, startpos=%d, leadin=%d, buflen=%d\n", dsb->playflags, dsb->startpos, dsb->leadin, dsb->buflen);
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);
@ -610,7 +665,7 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mi
if(mixlen < primary_done)
{
/* Should *NEVER* happen */
ERR("Fatal error. Under/Overflow? primary_done=%d, mixpos=%d, primary_mixpos=%d, writepos=%d, mixlen=%d\n", primary_done,dsb->buf_mixpos,dsb->primary_mixpos, writepos, mixlen);
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);
return 0;
}
@ -619,45 +674,32 @@ static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mi
TRACE("primary_done=%d, mixlen (primary) = %i\n", primary_done, mixlen);
if ((dsb->playflags & DSBPLAY_LOOPING) && mixlen > buflen)
if (!mixlen)
return 0;
/* 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)
{
while (mixlen > buflen)
{
DWORD mixedlength = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, buflen);
mixlen -= buflen;
if (!mixedlength)
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)
{
mixlen = 0;
break;
mixfirst = (dsb->tmp_buffer_len < mixlen ? dsb->tmp_buffer_len : mixlen);
newmixed = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixfirst);
mixlen -= newmixed;
}
}
}
/* clip to valid length */
mixlen = (buflen < mixlen) ? buflen : mixlen;
TRACE("mixlen (buffer)=%d\n", mixlen);
if (mixlen)
/* mix more data */
mixlen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixlen);
TRACE("new primary_mixpos=%d, mixed data len=%d, buffer left = %d\n",
dsb->primary_mixpos, mixlen, (dsb->buflen - dsb->buf_mixpos));
else DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixlen);
/* re-calculate the primary done */
primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos);
/* check if buffer should be considered complete */
if (((dsb->buflen - dsb->buf_mixpos) < dsb->writelead) &&
!(dsb->playflags & DSBPLAY_LOOPING)) {
TRACE("Buffer reached end. Stopped\n");
dsb->state = STATE_STOPPED;
dsb->buf_mixpos = 0;
dsb->leadin = FALSE;
}
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;
@ -705,7 +747,7 @@ static DWORD DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos
if (dsb->state == STATE_STOPPING) {
dsb->state = STATE_STOPPED;
DSOUND_CheckEvent(dsb, 0, 0);
} else {
} else if (dsb->state != STATE_STOPPED) {
/* if recovering, reset the mix position */
if ((dsb->state == STATE_STARTING) || recover) {
@ -719,18 +761,14 @@ static DWORD DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos
if (dsb->state == STATE_STARTING)
dsb->state = STATE_PLAYING;
/* check if min-len should be initialized */
if(minlen == 0) minlen = len;
if (!minlen) minlen = len;
/* record the minimum length mixed from all buffers */
/* record the minimum length mixed from all buffers */
/* we only want to return the length which *all* buffers have mixed */
if(len != 0) minlen = (len < minlen) ? len : minlen;
}
else if (len) minlen = (len < minlen) ? len : minlen;
if(dsb->state != STATE_STOPPED){
*all_stopped = FALSE;
}
RtlReleaseResource(&dsb->lock);
}
}
@ -872,7 +910,6 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
/* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */
prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos);
/* find the maximum we can prebuffer from current write position */
@ -996,11 +1033,11 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
DWORD_PTR dw1, DWORD_PTR dw2)
{
DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
DWORD start_time = GetTickCount();
DWORD end_time;
DWORD end_time;
TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
TRACE("entering at %d\n", start_time);
TRACE("entering at %d\n", start_time);
if (DSOUND_renderer[device->drvdesc.dnDevNode] != device) {
ERR("dsound died without killing us?\n");
@ -1022,7 +1059,7 @@ void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
TRACE("(%p,%x,%x,%x,%x)\n",hwo,msg,dwUser,dw1,dw2);
TRACE("entering at %d, msg=%08x(%s)\n", GetTickCount(), msg,
msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" :

View File

@ -378,7 +378,7 @@ HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex)
{
HRESULT err = DS_OK;
int i, alloc_size, cp_size;
DWORD nSamplesPerSec;
DWORD nSamplesPerSec, bpp, chans;
TRACE("(%p,%p)\n", device, wfex);
if (device->priolevel == DSSCL_NORMAL) {
@ -410,6 +410,8 @@ HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex)
device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size);
nSamplesPerSec = device->pwfx->nSamplesPerSec;
bpp = device->pwfx->wBitsPerSample;
chans = device->pwfx->nChannels;
CopyMemory(device->pwfx, wfex, cp_size);
@ -459,14 +461,15 @@ HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex)
DSOUND_RecalcPrimary(device);
}
if (nSamplesPerSec != device->pwfx->nSamplesPerSec) {
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 = ((*dsb)->freq << DSOUND_FREQSHIFT) /
wfex->nSamplesPerSec;
(*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
DSOUND_RecalcFormat((*dsb));
DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen);
RtlReleaseResource(&(*dsb)->lock);
/* **** */

View File

@ -279,7 +279,7 @@ void DSOUND_Calc3DBuffer(IDirectSoundBufferImpl *dsb)
{
TRACE("doppler: Buffer and Listener don't have velocities\n");
}
else
else if (ds3db_ds3db.vVelocity != dsb->device->ds3dl.vVelocity)
{
/* calculate length of ds3db_ds3db.vVelocity component which causes Doppler Effect
NOTE: if buffer moves TOWARDS the listener, it's velocity component is NEGATIVE
@ -296,6 +296,8 @@ void DSOUND_Calc3DBuffer(IDirectSoundBufferImpl *dsb)
dsb->freq, flFreq);
/* FIXME: replace following line with correct frequency setting ! */
dsb->freq = flFreq;
DSOUND_RecalcFormat(dsb);
DSOUND_MixToTemporary(dsb, 0, dsb->buflen);
}
#endif