mixer.c
来自「一个类似windows」· C语言 代码 · 共 1,178 行 · 第 1/3 页
C
1,178 行
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 + =
减小字号Ctrl + -
显示快捷键?