mixer.c
来自「一个类似windows」· C语言 代码 · 共 1,178 行 · 第 1/3 页
C
1,178 行
if (len % dsb->dsound->device->pwfx->nBlockAlign) { INT nBlockAlign = dsb->dsound->device->pwfx->nBlockAlign; ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign); len = (len / nBlockAlign) * nBlockAlign; /* data alignment */ } if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound->device, len)) == NULL) return 0; TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos); ilen = DSOUND_MixerNorm(dsb, ibuf, len); if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D)) DSOUND_MixerVol(dsb, ibuf, len); if (dsb->dsound->device->pwfx->wBitsPerSample == 8) { BYTE *obuf = dsb->dsound->device->buffer + writepos; if ((writepos + len) <= dsb->dsound->device->buflen) todo = len; else todo = dsb->dsound->device->buflen - writepos; for (i = 0; i < todo; i++) { /* 8-bit WAV is unsigned */ field = (*ibuf++ - 128); field += (*obuf - 128); if (field > 127) field = 127; else if (field < -128) field = -128; *obuf++ = field + 128; } if (todo < len) { todo = len - todo; obuf = dsb->dsound->device->buffer; for (i = 0; i < todo; i++) { /* 8-bit WAV is unsigned */ field = (*ibuf++ - 128); field += (*obuf - 128); if (field > 127) field = 127; else if (field < -128) field = -128; *obuf++ = field + 128; } } } else { INT16 *ibufs, *obufs; ibufs = (INT16 *) ibuf; obufs = (INT16 *)(dsb->dsound->device->buffer + writepos); if ((writepos + len) <= dsb->dsound->device->buflen) todo = len / 2; else todo = (dsb->dsound->device->buflen - writepos) / 2; 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; *obufs++ = field; } if (todo < (len / 2)) { todo = (len / 2) - todo; obufs = (INT16 *)dsb->dsound->device->buffer; 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; *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; } 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 (%lu) past buflen (%lu), capping...\n", dsb->buf_mixpos, dsb->buflen); dsb->buf_mixpos = dsb->buflen; } } return len;}static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len){ INT ilen, field; UINT i, todo; BYTE *buf, *ibuf; TRACE("(%p,%ld,%ld)\n",dsb,writepos,len); if (len % dsb->dsound->device->pwfx->nBlockAlign) { INT nBlockAlign = dsb->dsound->device->pwfx->nBlockAlign; ERR("length not a multiple of block size, len = %ld, block size = %d\n", len, nBlockAlign); len = (len / nBlockAlign) * nBlockAlign; /* data alignment */ } if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound->device, len)) == NULL) return; TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos); ilen = DSOUND_MixerNorm(dsb, ibuf, len); if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D)) DSOUND_MixerVol(dsb, ibuf, len); /* subtract instead of add, to phase out premixed data */ if (dsb->dsound->device->pwfx->wBitsPerSample == 8) { BYTE *obuf = dsb->dsound->device->buffer + writepos; if ((writepos + len) <= dsb->dsound->device->buflen) todo = len; else todo = dsb->dsound->device->buflen - writepos; for (i = 0; i < todo; i++) { /* 8-bit WAV is unsigned */ field = (*ibuf++ - 128); field -= (*obuf - 128); if (field > 127) field = 127; else if (field < -128) field = -128; *obuf++ = field + 128; } if (todo < len) { todo = len - todo; obuf = dsb->dsound->device->buffer; for (i = 0; i < todo; i++) { /* 8-bit WAV is unsigned */ field = (*ibuf++ - 128); field -= (*obuf - 128); if (field > 127) field = 127; else if (field < -128) field = -128; *obuf++ = field + 128; } } } else { INT16 *ibufs, *obufs; ibufs = (INT16 *) ibuf; obufs = (INT16 *)(dsb->dsound->device->buffer + writepos); if ((writepos + len) <= dsb->dsound->device->buflen) todo = len / 2; else todo = (dsb->dsound->device->buflen - writepos) / 2; 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; *obufs++ = field; } if (todo < (len / 2)) { todo = (len / 2) - todo; obufs = (INT16 *)dsb->dsound->device->buffer; 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; *obufs++ = field; } } }}static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel){ DWORD size, flen, len, npos, nlen; INT iAdvance = dsb->pwfx->nBlockAlign; INT oAdvance = dsb->dsound->device->pwfx->nBlockAlign; /* determine amount of premixed data to cancel */ DWORD primary_done = ((dsb->primary_mixpos < writepos) ? dsb->dsound->device->buflen : 0) + dsb->primary_mixpos - writepos; TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos); /* backtrack the mix position */ size = primary_done / oAdvance; flen = size * dsb->freqAdjust; len = (flen >> DSOUND_FREQSHIFT) * iAdvance; flen &= (1<<DSOUND_FREQSHIFT)-1; while (dsb->freqAcc < flen) { len += iAdvance; dsb->freqAcc += 1<<DSOUND_FREQSHIFT; } len %= dsb->buflen; npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) + dsb->buf_mixpos - len; if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) { /* stop backtracking at startpos */ npos = dsb->startpos; len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) + dsb->buf_mixpos - npos; flen = dsb->freqAcc; nlen = len / dsb->pwfx->nBlockAlign; nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust; nlen *= dsb->dsound->device->pwfx->nBlockAlign; writepos = ((dsb->primary_mixpos < nlen) ? dsb->dsound->device->buflen : 0) + dsb->primary_mixpos - nlen; } dsb->freqAcc -= flen; dsb->buf_mixpos = npos; dsb->primary_mixpos = writepos; TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n", dsb->buf_mixpos, dsb->primary_mixpos, len); if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);}void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos){#if 0 DWORD i, size, flen, len, npos, nlen; INT iAdvance = dsb->pwfx->nBlockAlign; INT oAdvance = dsb->dsound->device->pwfx->nBlockAlign; /* determine amount of premixed data to cancel */ DWORD buf_done = ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) + dsb->buf_mixpos - buf_writepos;#endif WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos); /* since this is not implemented yet, just cancel *ALL* prebuffering for now * (which is faster anyway when there's only a single secondary buffer) */ dsb->dsound->device->need_remix = TRUE;}void DSOUND_ForceRemix(IDirectSoundBufferImpl *dsb){ TRACE("(%p)\n",dsb); EnterCriticalSection(&dsb->lock); if (dsb->state == STATE_PLAYING) dsb->dsound->device->need_remix = TRUE; LeaveCriticalSection(&dsb->lock);}static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen){ DWORD len, slen; /* determine this buffer's write position */ DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, writepos, writepos); /* determine how much already-mixed data exists */ DWORD buf_done = ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) + dsb->buf_mixpos - buf_writepos; DWORD primary_done = ((dsb->primary_mixpos < writepos) ? dsb->dsound->device->buflen : 0) + dsb->primary_mixpos - writepos; DWORD adv_done = ((dsb->dsound->device->mixpos < writepos) ? dsb->dsound->device->buflen : 0) + dsb->dsound->device->mixpos - writepos; DWORD played = ((buf_writepos < dsb->playpos) ? dsb->buflen : 0) + buf_writepos - dsb->playpos; DWORD buf_left = dsb->buflen - buf_writepos; int still_behind; TRACE("(%p,%ld,%ld,%ld)\n",dsb,playpos,writepos,mixlen); TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos); TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done); TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos, mixlen); TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin); /* check for notification positions */ if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY && dsb->state != STATE_STARTING) { DSOUND_CheckEvent(dsb, played); } /* save write position for non-GETCURRENTPOSITION2... */ dsb->playpos = buf_writepos; /* check whether CalcPlayPosition detected a mixing underrun */ if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) { /* it did, but did we have more to play? */ if ((dsb->playflags & DSBPLAY_LOOPING) || (dsb->buf_mixpos < dsb->buflen)) { /* yes, have to recover */ ERR("underrun on sound buffer %p\n", dsb); TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos); } dsb->primary_mixpos = writepos; primary_done = 0; } /* determine how far ahead we should mix */ if (((dsb->playflags & DSBPLAY_LOOPING) || (dsb->leadin && (dsb->probably_valid_to != 0))) && !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) { /* if this is a streaming buffer, it typically means that * we should defer mixing past probably_valid_to as long * as we can, to avoid unnecessary remixing */ /* the heavy-looking calculations shouldn't be that bad, * as any game isn't likely to be have more than 1 or 2 * streaming buffers in use at any time anyway... */ DWORD probably_valid_left = (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen : ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) + dsb->probably_valid_to - buf_writepos; /* check for leadin condition */ if ((probably_valid_left == 0) && (dsb->probably_valid_to == dsb->startpos) && dsb->leadin) probably_valid_left = dsb->buflen; TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n", dsb->probably_valid_to, probably_valid_left); /* check whether the app's time is already up */ if (probably_valid_left < dsb->writelead) { WARN("probably_valid_to now within writelead, possible streaming underrun\n"); /* once we pass the point of no return, * no reason to hold back anymore */ dsb->probably_valid_to = (DWORD)-1; /* we just have to go ahead and mix what we have, * there's no telling what the app is thinking anyway */ } else { /* adjust for our frequency and our sample size */ probably_valid_left = MulDiv(probably_valid_left, 1 << DSOUND_FREQSHIFT, dsb->pwfx->nBlockAlign * dsb->freqAdjust) * dsb->dsound->device->pwfx->nBlockAlign; /* check whether to clip mix_len */ if (probably_valid_left < mixlen) { TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left); mixlen = probably_valid_left; } } } /* cut mixlen with what's already been mixed */ if (mixlen < primary_done) { /* huh? and still CalcPlayPosition didn't * detect an underrun? */ FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done); return 0; } len = mixlen - primary_done; TRACE("remaining mixlen=%ld\n", len); if (len < dsb->dsound->device->fraglen) { /* smaller than a fragment, wait until it gets larger * before we take the mixing overhead */ TRACE("mixlen not worth it, deferring mixing\n"); still_behind = 1; goto post_mix; } /* ok, we know how much to mix, let's go */ still_behind = (adv_done > primary_done); while (len) { slen = dsb->dsound->device->buflen - dsb->primary_mixpos; if (slen > len) slen = len; slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen); if ((dsb->primary_mixpos < dsb->dsound->device->mixpos) && (dsb->primary_mixpos + slen >= dsb->dsound->device->mixpos))
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?