ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

mixer.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 doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.