Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenmixer.c
Go to the documentation of this file.
00001 /* DirectSound 00002 * 00003 * Copyright 1998 Marcus Meissner 00004 * Copyright 1998 Rob Riggs 00005 * Copyright 2000-2002 TransGaming Technologies, Inc. 00006 * Copyright 2007 Peter Dons Tychsen 00007 * Copyright 2007 Maarten Lankhorst 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00022 */ 00023 00024 #include <assert.h> 00025 #include <stdarg.h> 00026 #include <math.h> /* Insomnia - pow() function */ 00027 00028 #define NONAMELESSSTRUCT 00029 #define NONAMELESSUNION 00030 #include "windef.h" 00031 #include "winbase.h" 00032 #include "mmsystem.h" 00033 #include "winternl.h" 00034 #include "wine/debug.h" 00035 #include "dsound.h" 00036 #include "dsdriver.h" 00037 #include "dsound_private.h" 00038 00039 WINE_DEFAULT_DEBUG_CHANNEL(dsound); 00040 00041 void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan) 00042 { 00043 double temp; 00044 TRACE("(%p)\n",volpan); 00045 00046 TRACE("Vol=%d Pan=%d\n", volpan->lVolume, volpan->lPan); 00047 /* the AmpFactors are expressed in 16.16 fixed point */ 00048 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 0xffff); 00049 /* FIXME: dwPan{Left|Right}AmpFactor */ 00050 00051 /* FIXME: use calculated vol and pan ampfactors */ 00052 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0)); 00053 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff); 00054 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0)); 00055 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff); 00056 00057 TRACE("left = %x, right = %x\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor); 00058 } 00059 00060 void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan) 00061 { 00062 double left,right; 00063 TRACE("(%p)\n",volpan); 00064 00065 TRACE("left=%x, right=%x\n",volpan->dwTotalLeftAmpFactor,volpan->dwTotalRightAmpFactor); 00066 if (volpan->dwTotalLeftAmpFactor==0) 00067 left=-10000; 00068 else 00069 left=600 * log(((double)volpan->dwTotalLeftAmpFactor) / 0xffff) / log(2); 00070 if (volpan->dwTotalRightAmpFactor==0) 00071 right=-10000; 00072 else 00073 right=600 * log(((double)volpan->dwTotalRightAmpFactor) / 0xffff) / log(2); 00074 if (left<right) 00075 { 00076 volpan->lVolume=right; 00077 volpan->dwVolAmpFactor=volpan->dwTotalRightAmpFactor; 00078 } 00079 else 00080 { 00081 volpan->lVolume=left; 00082 volpan->dwVolAmpFactor=volpan->dwTotalLeftAmpFactor; 00083 } 00084 if (volpan->lVolume < -10000) 00085 volpan->lVolume=-10000; 00086 volpan->lPan=right-left; 00087 if (volpan->lPan < -10000) 00088 volpan->lPan=-10000; 00089 00090 TRACE("Vol=%d Pan=%d\n", volpan->lVolume, volpan->lPan); 00091 } 00092 00098 DWORD DSOUND_bufpos_to_mixpos(const DirectSoundDevice* device, DWORD pos) 00099 { 00100 DWORD ret = pos * 32 / device->pwfx->wBitsPerSample; 00101 if (device->pwfx->wBitsPerSample == 32) 00102 ret *= 2; 00103 return ret; 00104 } 00105 00106 /* NOTE: Not all secpos have to always be mapped to a bufpos, other way around is always the case 00107 * DWORD64 is used here because a single DWORD wouldn't be big enough to fit the freqAcc for big buffers 00108 */ 00113 DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos, DWORD secmixpos, DWORD* overshot) 00114 { 00115 DWORD64 framelen = secpos / dsb->pwfx->nBlockAlign; 00116 DWORD64 freqAdjust = dsb->freqAdjust; 00117 DWORD64 acc, freqAcc; 00118 00119 if (secpos < secmixpos) 00120 freqAcc = dsb->freqAccNext; 00121 else freqAcc = dsb->freqAcc; 00122 acc = (framelen << DSOUND_FREQSHIFT) + (freqAdjust - 1 - freqAcc); 00123 acc /= freqAdjust; 00124 if (overshot) 00125 { 00126 DWORD64 oshot = acc * freqAdjust + freqAcc; 00127 assert(oshot >= framelen << DSOUND_FREQSHIFT); 00128 oshot -= framelen << DSOUND_FREQSHIFT; 00129 *overshot = (DWORD)oshot; 00130 assert(*overshot < dsb->freqAdjust); 00131 } 00132 return (DWORD)acc * dsb->device->pwfx->nBlockAlign; 00133 } 00134 00139 static DWORD DSOUND_bufpos_to_secpos(const IDirectSoundBufferImpl *dsb, DWORD bufpos) 00140 { 00141 DWORD oAdv = dsb->device->pwfx->nBlockAlign, iAdv = dsb->pwfx->nBlockAlign, pos; 00142 DWORD64 framelen; 00143 DWORD64 acc; 00144 00145 framelen = bufpos/oAdv; 00146 acc = framelen * (DWORD64)dsb->freqAdjust + (DWORD64)dsb->freqAccNext; 00147 acc = acc >> DSOUND_FREQSHIFT; 00148 pos = (DWORD)acc * iAdv; 00149 if (pos >= dsb->buflen) 00150 /* Because of differences between freqAcc and freqAccNext, this might happen */ 00151 pos = dsb->buflen - iAdv; 00152 TRACE("Converted %d/%d to %d/%d\n", bufpos, dsb->tmp_buffer_len, pos, dsb->buflen); 00153 return pos; 00154 } 00155 00159 static void DSOUND_RecalcFreqAcc(IDirectSoundBufferImpl *dsb) 00160 { 00161 if (!dsb->freqneeded) return; 00162 dsb->freqAcc = dsb->freqAccNext; 00163 dsb->tmp_buffer_len = DSOUND_secpos_to_bufpos(dsb, dsb->buflen, 0, &dsb->freqAccNext); 00164 TRACE("New freqadjust: %04x, new buflen: %d\n", dsb->freqAccNext, dsb->tmp_buffer_len); 00165 } 00166 00176 void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb) 00177 { 00178 BOOL needremix = TRUE, needresample = (dsb->freq != dsb->device->pwfx->nSamplesPerSec); 00179 DWORD bAlign = dsb->pwfx->nBlockAlign, pAlign = dsb->device->pwfx->nBlockAlign; 00180 00181 TRACE("(%p)\n",dsb); 00182 00183 /* calculate the 10ms write lead */ 00184 dsb->writelead = (dsb->freq / 100) * dsb->pwfx->nBlockAlign; 00185 00186 if ((dsb->pwfx->wBitsPerSample == dsb->device->pwfx->wBitsPerSample) && 00187 (dsb->pwfx->nChannels == dsb->device->pwfx->nChannels) && !needresample) 00188 needremix = FALSE; 00189 HeapFree(GetProcessHeap(), 0, dsb->tmp_buffer); 00190 dsb->tmp_buffer = NULL; 00191 dsb->max_buffer_len = dsb->freqAcc = dsb->freqAccNext = 0; 00192 dsb->freqneeded = needresample; 00193 00194 dsb->convert = convertbpp[dsb->pwfx->wBitsPerSample/8 - 1][dsb->device->pwfx->wBitsPerSample/8 - 1]; 00195 00196 dsb->resampleinmixer = FALSE; 00197 00198 if (needremix) 00199 { 00200 if (needresample) 00201 DSOUND_RecalcFreqAcc(dsb); 00202 else 00203 dsb->tmp_buffer_len = dsb->buflen / bAlign * pAlign; 00204 dsb->max_buffer_len = dsb->tmp_buffer_len; 00205 if ((dsb->max_buffer_len <= dsb->device->buflen || dsb->max_buffer_len < ds_snd_shadow_maxsize * 1024 * 1024) && ds_snd_shadow_maxsize >= 0) 00206 dsb->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, dsb->max_buffer_len); 00207 if (dsb->tmp_buffer) 00208 FillMemory(dsb->tmp_buffer, dsb->tmp_buffer_len, dsb->device->pwfx->wBitsPerSample == 8 ? 128 : 0); 00209 else 00210 dsb->resampleinmixer = TRUE; 00211 } 00212 else dsb->max_buffer_len = dsb->tmp_buffer_len = dsb->buflen; 00213 dsb->buf_mixpos = DSOUND_secpos_to_bufpos(dsb, dsb->sec_mixpos, 0, NULL); 00214 } 00215 00224 void DSOUND_CheckEvent(const IDirectSoundBufferImpl *dsb, DWORD playpos, int len) 00225 { 00226 int i; 00227 DWORD offset; 00228 LPDSBPOSITIONNOTIFY event; 00229 TRACE("(%p,%d)\n",dsb,len); 00230 00231 if (dsb->nrofnotifies == 0) 00232 return; 00233 00234 TRACE("(%p) buflen = %d, playpos = %d, len = %d\n", 00235 dsb, dsb->buflen, playpos, len); 00236 for (i = 0; i < dsb->nrofnotifies ; i++) { 00237 event = dsb->notifies + i; 00238 offset = event->dwOffset; 00239 TRACE("checking %d, position %d, event = %p\n", 00240 i, offset, event->hEventNotify); 00241 /* DSBPN_OFFSETSTOP has to be the last element. So this is */ 00242 /* OK. [Inside DirectX, p274] */ 00243 /* Windows does not seem to enforce this, and some apps rely */ 00244 /* on that, so we can't stop there. */ 00245 /* */ 00246 /* This also means we can't sort the entries by offset, */ 00247 /* because DSBPN_OFFSETSTOP == -1 */ 00248 if (offset == DSBPN_OFFSETSTOP) { 00249 if (dsb->state == STATE_STOPPED) { 00250 SetEvent(event->hEventNotify); 00251 TRACE("signalled event %p (%d)\n", event->hEventNotify, i); 00252 } 00253 continue; 00254 } 00255 if ((playpos + len) >= dsb->buflen) { 00256 if ((offset < ((playpos + len) % dsb->buflen)) || 00257 (offset >= playpos)) { 00258 TRACE("signalled event %p (%d)\n", event->hEventNotify, i); 00259 SetEvent(event->hEventNotify); 00260 } 00261 } else { 00262 if ((offset >= playpos) && (offset < (playpos + len))) { 00263 TRACE("signalled event %p (%d)\n", event->hEventNotify, i); 00264 SetEvent(event->hEventNotify); 00265 } 00266 } 00267 } 00268 } 00269 00274 static inline void cp_fields(const IDirectSoundBufferImpl *dsb, const BYTE *ibuf, BYTE *obuf, 00275 UINT istride, UINT ostride, UINT count, UINT freqAcc, UINT adj) 00276 { 00277 DirectSoundDevice *device = dsb->device; 00278 INT istep = dsb->pwfx->wBitsPerSample / 8, ostep = device->pwfx->wBitsPerSample / 8; 00279 00280 if (device->pwfx->nChannels == dsb->pwfx->nChannels) { 00281 dsb->convert(ibuf, obuf, istride, ostride, count, freqAcc, adj); 00282 if (device->pwfx->nChannels == 2) 00283 dsb->convert(ibuf + istep, obuf + ostep, istride, ostride, count, freqAcc, adj); 00284 } 00285 00286 if (device->pwfx->nChannels == 1 && dsb->pwfx->nChannels == 2) 00287 { 00288 dsb->convert(ibuf, obuf, istride, ostride, count, freqAcc, adj); 00289 } 00290 00291 if (device->pwfx->nChannels == 2 && dsb->pwfx->nChannels == 1) 00292 { 00293 dsb->convert(ibuf, obuf, istride, ostride, count, freqAcc, adj); 00294 dsb->convert(ibuf, obuf + ostep, istride, ostride, count, freqAcc, adj); 00295 } 00296 } 00297 00302 static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2) 00303 { 00304 /* If these asserts fail, the problem is not here, but in the underlying code */ 00305 assert(ptr1 < buflen); 00306 assert(ptr2 < buflen); 00307 if (ptr1 >= ptr2) { 00308 return ptr1 - ptr2; 00309 } else { 00310 return buflen + ptr1 - ptr2; 00311 } 00312 } 00326 void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len, BOOL inmixer) 00327 { 00328 INT size; 00329 BYTE *ibp, *obp, *obp_begin; 00330 INT iAdvance = dsb->pwfx->nBlockAlign; 00331 INT oAdvance = dsb->device->pwfx->nBlockAlign; 00332 DWORD freqAcc, target_writepos = 0, overshot, maxlen; 00333 00334 /* We resample only when needed */ 00335 if ((dsb->tmp_buffer && inmixer) || (!dsb->tmp_buffer && !inmixer) || dsb->resampleinmixer != inmixer) 00336 return; 00337 00338 assert(writepos + len <= dsb->buflen); 00339 if (inmixer && writepos + len < dsb->buflen) 00340 len += dsb->pwfx->nBlockAlign; 00341 00342 maxlen = DSOUND_secpos_to_bufpos(dsb, len, 0, NULL); 00343 00344 ibp = dsb->buffer->memory + writepos; 00345 if (!inmixer) 00346 obp_begin = dsb->tmp_buffer; 00347 else if (dsb->device->tmp_buffer_len < maxlen || !dsb->device->tmp_buffer) 00348 { 00349 dsb->device->tmp_buffer_len = maxlen; 00350 if (dsb->device->tmp_buffer) 00351 dsb->device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsb->device->tmp_buffer, maxlen); 00352 else 00353 dsb->device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, maxlen); 00354 obp_begin = dsb->device->tmp_buffer; 00355 } 00356 else 00357 obp_begin = dsb->device->tmp_buffer; 00358 00359 TRACE("(%p, %p)\n", dsb, ibp); 00360 size = len / iAdvance; 00361 00362 /* Check for same sample rate */ 00363 if (dsb->freq == dsb->device->pwfx->nSamplesPerSec) { 00364 TRACE("(%p) Same sample rate %d = primary %d\n", dsb, 00365 dsb->freq, dsb->device->pwfx->nSamplesPerSec); 00366 obp = obp_begin; 00367 if (!inmixer) 00368 obp += writepos/iAdvance*oAdvance; 00369 00370 cp_fields(dsb, ibp, obp, iAdvance, oAdvance, size, 0, 1 << DSOUND_FREQSHIFT); 00371 return; 00372 } 00373 00374 /* Mix in different sample rates */ 00375 TRACE("(%p) Adjusting frequency: %d -> %d\n", dsb, dsb->freq, dsb->device->pwfx->nSamplesPerSec); 00376 00377 target_writepos = DSOUND_secpos_to_bufpos(dsb, writepos, dsb->sec_mixpos, &freqAcc); 00378 overshot = freqAcc >> DSOUND_FREQSHIFT; 00379 if (overshot) 00380 { 00381 if (overshot >= size) 00382 return; 00383 size -= overshot; 00384 writepos += overshot * iAdvance; 00385 if (writepos >= dsb->buflen) 00386 return; 00387 ibp = dsb->buffer->memory + writepos; 00388 freqAcc &= (1 << DSOUND_FREQSHIFT) - 1; 00389 TRACE("Overshot: %d, freqAcc: %04x\n", overshot, freqAcc); 00390 } 00391 00392 if (!inmixer) 00393 obp = obp_begin + target_writepos; 00394 else obp = obp_begin; 00395 00396 /* FIXME: Small problem here when we're overwriting buf_mixpos, it then STILL uses old freqAcc, not sure if it matters or not */ 00397 cp_fields(dsb, ibp, obp, iAdvance, oAdvance, size, freqAcc, dsb->freqAdjust); 00398 } 00399 00403 static LPBYTE DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, INT len) 00404 { 00405 INT i; 00406 BYTE *bpc; 00407 INT16 *bps, *mems; 00408 DWORD vLeft, vRight; 00409 INT nChannels = dsb->device->pwfx->nChannels; 00410 LPBYTE mem = (dsb->tmp_buffer ? dsb->tmp_buffer : dsb->buffer->memory) + dsb->buf_mixpos; 00411 00412 if (dsb->resampleinmixer) 00413 mem = dsb->device->tmp_buffer; 00414 00415 TRACE("(%p,%d)\n",dsb,len); 00416 TRACE("left = %x, right = %x\n", dsb->volpan.dwTotalLeftAmpFactor, 00417 dsb->volpan.dwTotalRightAmpFactor); 00418 00419 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) && 00420 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) && 00421 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D)) 00422 return NULL; /* Nothing to do */ 00423 00424 if (nChannels != 1 && nChannels != 2) 00425 { 00426 FIXME("There is no support for %d channels\n", nChannels); 00427 return NULL; 00428 } 00429 00430 if (dsb->device->pwfx->wBitsPerSample != 8 && dsb->device->pwfx->wBitsPerSample != 16) 00431 { 00432 FIXME("There is no support for %d bpp\n", dsb->device->pwfx->wBitsPerSample); 00433 return NULL; 00434 } 00435 00436 if (dsb->device->tmp_buffer_len < len || !dsb->device->tmp_buffer) 00437 { 00438 /* If we just resampled in DSOUND_MixToTemporary, we shouldn't need to resize here */ 00439 assert(!dsb->resampleinmixer); 00440 dsb->device->tmp_buffer_len = len; 00441 if (dsb->device->tmp_buffer) 00442 dsb->device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsb->device->tmp_buffer, len); 00443 else 00444 dsb->device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, len); 00445 } 00446 00447 bpc = dsb->device->tmp_buffer; 00448 bps = (INT16 *)bpc; 00449 mems = (INT16 *)mem; 00450 vLeft = dsb->volpan.dwTotalLeftAmpFactor; 00451 if (nChannels > 1) 00452 vRight = dsb->volpan.dwTotalRightAmpFactor; 00453 else 00454 vRight = vLeft; 00455 00456 switch (dsb->device->pwfx->wBitsPerSample) { 00457 case 8: 00458 /* 8-bit WAV is unsigned, but we need to operate */ 00459 /* on signed data for this to work properly */ 00460 for (i = 0; i < len-1; i+=2) { 00461 *(bpc++) = (((*(mem++) - 128) * vLeft) >> 16) + 128; 00462 *(bpc++) = (((*(mem++) - 128) * vRight) >> 16) + 128; 00463 } 00464 if (len % 2 == 1 && nChannels == 1) 00465 *(bpc++) = (((*(mem++) - 128) * vLeft) >> 16) + 128; 00466 break; 00467 case 16: 00468 /* 16-bit WAV is signed -- much better */ 00469 for (i = 0; i < len-3; i += 4) { 00470 *(bps++) = (*(mems++) * vLeft) >> 16; 00471 *(bps++) = (*(mems++) * vRight) >> 16; 00472 } 00473 if (len % 4 == 2 && nChannels == 1) 00474 *(bps++) = ((INT)*(mems++) * vLeft) >> 16; 00475 break; 00476 } 00477 return dsb->device->tmp_buffer; 00478 } 00479 00493 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen) 00494 { 00495 INT len = fraglen, ilen; 00496 BYTE *ibuf = (dsb->tmp_buffer ? dsb->tmp_buffer : dsb->buffer->memory) + dsb->buf_mixpos, *volbuf; 00497 DWORD oldpos, mixbufpos; 00498 00499 TRACE("buf_mixpos=%d/%d sec_mixpos=%d/%d\n", dsb->buf_mixpos, dsb->tmp_buffer_len, dsb->sec_mixpos, dsb->buflen); 00500 TRACE("(%p,%d,%d)\n",dsb,writepos,fraglen); 00501 00502 assert(dsb->buf_mixpos + len <= dsb->tmp_buffer_len); 00503 00504 if (len % dsb->device->pwfx->nBlockAlign) { 00505 INT nBlockAlign = dsb->device->pwfx->nBlockAlign; 00506 ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign); 00507 len -= len % nBlockAlign; /* data alignment */ 00508 } 00509 00510 /* Resample buffer to temporary buffer specifically allocated for this purpose, if needed */ 00511 DSOUND_MixToTemporary(dsb, dsb->sec_mixpos, DSOUND_bufpos_to_secpos(dsb, dsb->buf_mixpos+len) - dsb->sec_mixpos, TRUE); 00512 if (dsb->resampleinmixer) 00513 ibuf = dsb->device->tmp_buffer; 00514 00515 /* Apply volume if needed */ 00516 volbuf = DSOUND_MixerVol(dsb, len); 00517 if (volbuf) 00518 ibuf = volbuf; 00519 00520 mixbufpos = DSOUND_bufpos_to_mixpos(dsb->device, writepos); 00521 /* Now mix the temporary buffer into the devices main buffer */ 00522 if ((writepos + len) <= dsb->device->buflen) 00523 dsb->device->mixfunction(ibuf, dsb->device->mix_buffer + mixbufpos, len); 00524 else 00525 { 00526 DWORD todo = dsb->device->buflen - writepos; 00527 dsb->device->mixfunction(ibuf, dsb->device->mix_buffer + mixbufpos, todo); 00528 dsb->device->mixfunction(ibuf + todo, dsb->device->mix_buffer, len - todo); 00529 } 00530 00531 oldpos = dsb->sec_mixpos; 00532 dsb->buf_mixpos += len; 00533 00534 if (dsb->buf_mixpos >= dsb->tmp_buffer_len) { 00535 if (dsb->buf_mixpos > dsb->tmp_buffer_len) 00536 ERR("Mixpos (%u) past buflen (%u), capping...\n", dsb->buf_mixpos, dsb->tmp_buffer_len); 00537 if (dsb->playflags & DSBPLAY_LOOPING) { 00538 dsb->buf_mixpos -= dsb->tmp_buffer_len; 00539 } else if (dsb->buf_mixpos >= dsb->tmp_buffer_len) { 00540 dsb->buf_mixpos = dsb->sec_mixpos = 0; 00541 dsb->state = STATE_STOPPED; 00542 } 00543 DSOUND_RecalcFreqAcc(dsb); 00544 } 00545 00546 dsb->sec_mixpos = DSOUND_bufpos_to_secpos(dsb, dsb->buf_mixpos); 00547 ilen = DSOUND_BufPtrDiff(dsb->buflen, dsb->sec_mixpos, oldpos); 00548 /* check for notification positions */ 00549 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY && 00550 dsb->state != STATE_STARTING) { 00551 DSOUND_CheckEvent(dsb, oldpos, ilen); 00552 } 00553 00554 /* increase mix position */ 00555 dsb->primary_mixpos += len; 00556 if (dsb->primary_mixpos >= dsb->device->buflen) 00557 dsb->primary_mixpos -= dsb->device->buflen; 00558 return len; 00559 } 00560 00573 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen) 00574 { 00575 /* The buffer's primary_mixpos may be before or after the device 00576 * buffer's mixpos, but both must be ahead of writepos. */ 00577 DWORD primary_done; 00578 00579 TRACE("(%p,%d,%d)\n",dsb,writepos,mixlen); 00580 TRACE("writepos=%d, buf_mixpos=%d, primary_mixpos=%d, mixlen=%d\n", writepos, dsb->buf_mixpos, dsb->primary_mixpos, mixlen); 00581 TRACE("looping=%d, leadin=%d, buflen=%d\n", dsb->playflags, dsb->leadin, dsb->tmp_buffer_len); 00582 00583 /* If leading in, only mix about 20 ms, and 'skip' mixing the rest, for more fluid pointer advancement */ 00584 if (dsb->leadin && dsb->state == STATE_STARTING) 00585 { 00586 if (mixlen > 2 * dsb->device->fraglen) 00587 { 00588 dsb->primary_mixpos += mixlen - 2 * dsb->device->fraglen; 00589 dsb->primary_mixpos %= dsb->device->buflen; 00590 } 00591 } 00592 dsb->leadin = FALSE; 00593 00594 /* calculate how much pre-buffering has already been done for this buffer */ 00595 primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos); 00596 00597 /* sanity */ 00598 if(mixlen < primary_done) 00599 { 00600 /* Should *NEVER* happen */ 00601 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); 00602 return 0; 00603 } 00604 00605 /* take into account already mixed data */ 00606 mixlen -= primary_done; 00607 00608 TRACE("primary_done=%d, mixlen (primary) = %i\n", primary_done, mixlen); 00609 00610 if (!mixlen) 00611 return primary_done; 00612 00613 /* First try to mix to the end of the buffer if possible 00614 * Theoretically it would allow for better optimization 00615 */ 00616 if (mixlen + dsb->buf_mixpos >= dsb->tmp_buffer_len) 00617 { 00618 DWORD newmixed, mixfirst = dsb->tmp_buffer_len - dsb->buf_mixpos; 00619 newmixed = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixfirst); 00620 mixlen -= newmixed; 00621 00622 if (dsb->playflags & DSBPLAY_LOOPING) 00623 while (newmixed && mixlen) 00624 { 00625 mixfirst = (dsb->tmp_buffer_len < mixlen ? dsb->tmp_buffer_len : mixlen); 00626 newmixed = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixfirst); 00627 mixlen -= newmixed; 00628 } 00629 } 00630 else DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixlen); 00631 00632 /* re-calculate the primary done */ 00633 primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos); 00634 00635 TRACE("new primary_mixpos=%d, total mixed data=%d\n", dsb->primary_mixpos, primary_done); 00636 00637 /* Report back the total prebuffered amount for this buffer */ 00638 return primary_done; 00639 } 00640 00656 static DWORD DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, DWORD mixlen, BOOL mustlock, BOOL recover, BOOL *all_stopped) 00657 { 00658 INT i, len; 00659 DWORD minlen = 0; 00660 IDirectSoundBufferImpl *dsb; 00661 BOOL gotall = TRUE; 00662 00663 /* unless we find a running buffer, all have stopped */ 00664 *all_stopped = TRUE; 00665 00666 TRACE("(%d,%d,%d)\n", writepos, mixlen, recover); 00667 for (i = 0; i < device->nrofbuffers; i++) { 00668 dsb = device->buffers[i]; 00669 00670 TRACE("MixToPrimary for %p, state=%d\n", dsb, dsb->state); 00671 00672 if (dsb->buflen && dsb->state && !dsb->hwbuf) { 00673 TRACE("Checking %p, mixlen=%d\n", dsb, mixlen); 00674 if (!RtlAcquireResourceShared(&dsb->lock, mustlock)) 00675 { 00676 gotall = FALSE; 00677 continue; 00678 } 00679 /* if buffer is stopping it is stopped now */ 00680 if (dsb->state == STATE_STOPPING) { 00681 dsb->state = STATE_STOPPED; 00682 DSOUND_CheckEvent(dsb, 0, 0); 00683 } else if (dsb->state != STATE_STOPPED) { 00684 00685 /* if recovering, reset the mix position */ 00686 if ((dsb->state == STATE_STARTING) || recover) { 00687 dsb->primary_mixpos = writepos; 00688 } 00689 00690 /* if the buffer was starting, it must be playing now */ 00691 if (dsb->state == STATE_STARTING) 00692 dsb->state = STATE_PLAYING; 00693 00694 /* mix next buffer into the main buffer */ 00695 len = DSOUND_MixOne(dsb, writepos, mixlen); 00696 00697 if (!minlen) minlen = len; 00698 00699 /* record the minimum length mixed from all buffers */ 00700 /* we only want to return the length which *all* buffers have mixed */ 00701 else if (len) minlen = (len < minlen) ? len : minlen; 00702 00703 *all_stopped = FALSE; 00704 } 00705 RtlReleaseResource(&dsb->lock); 00706 } 00707 } 00708 00709 TRACE("Mixed at least %d from all buffers\n", minlen); 00710 if (!gotall) return 0; 00711 return minlen; 00712 } 00713 00724 static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) 00725 { 00726 DWORD prebuf_frags, wave_writepos, wave_fragpos, i; 00727 TRACE("(%p)\n", device); 00728 00729 /* calculate the current wave frag position */ 00730 wave_fragpos = (device->pwplay + device->pwqueue) % device->helfrags; 00731 00732 /* calculate the current wave write position */ 00733 wave_writepos = wave_fragpos * device->fraglen; 00734 00735 TRACE("wave_fragpos = %i, wave_writepos = %i, pwqueue = %i, prebuf = %i\n", 00736 wave_fragpos, wave_writepos, device->pwqueue, device->prebuf); 00737 00738 if (!force) 00739 { 00740 /* check remaining prebuffered frags */ 00741 prebuf_frags = device->mixpos / device->fraglen; 00742 if (prebuf_frags == device->helfrags) 00743 --prebuf_frags; 00744 TRACE("wave_fragpos = %d, mixpos_frags = %d\n", wave_fragpos, prebuf_frags); 00745 if (prebuf_frags < wave_fragpos) 00746 prebuf_frags += device->helfrags; 00747 prebuf_frags -= wave_fragpos; 00748 TRACE("wanted prebuf_frags = %d\n", prebuf_frags); 00749 } 00750 else 00751 /* buffer the maximum amount of frags */ 00752 prebuf_frags = device->prebuf; 00753 00754 /* limit to the queue we have left */ 00755 if ((prebuf_frags + device->pwqueue) > device->prebuf) 00756 prebuf_frags = device->prebuf - device->pwqueue; 00757 00758 TRACE("prebuf_frags = %i\n", prebuf_frags); 00759 00760 /* adjust queue */ 00761 device->pwqueue += prebuf_frags; 00762 00763 /* get out of CS when calling the wave system */ 00764 LeaveCriticalSection(&(device->mixlock)); 00765 /* **** */ 00766 00767 /* queue up the new buffers */ 00768 for(i=0; i<prebuf_frags; i++){ 00769 TRACE("queueing wave buffer %i\n", wave_fragpos); 00770 waveOutWrite(device->hwo, &device->pwave[wave_fragpos], sizeof(WAVEHDR)); 00771 wave_fragpos++; 00772 wave_fragpos %= device->helfrags; 00773 } 00774 00775 /* **** */ 00776 EnterCriticalSection(&(device->mixlock)); 00777 00778 TRACE("queue now = %i\n", device->pwqueue); 00779 } 00780 00786 static void DSOUND_PerformMix(DirectSoundDevice *device) 00787 { 00788 TRACE("(%p)\n", device); 00789 00790 /* **** */ 00791 EnterCriticalSection(&(device->mixlock)); 00792 00793 if (device->priolevel != DSSCL_WRITEPRIMARY) { 00794 BOOL recover = FALSE, all_stopped = FALSE; 00795 DWORD playpos, writepos, writelead, maxq, frag, prebuff_max, prebuff_left, size1, size2, mixplaypos, mixplaypos2; 00796 LPVOID buf1, buf2; 00797 BOOL lock = (device->hwbuf && !(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK)); 00798 BOOL mustlock = FALSE; 00799 int nfiller; 00800 00801 /* the sound of silence */ 00802 nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0; 00803 00804 /* get the position in the primary buffer */ 00805 if (DSOUND_PrimaryGetPosition(device, &playpos, &writepos) != 0){ 00806 LeaveCriticalSection(&(device->mixlock)); 00807 return; 00808 } 00809 00810 TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n", 00811 playpos,writepos,device->playpos,device->mixpos,device->buflen); 00812 assert(device->playpos < device->buflen); 00813 00814 mixplaypos = DSOUND_bufpos_to_mixpos(device, device->playpos); 00815 mixplaypos2 = DSOUND_bufpos_to_mixpos(device, playpos); 00816 00817 /* calc maximum prebuff */ 00818 prebuff_max = (device->prebuf * device->fraglen); 00819 if (!device->hwbuf && playpos + prebuff_max >= device->helfrags * device->fraglen) 00820 prebuff_max += device->buflen - device->helfrags * device->fraglen; 00821 00822 /* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */ 00823 prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos); 00824 writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos); 00825 00826 /* check for underrun. underrun occurs when the write position passes the mix position 00827 * also wipe out just-played sound data */ 00828 if((prebuff_left > prebuff_max) || (device->state == STATE_STOPPED) || (device->state == STATE_STARTING)){ 00829 if (device->state == STATE_STOPPING || device->state == STATE_PLAYING) 00830 WARN("Probable buffer underrun\n"); 00831 else TRACE("Buffer starting or buffer underrun\n"); 00832 00833 /* recover mixing for all buffers */ 00834 recover = TRUE; 00835 00836 /* reset mix position to write position */ 00837 device->mixpos = writepos; 00838 00839 ZeroMemory(device->mix_buffer, device->mix_buffer_len); 00840 ZeroMemory(device->buffer, device->buflen); 00841 } else if (playpos < device->playpos) { 00842 buf1 = device->buffer + device->playpos; 00843 buf2 = device->buffer; 00844 size1 = device->buflen - device->playpos; 00845 size2 = playpos; 00846 FillMemory(device->mix_buffer + mixplaypos, device->mix_buffer_len - mixplaypos, 0); 00847 FillMemory(device->mix_buffer, mixplaypos2, 0); 00848 if (lock) 00849 IDsDriverBuffer_Lock(device->hwbuf, &buf1, &size1, &buf2, &size2, device->playpos, size1+size2, 0); 00850 FillMemory(buf1, size1, nfiller); 00851 if (playpos && (!buf2 || !size2)) 00852 FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__, device->playpos, device->mixpos, playpos, writepos); 00853 FillMemory(buf2, size2, nfiller); 00854 if (lock) 00855 IDsDriverBuffer_Unlock(device->hwbuf, buf1, size1, buf2, size2); 00856 } else { 00857 buf1 = device->buffer + device->playpos; 00858 buf2 = NULL; 00859 size1 = playpos - device->playpos; 00860 size2 = 0; 00861 FillMemory(device->mix_buffer + mixplaypos, mixplaypos2 - mixplaypos, 0); 00862 if (lock) 00863 IDsDriverBuffer_Lock(device->hwbuf, &buf1, &size1, &buf2, &size2, device->playpos, size1+size2, 0); 00864 FillMemory(buf1, size1, nfiller); 00865 if (buf2 && size2) 00866 { 00867 FIXME("%d: There should be no additional buffer here!!\n", __LINE__); 00868 FillMemory(buf2, size2, nfiller); 00869 } 00870 if (lock) 00871 IDsDriverBuffer_Unlock(device->hwbuf, buf1, size1, buf2, size2); 00872 } 00873 device->playpos = playpos; 00874 00875 /* find the maximum we can prebuffer from current write position */ 00876 maxq = (writelead < prebuff_max) ? (prebuff_max - writelead) : 0; 00877 00878 TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n", 00879 prebuff_left, device->prebuf, device->fraglen, prebuff_max, writelead); 00880 00881 /* Do we risk an 'underrun' if we don't advance pointer? */ 00882 if (writelead/device->fraglen <= ds_snd_queue_min || recover) 00883 mustlock = TRUE; 00884 00885 if (lock) 00886 IDsDriverBuffer_Lock(device->hwbuf, &buf1, &size1, &buf2, &size2, writepos, maxq, 0); 00887 00888 /* do the mixing */ 00889 frag = DSOUND_MixToPrimary(device, writepos, maxq, mustlock, recover, &all_stopped); 00890 00891 if (frag + writepos > device->buflen) 00892 { 00893 DWORD todo = device->buflen - writepos; 00894 device->normfunction(device->mix_buffer + DSOUND_bufpos_to_mixpos(device, writepos), device->buffer + writepos, todo); 00895 device->normfunction(device->mix_buffer, device->buffer, frag - todo); 00896 } 00897 else 00898 device->normfunction(device->mix_buffer + DSOUND_bufpos_to_mixpos(device, writepos), device->buffer + writepos, frag); 00899 00900 /* update the mix position, taking wrap-around into account */ 00901 device->mixpos = writepos + frag; 00902 device->mixpos %= device->buflen; 00903 00904 if (lock) 00905 { 00906 DWORD frag2 = (frag > size1 ? frag - size1 : 0); 00907 frag -= frag2; 00908 if (frag2 > size2) 00909 { 00910 FIXME("Buffering too much! (%d, %d, %d, %d)\n", maxq, frag, size2, frag2 - size2); 00911 frag2 = size2; 00912 } 00913 IDsDriverBuffer_Unlock(device->hwbuf, buf1, frag, buf2, frag2); 00914 } 00915 00916 /* update prebuff left */ 00917 prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos); 00918 00919 /* check if have a whole fragment */ 00920 if (prebuff_left >= device->fraglen){ 00921 00922 /* update the wave queue if using wave system */ 00923 if (!device->hwbuf) 00924 DSOUND_WaveQueue(device, FALSE); 00925 00926 /* buffers are full. start playing if applicable */ 00927 if(device->state == STATE_STARTING){ 00928 TRACE("started primary buffer\n"); 00929 if(DSOUND_PrimaryPlay(device) != DS_OK){ 00930 WARN("DSOUND_PrimaryPlay failed\n"); 00931 } 00932 else{ 00933 /* we are playing now */ 00934 device->state = STATE_PLAYING; 00935 } 00936 } 00937 00938 /* buffers are full. start stopping if applicable */ 00939 if(device->state == STATE_STOPPED){ 00940 TRACE("restarting primary buffer\n"); 00941 if(DSOUND_PrimaryPlay(device) != DS_OK){ 00942 WARN("DSOUND_PrimaryPlay failed\n"); 00943 } 00944 else{ 00945 /* start stopping again. as soon as there is no more data, it will stop */ 00946 device->state = STATE_STOPPING; 00947 } 00948 } 00949 } 00950 00951 /* if device was stopping, its for sure stopped when all buffers have stopped */ 00952 else if((all_stopped == TRUE) && (device->state == STATE_STOPPING)){ 00953 TRACE("All buffers have stopped. Stopping primary buffer\n"); 00954 device->state = STATE_STOPPED; 00955 00956 /* stop the primary buffer now */ 00957 DSOUND_PrimaryStop(device); 00958 } 00959 00960 } else { 00961 00962 /* update the wave queue if using wave system */ 00963 if (!device->hwbuf) 00964 DSOUND_WaveQueue(device, TRUE); 00965 else 00966 /* Keep alsa happy, which needs GetPosition called once every 10 ms */ 00967 IDsDriverBuffer_GetPosition(device->hwbuf, NULL, NULL); 00968 00969 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */ 00970 if (device->state == STATE_STARTING) { 00971 if (DSOUND_PrimaryPlay(device) != DS_OK) 00972 WARN("DSOUND_PrimaryPlay failed\n"); 00973 else 00974 device->state = STATE_PLAYING; 00975 } 00976 else if (device->state == STATE_STOPPING) { 00977 if (DSOUND_PrimaryStop(device) != DS_OK) 00978 WARN("DSOUND_PrimaryStop failed\n"); 00979 else 00980 device->state = STATE_STOPPED; 00981 } 00982 } 00983 00984 LeaveCriticalSection(&(device->mixlock)); 00985 /* **** */ 00986 } 00987 00988 void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser, 00989 DWORD_PTR dw1, DWORD_PTR dw2) 00990 { 00991 DirectSoundDevice * device = (DirectSoundDevice*)dwUser; 00992 DWORD start_time = GetTickCount(); 00993 DWORD end_time; 00994 TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2); 00995 TRACE("entering at %d\n", start_time); 00996 00997 if (DSOUND_renderer[device->drvdesc.dnDevNode] != device) { 00998 ERR("dsound died without killing us?\n"); 00999 timeKillEvent(timerID); 01000 timeEndPeriod(DS_TIME_RES); 01001 return; 01002 } 01003 01004 RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE); 01005 01006 if (device->ref) 01007 DSOUND_PerformMix(device); 01008 01009 RtlReleaseResource(&(device->buffer_list_lock)); 01010 01011 end_time = GetTickCount(); 01012 TRACE("completed processing at %d, duration = %d\n", end_time, end_time - start_time); 01013 } 01014 01015 void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) 01016 { 01017 DirectSoundDevice * device = (DirectSoundDevice*)dwUser; 01018 TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo,msg,dwUser,dw1,dw2); 01019 TRACE("entering at %d, msg=%08x(%s)\n", GetTickCount(), msg, 01020 msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" : 01021 msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN"); 01022 01023 /* check if packet completed from wave driver */ 01024 if (msg == MM_WOM_DONE) { 01025 01026 /* **** */ 01027 EnterCriticalSection(&(device->mixlock)); 01028 01029 TRACE("done playing primary pos=%d\n", device->pwplay * device->fraglen); 01030 01031 /* update playpos */ 01032 device->pwplay++; 01033 device->pwplay %= device->helfrags; 01034 01035 /* sanity */ 01036 if(device->pwqueue == 0){ 01037 ERR("Wave queue corrupted!\n"); 01038 } 01039 01040 /* update queue */ 01041 device->pwqueue--; 01042 01043 LeaveCriticalSection(&(device->mixlock)); 01044 /* **** */ 01045 } 01046 TRACE("completed\n"); 01047 } Generated on Fri May 25 2012 04:15:50 for ReactOS by
1.7.6.1
|