📄 mixer.c
字号:
still_behind = FALSE;
dsb->primary_mixpos += slen; len -= slen;
dsb->primary_mixpos %= dsb->dsound->device->buflen;
if ((dsb->state == STATE_STOPPED) || !slen) break;
}
TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, dsb->dsound->device->mixpos);
TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
post_mix:
/* check if buffer should be considered complete */
if (buf_left < dsb->writelead &&
!(dsb->playflags & DSBPLAY_LOOPING)) {
dsb->state = STATE_STOPPED;
dsb->playpos = 0;
dsb->last_playpos = 0;
dsb->buf_mixpos = 0;
dsb->leadin = FALSE;
dsb->need_remix = FALSE;
DSOUND_CheckEvent(dsb, buf_left);
}
/* return how far we think the primary buffer can
* advance its underrun detector...*/
if (still_behind) return 0;
if ((mixlen - len) < primary_done) return 0;
slen = ((dsb->primary_mixpos < dsb->dsound->device->mixpos) ?
dsb->dsound->device->buflen : 0) + dsb->primary_mixpos -
dsb->dsound->device->mixpos;
if (slen > mixlen) {
/* the primary_done and still_behind checks above should have worked */
FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
slen = 0;
}
return slen;
}
static DWORD DSOUND_MixToPrimary(DirectSoundDevice *device, DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
{
INT i, len, maxlen = 0;
IDirectSoundBufferImpl *dsb;
TRACE("(%ld,%ld,%ld,%d)\n", playpos, writepos, mixlen, recover);
for (i = 0; i < device->nrofbuffers; i++) {
dsb = device->buffers[i];
if (dsb->buflen && dsb->state && !dsb->hwbuf) {
TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
EnterCriticalSection(&(dsb->lock));
if (dsb->state == STATE_STOPPING) {
DSOUND_MixCancel(dsb, writepos, TRUE);
dsb->state = STATE_STOPPED;
DSOUND_CheckEvent(dsb, 0);
} else {
if ((dsb->state == STATE_STARTING) || recover) {
dsb->primary_mixpos = writepos;
dsb->cvolpan = dsb->volpan;
dsb->need_remix = FALSE;
}
else if (dsb->need_remix) {
DSOUND_MixCancel(dsb, writepos, TRUE);
dsb->cvolpan = dsb->volpan;
dsb->need_remix = FALSE;
}
len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
if (dsb->state == STATE_STARTING)
dsb->state = STATE_PLAYING;
maxlen = (len > maxlen) ? len : maxlen;
}
LeaveCriticalSection(&(dsb->lock));
}
}
return maxlen;
}
static void DSOUND_MixReset(DirectSoundDevice *device, DWORD writepos)
{
INT i;
IDirectSoundBufferImpl *dsb;
int nfiller;
TRACE("(%p,%ld)\n", device, writepos);
/* the sound of silence */
nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
/* reset all buffer mix positions */
for (i = 0; i < device->nrofbuffers; i++) {
dsb = device->buffers[i];
if (dsb->buflen && dsb->state && !dsb->hwbuf) {
TRACE("Resetting %p\n", dsb);
EnterCriticalSection(&(dsb->lock));
if (dsb->state == STATE_STOPPING) {
dsb->state = STATE_STOPPED;
}
else if (dsb->state == STATE_STARTING) {
/* nothing */
} else {
DSOUND_MixCancel(dsb, writepos, FALSE);
dsb->cvolpan = dsb->volpan;
dsb->need_remix = FALSE;
}
LeaveCriticalSection(&(dsb->lock));
}
}
/* wipe out premixed data */
if (device->mixpos < writepos) {
FillMemory(device->buffer + writepos, device->buflen - writepos, nfiller);
FillMemory(device->buffer, device->mixpos, nfiller);
} else {
FillMemory(device->buffer + writepos, device->mixpos - writepos, nfiller);
}
/* reset primary mix position */
device->mixpos = writepos;
}
static void DSOUND_CheckReset(DirectSoundDevice *device, DWORD writepos)
{
TRACE("(%p,%ld)\n",device,writepos);
if (device->need_remix) {
DSOUND_MixReset(device, writepos);
device->need_remix = FALSE;
/* maximize Half-Life performance */
device->prebuf = ds_snd_queue_min;
device->precount = 0;
} else {
device->precount++;
if (device->precount >= 4) {
if (device->prebuf < ds_snd_queue_max)
device->prebuf++;
device->precount = 0;
}
}
TRACE("premix adjust: %d\n", device->prebuf);
}
void DSOUND_WaveQueue(DirectSoundDevice *device, DWORD mixq)
{
TRACE("(%p,%ld)\n", device, mixq);
if (mixq + device->pwqueue > ds_hel_queue) mixq = ds_hel_queue - device->pwqueue;
TRACE("queueing %ld buffers, starting at %d\n", mixq, device->pwwrite);
for (; mixq; mixq--) {
waveOutWrite(device->hwo, device->pwave[device->pwwrite], sizeof(WAVEHDR));
device->pwwrite++;
if (device->pwwrite >= DS_HEL_FRAGS) device->pwwrite = 0;
device->pwqueue++;
}
}
/* #define SYNC_CALLBACK */
void DSOUND_PerformMix(DirectSoundDevice *device)
{
int nfiller;
BOOL forced;
HRESULT hres;
TRACE("(%p)\n", device);
/* the sound of silence */
nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
/* whether the primary is forced to play even without secondary buffers */
forced = ((device->state == STATE_PLAYING) || (device->state == STATE_STARTING));
if (device->priolevel != DSSCL_WRITEPRIMARY) {
BOOL paused = ((device->state == STATE_STOPPED) || (device->state == STATE_STARTING));
/* FIXME: document variables */
DWORD playpos, writepos, inq, maxq, frag;
if (device->hwbuf) {
hres = IDsDriverBuffer_GetPosition(device->hwbuf, &playpos, &writepos);
if (hres) {
WARN("IDsDriverBuffer_GetPosition failed\n");
return;
}
/* Well, we *could* do Just-In-Time mixing using the writepos,
* but that's a little bit ambitious and unnecessary... */
/* rather add our safety margin to the writepos, if we're playing */
if (!paused) {
writepos += device->writelead;
writepos %= device->buflen;
} else writepos = playpos;
} else {
playpos = device->pwplay * device->fraglen;
writepos = playpos;
if (!paused) {
writepos += ds_hel_margin * device->fraglen;
writepos %= device->buflen;
}
}
TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld, buflen=%ld\n",
playpos,writepos,device->playpos,device->mixpos,device->buflen);
assert(device->playpos < device->buflen);
/* wipe out just-played sound data */
if (playpos < device->playpos) {
FillMemory(device->buffer + device->playpos, device->buflen - device->playpos, nfiller);
FillMemory(device->buffer, playpos, nfiller);
} else {
FillMemory(device->buffer + device->playpos, playpos - device->playpos, nfiller);
}
device->playpos = playpos;
EnterCriticalSection(&(device->mixlock));
/* reset mixing if necessary */
DSOUND_CheckReset(device, writepos);
/* check how much prebuffering is left */
inq = device->mixpos;
if (inq < writepos)
inq += device->buflen;
inq -= writepos;
/* find the maximum we can prebuffer */
if (!paused) {
maxq = playpos;
if (maxq < writepos)
maxq += device->buflen;
maxq -= writepos;
} else maxq = device->buflen;
/* clip maxq to device->prebuf */
frag = device->prebuf * device->fraglen;
if (maxq > frag) maxq = frag;
/* check for consistency */
if (inq > maxq) {
/* the playback position must have passed our last
* mixed position, i.e. it's an underrun, or we have
* nothing more to play */
TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
inq = 0;
/* stop the playback now, to allow buffers to refill */
if (device->state == STATE_PLAYING) {
device->state = STATE_STARTING;
}
else if (device->state == STATE_STOPPING) {
device->state = STATE_STOPPED;
}
else {
/* how can we have an underrun if we aren't playing? */
WARN("unexpected primary state (%ld)\n", device->state);
}
#ifdef SYNC_CALLBACK
/* DSOUND_callback may need this lock */
LeaveCriticalSection(&(device->mixlock));
#endif
if (DSOUND_PrimaryStop(device) != DS_OK)
WARN("DSOUND_PrimaryStop failed\n");
#ifdef SYNC_CALLBACK
EnterCriticalSection(&(device->mixlock));
#endif
if (device->hwbuf) {
/* the Stop is supposed to reset play position to beginning of buffer */
/* unfortunately, OSS is not able to do so, so get current pointer */
hres = IDsDriverBuffer_GetPosition(device->hwbuf, &playpos, NULL);
if (hres) {
LeaveCriticalSection(&(device->mixlock));
WARN("IDsDriverBuffer_GetPosition failed\n");
return;
}
} else {
playpos = device->pwplay * device->fraglen;
}
writepos = playpos;
device->playpos = playpos;
device->mixpos = writepos;
inq = 0;
maxq = device->buflen;
if (maxq > frag) maxq = frag;
FillMemory(device->buffer, device->buflen, nfiller);
paused = TRUE;
}
/* do the mixing */
frag = DSOUND_MixToPrimary(device, playpos, writepos, maxq, paused);
if (forced) frag = maxq - inq;
device->mixpos += frag;
device->mixpos %= device->buflen;
if (frag) {
/* buffers have been filled, restart playback */
if (device->state == STATE_STARTING) {
device->state = STATE_PLAYING;
}
else if (device->state == STATE_STOPPED) {
/* the dsound is supposed to play if there's something to play
* even if it is reported as stopped, so don't let this confuse you */
device->state = STATE_STOPPING;
}
LeaveCriticalSection(&(device->mixlock));
if (paused) {
if (DSOUND_PrimaryPlay(device) != DS_OK)
WARN("DSOUND_PrimaryPlay failed\n");
else
TRACE("starting playback\n");
}
}
else
LeaveCriticalSection(&(device->mixlock));
} else {
/* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
if (device->state == STATE_STARTING) {
if (DSOUND_PrimaryPlay(device) != DS_OK)
WARN("DSOUND_PrimaryPlay failed\n");
else
device->state = STATE_PLAYING;
}
else if (device->state == STATE_STOPPING) {
if (DSOUND_PrimaryStop(device) != DS_OK)
WARN("DSOUND_PrimaryStop failed\n");
else
device->state = STATE_STOPPED;
}
}
}
void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
DWORD start_time = GetTickCount();
DWORD end_time;
TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
TRACE("entering at %ld\n", start_time);
if (DSOUND_renderer[device->drvdesc.dnDevNode] != device) {
ERR("dsound died without killing us?\n");
timeKillEvent(timerID);
timeEndPeriod(DS_TIME_RES);
return;
}
RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
if (device->ref)
DSOUND_PerformMix(device);
RtlReleaseResource(&(device->buffer_list_lock));
end_time = GetTickCount();
TRACE("completed processing at %ld, duration = %ld\n", end_time, end_time - start_time);
}
void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo,msg,dwUser,dw1,dw2);
TRACE("entering at %ld, msg=%08x(%s)\n", GetTickCount(), msg,
msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" :
msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN");
if (msg == MM_WOM_DONE) {
DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
if (device->pwqueue == (DWORD)-1) {
TRACE("completed due to reset\n");
return;
}
/* it could be a bad idea to enter critical section here... if there's lock contention,
* the resulting scheduling delays might obstruct the winmm player thread */
#ifdef SYNC_CALLBACK
EnterCriticalSection(&(device->mixlock));
#endif
/* retrieve current values */
fraglen = device->fraglen;
buflen = device->buflen;
pwplay = device->pwplay;
playpos = pwplay * fraglen;
mixpos = device->mixpos;
/* check remaining mixed data */
inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
mixq = inq / fraglen;
if ((inq - (mixq * fraglen)) > 0) mixq++;
/* complete the playing buffer */
TRACE("done playing primary pos=%ld\n", playpos);
pwplay++;
if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
/* write new values */
device->pwplay = pwplay;
device->pwqueue--;
/* queue new buffer if we have data for it */
if (inq>1) DSOUND_WaveQueue(device, inq-1);
#ifdef SYNC_CALLBACK
LeaveCriticalSection(&(device->mixlock));
#endif
}
TRACE("completed\n");
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -