Signed/unsigned fix and code cleanup.
[wine.git] / dlls / dsound / mixer.c
blob75ffb9283bf4e424e9ea7349b6b45e94f49763df
1 /* DirectSound
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2002 TransGaming Technologies, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <math.h> /* Insomnia - pow() function */
26 #define NONAMELESSSTRUCT
27 #define NONAMELESSUNION
28 #include "windef.h"
29 #include "winbase.h"
30 #include "mmsystem.h"
31 #include "winreg.h"
32 #include "winternl.h"
33 #include "wine/debug.h"
34 #include "dsound.h"
35 #include "dsdriver.h"
36 #include "dsound_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
40 void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
42 double temp;
43 TRACE("(%p)\n",volpan);
45 TRACE("Vol=%ld Pan=%ld\n", volpan->lVolume, volpan->lPan);
46 /* the AmpFactors are expressed in 16.16 fixed point */
47 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 0xffff);
48 /* FIXME: dwPan{Left|Right}AmpFactor */
50 /* FIXME: use calculated vol and pan ampfactors */
51 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
52 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff);
53 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
54 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff);
56 TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
59 void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan)
61 double left,right;
62 TRACE("(%p)\n",volpan);
64 TRACE("left=%lx, right=%lx\n",volpan->dwTotalLeftAmpFactor,volpan->dwTotalRightAmpFactor);
65 if (volpan->dwTotalLeftAmpFactor==0)
66 left=-10000;
67 else
68 left=600 * log(((double)volpan->dwTotalLeftAmpFactor) / 0xffff) / log(2);
69 if (volpan->dwTotalRightAmpFactor==0)
70 right=-10000;
71 else
72 right=600 * log(((double)volpan->dwTotalRightAmpFactor) / 0xffff) / log(2);
73 if (left<right)
75 volpan->lVolume=right;
76 volpan->dwVolAmpFactor=volpan->dwTotalRightAmpFactor;
78 else
80 volpan->lVolume=left;
81 volpan->dwVolAmpFactor=volpan->dwTotalLeftAmpFactor;
83 if (volpan->lVolume < -10000)
84 volpan->lVolume=-10000;
85 volpan->lPan=right-left;
86 if (volpan->lPan < -10000)
87 volpan->lPan=-10000;
89 TRACE("Vol=%ld Pan=%ld\n", volpan->lVolume, volpan->lPan);
92 void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
94 DWORD sw;
95 TRACE("(%p)\n",dsb);
97 sw = dsb->pwfx->nChannels * (dsb->pwfx->wBitsPerSample / 8);
98 /* calculate the 10ms write lead */
99 dsb->writelead = (dsb->freq / 100) * sw;
102 void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
104 int i;
105 DWORD offset;
106 LPDSBPOSITIONNOTIFY event;
107 TRACE("(%p,%d)\n",dsb,len);
109 if (dsb->nrofnotifies == 0)
110 return;
112 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
113 dsb, dsb->buflen, dsb->playpos, len);
114 for (i = 0; i < dsb->nrofnotifies ; i++) {
115 event = dsb->notifies + i;
116 offset = event->dwOffset;
117 TRACE("checking %d, position %ld, event = %p\n",
118 i, offset, event->hEventNotify);
119 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
120 /* OK. [Inside DirectX, p274] */
121 /* */
122 /* This also means we can't sort the entries by offset, */
123 /* because DSBPN_OFFSETSTOP == -1 */
124 if (offset == DSBPN_OFFSETSTOP) {
125 if (dsb->state == STATE_STOPPED) {
126 SetEvent(event->hEventNotify);
127 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
128 return;
129 } else
130 return;
132 if ((dsb->playpos + len) >= dsb->buflen) {
133 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
134 (offset >= dsb->playpos)) {
135 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
136 SetEvent(event->hEventNotify);
138 } else {
139 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
140 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
141 SetEvent(event->hEventNotify);
147 /* WAV format info can be found at:
149 * http://www.cwi.nl/ftp/audio/AudioFormats.part2
150 * ftp://ftp.cwi.nl/pub/audio/RIFF-format
152 * Import points to remember:
153 * 8-bit WAV is unsigned
154 * 16-bit WAV is signed
156 /* Use the same formulas as pcmconverter.c */
157 static inline INT16 cvtU8toS16(BYTE b)
159 return (short)((b+(b << 8))-32768);
162 static inline BYTE cvtS16toU8(INT16 s)
164 return (s >> 8) ^ (unsigned char)0x80;
167 static inline void cp_fields(const IDirectSoundBufferImpl *dsb, BYTE *ibuf, BYTE *obuf )
169 INT fl,fr;
171 if (dsb->pwfx->wBitsPerSample == 8) {
172 if (dsb->dsound->pwfx->wBitsPerSample == 8 &&
173 dsb->dsound->pwfx->nChannels == dsb->pwfx->nChannels) {
174 /* avoid needless 8->16->8 conversion */
175 *obuf=*ibuf;
176 if (dsb->pwfx->nChannels==2)
177 *(obuf+1)=*(ibuf+1);
178 return;
180 fl = cvtU8toS16(*ibuf);
181 fr = (dsb->pwfx->nChannels==2 ? cvtU8toS16(*(ibuf + 1)) : fl);
182 } else {
183 fl = *((INT16 *)ibuf);
184 fr = (dsb->pwfx->nChannels==2 ? *(((INT16 *)ibuf) + 1) : fl);
187 if (dsb->dsound->pwfx->nChannels == 2) {
188 if (dsb->dsound->pwfx->wBitsPerSample == 8) {
189 *obuf = cvtS16toU8(fl);
190 *(obuf + 1) = cvtS16toU8(fr);
191 return;
193 if (dsb->dsound->pwfx->wBitsPerSample == 16) {
194 *((INT16 *)obuf) = fl;
195 *(((INT16 *)obuf) + 1) = fr;
196 return;
199 if (dsb->dsound->pwfx->nChannels == 1) {
200 fl = (fl + fr) >> 1;
201 if (dsb->dsound->pwfx->wBitsPerSample == 8) {
202 *obuf = cvtS16toU8(fl);
203 return;
205 if (dsb->dsound->pwfx->wBitsPerSample == 16) {
206 *((INT16 *)obuf) = fl;
207 return;
212 /* Now with PerfectPitch (tm) technology */
213 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
215 INT i, size, ipos, ilen;
216 BYTE *ibp, *obp;
217 INT iAdvance = dsb->pwfx->nBlockAlign;
218 INT oAdvance = dsb->dsound->pwfx->nBlockAlign;
220 ibp = dsb->buffer->memory + dsb->buf_mixpos;
221 obp = buf;
223 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
224 /* Check for the best case */
225 if ((dsb->freq == dsb->dsound->pwfx->nSamplesPerSec) &&
226 (dsb->pwfx->wBitsPerSample == dsb->dsound->pwfx->wBitsPerSample) &&
227 (dsb->pwfx->nChannels == dsb->dsound->pwfx->nChannels)) {
228 INT bytesleft = dsb->buflen - dsb->buf_mixpos;
229 TRACE("(%p) Best case\n", dsb);
230 if (len <= bytesleft )
231 CopyMemory(obp, ibp, len);
232 else { /* wrap */
233 CopyMemory(obp, ibp, bytesleft);
234 CopyMemory(obp + bytesleft, dsb->buffer->memory, len - bytesleft);
236 return len;
239 /* Check for same sample rate */
240 if (dsb->freq == dsb->dsound->pwfx->nSamplesPerSec) {
241 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
242 dsb->freq, dsb->dsound->pwfx->nSamplesPerSec);
243 ilen = 0;
244 for (i = 0; i < len; i += oAdvance) {
245 cp_fields(dsb, ibp, obp );
246 ibp += iAdvance;
247 ilen += iAdvance;
248 obp += oAdvance;
249 if (ibp >= (BYTE *)(dsb->buffer->memory + dsb->buflen))
250 ibp = dsb->buffer->memory; /* wrap */
252 return (ilen);
255 /* Mix in different sample rates */
256 /* */
257 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
258 /* Patent Pending :-] */
260 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
261 * TransGaming Technologies Inc. */
263 /* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
264 dsb, dsb->freq, dsb->dsound->pwfx->nSamplesPerSec); */
266 size = len / oAdvance;
267 ilen = 0;
268 ipos = dsb->buf_mixpos;
269 for (i = 0; i < size; i++) {
270 cp_fields(dsb, (dsb->buffer->memory + ipos), obp);
271 obp += oAdvance;
272 dsb->freqAcc += dsb->freqAdjust;
273 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
274 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
275 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
276 ipos += adv; ilen += adv;
277 ipos %= dsb->buflen;
280 return ilen;
283 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
285 INT i;
286 BYTE *bpc = buf;
287 INT16 *bps = (INT16 *) buf;
289 TRACE("(%p,%p,%d)\n",dsb,buf,len);
290 TRACE("left = %lx, right = %lx\n", dsb->cvolpan.dwTotalLeftAmpFactor,
291 dsb->cvolpan.dwTotalRightAmpFactor);
293 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->cvolpan.lPan == 0)) &&
294 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->cvolpan.lVolume == 0)) &&
295 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
296 return; /* Nothing to do */
298 /* If we end up with some bozo coder using panning or 3D sound */
299 /* with a mono primary buffer, it could sound very weird using */
300 /* this method. Oh well, tough patooties. */
302 switch (dsb->dsound->pwfx->wBitsPerSample) {
303 case 8:
304 /* 8-bit WAV is unsigned, but we need to operate */
305 /* on signed data for this to work properly */
306 switch (dsb->dsound->pwfx->nChannels) {
307 case 1:
308 for (i = 0; i < len; i++) {
309 INT val = *bpc - 128;
310 val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
311 *bpc = val + 128;
312 bpc++;
314 break;
315 case 2:
316 for (i = 0; i < len; i+=2) {
317 INT val = *bpc - 128;
318 val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
319 *bpc++ = val + 128;
320 val = *bpc - 128;
321 val = (val * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
322 *bpc = val + 128;
323 bpc++;
325 break;
326 default:
327 FIXME("doesn't support %d channels\n", dsb->dsound->pwfx->nChannels);
328 break;
330 break;
331 case 16:
332 /* 16-bit WAV is signed -- much better */
333 switch (dsb->dsound->pwfx->nChannels) {
334 case 1:
335 for (i = 0; i < len; i += 2) {
336 *bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
337 bps++;
339 break;
340 case 2:
341 for (i = 0; i < len; i += 4) {
342 *bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
343 bps++;
344 *bps = (*bps * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
345 bps++;
347 break;
348 default:
349 FIXME("doesn't support %d channels\n", dsb->dsound->pwfx->nChannels);
350 break;
352 break;
353 default:
354 FIXME("doesn't support %d bit samples\n", dsb->dsound->pwfx->wBitsPerSample);
355 break;
359 static LPBYTE DSOUND_tmpbuffer(IDirectSoundImpl *dsound, DWORD len)
361 TRACE("(%p,%ld)\n",dsound,len);
363 if (len > dsound->tmp_buffer_len) {
364 if (dsound->tmp_buffer)
365 dsound->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsound->tmp_buffer, len);
366 else
367 dsound->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, len);
369 dsound->tmp_buffer_len = len;
372 return dsound->tmp_buffer;
375 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
377 INT i, len, ilen, field, nBlockAlign, todo;
378 BYTE *buf, *ibuf;
380 TRACE("(%p,%ld,%ld)\n",dsb,writepos,fraglen);
382 len = fraglen;
383 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
384 INT temp = MulDiv(dsb->dsound->pwfx->nAvgBytesPerSec, dsb->buflen,
385 dsb->nAvgBytesPerSec) -
386 MulDiv(dsb->dsound->pwfx->nAvgBytesPerSec, dsb->buf_mixpos,
387 dsb->nAvgBytesPerSec);
388 len = min(len, temp);
390 nBlockAlign = dsb->dsound->pwfx->nBlockAlign;
391 len = len / nBlockAlign * nBlockAlign; /* data alignment */
393 if (len == 0) {
394 /* This should only happen if we aren't looping and temp < nBlockAlign */
395 return 0;
398 if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound, len)) == NULL)
399 return 0;
401 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
403 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
404 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
405 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
406 (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
407 DSOUND_MixerVol(dsb, ibuf, len);
409 if (dsb->dsound->pwfx->wBitsPerSample == 8) {
410 BYTE *obuf = dsb->dsound->buffer + writepos;
412 if ((writepos + len) <= dsb->dsound->buflen)
413 todo = len;
414 else
415 todo = dsb->dsound->buflen - writepos;
417 for (i = 0; i < todo; i++) {
418 /* 8-bit WAV is unsigned */
419 field = (*ibuf++ - 128);
420 field += (*obuf - 128);
421 if (field > 127) field = 127;
422 else if (field < -128) field = -128;
423 *obuf++ = field + 128;
426 if (todo < len) {
427 todo = len - todo;
428 obuf = dsb->dsound->buffer;
430 for (i = 0; i < todo; i++) {
431 /* 8-bit WAV is unsigned */
432 field = (*ibuf++ - 128);
433 field += (*obuf - 128);
434 if (field > 127) field = 127;
435 else if (field < -128) field = -128;
436 *obuf++ = field + 128;
439 } else {
440 INT16 *ibufs, *obufs;
442 ibufs = (INT16 *) ibuf;
443 obufs = (INT16 *)(dsb->dsound->buffer + writepos);
445 if ((writepos + len) <= dsb->dsound->buflen)
446 todo = len / 2;
447 else
448 todo = (dsb->dsound->buflen - writepos) / 2;
450 for (i = 0; i < todo; i++) {
451 /* 16-bit WAV is signed */
452 field = *ibufs++;
453 field += *obufs;
454 if (field > 32767) field = 32767;
455 else if (field < -32768) field = -32768;
456 *obufs++ = field;
459 if (todo < (len / 2)) {
460 todo = (len / 2) - todo;
461 obufs = (INT16 *)dsb->dsound->buffer;
463 for (i = 0; i < todo; i++) {
464 /* 16-bit WAV is signed */
465 field = *ibufs++;
466 field += *obufs;
467 if (field > 32767) field = 32767;
468 else if (field < -32768) field = -32768;
469 *obufs++ = field;
474 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
475 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
476 * not the MIX position... but if the sound buffer is bigger than our prebuffering
477 * (which must be the case for the streaming buffers that need this hack anyway)
478 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
479 dsb->leadin = FALSE;
482 dsb->buf_mixpos += ilen;
484 if (dsb->buf_mixpos >= dsb->buflen) {
485 if (dsb->playflags & DSBPLAY_LOOPING) {
486 /* wrap */
487 dsb->buf_mixpos %= dsb->buflen;
488 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
489 dsb->leadin = FALSE; /* HACK: see above */
493 return len;
496 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
498 INT ilen, field, nBlockAlign;
499 UINT i, todo;
500 BYTE *buf, *ibuf;
502 TRACE("(%p,%ld,%ld)\n",dsb,writepos,len);
504 nBlockAlign = dsb->dsound->pwfx->nBlockAlign;
505 len = len / nBlockAlign * nBlockAlign; /* data alignment */
507 if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound, len)) == NULL)
508 return;
510 TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);
512 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
513 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
514 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
515 (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
516 DSOUND_MixerVol(dsb, ibuf, len);
518 /* subtract instead of add, to phase out premixed data */
519 if (dsb->dsound->pwfx->wBitsPerSample == 8) {
520 BYTE *obuf = dsb->dsound->buffer + writepos;
522 if ((writepos + len) <= dsb->dsound->buflen)
523 todo = len;
524 else
525 todo = dsb->dsound->buflen - writepos;
527 for (i = 0; i < todo; i++) {
528 /* 8-bit WAV is unsigned */
529 field = (*ibuf++ - 128);
530 field -= (*obuf - 128);
531 if (field > 127) field = 127;
532 else if (field < -128) field = -128;
533 *obuf++ = field + 128;
536 if (todo < len) {
537 todo = len - todo;
538 obuf = dsb->dsound->buffer;
540 for (i = 0; i < todo; i++) {
541 /* 8-bit WAV is unsigned */
542 field = (*ibuf++ - 128);
543 field -= (*obuf - 128);
544 if (field > 127) field = 127;
545 else if (field < -128) field = -128;
546 *obuf++ = field + 128;
549 } else {
550 INT16 *ibufs, *obufs;
552 ibufs = (INT16 *) ibuf;
553 obufs = (INT16 *)(dsb->dsound->buffer + writepos);
555 if ((writepos + len) <= dsb->dsound->buflen)
556 todo = len / 2;
557 else
558 todo = (dsb->dsound->buflen - writepos) / 2;
560 for (i = 0; i < todo; i++) {
561 /* 16-bit WAV is signed */
562 field = *ibufs++;
563 field -= *obufs;
564 if (field > 32767) field = 32767;
565 else if (field < -32768) field = -32768;
566 *obufs++ = field;
569 if (todo < (len / 2)) {
570 todo = (len / 2) - todo;
571 obufs = (INT16 *)dsb->dsound->buffer;
573 for (i = 0; i < todo; i++) {
574 /* 16-bit WAV is signed */
575 field = *ibufs++;
576 field -= *obufs;
577 if (field > 32767) field = 32767;
578 else if (field < -32768) field = -32768;
579 *obufs++ = field;
585 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
587 DWORD size, flen, len, npos, nlen;
588 INT iAdvance = dsb->pwfx->nBlockAlign;
589 INT oAdvance = dsb->dsound->pwfx->nBlockAlign;
590 /* determine amount of premixed data to cancel */
591 DWORD primary_done =
592 ((dsb->primary_mixpos < writepos) ? dsb->dsound->buflen : 0) +
593 dsb->primary_mixpos - writepos;
595 TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);
597 /* backtrack the mix position */
598 size = primary_done / oAdvance;
599 flen = size * dsb->freqAdjust;
600 len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
601 flen &= (1<<DSOUND_FREQSHIFT)-1;
602 while (dsb->freqAcc < flen) {
603 len += iAdvance;
604 dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
606 len %= dsb->buflen;
607 npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
608 dsb->buf_mixpos - len;
609 if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
610 /* stop backtracking at startpos */
611 npos = dsb->startpos;
612 len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
613 dsb->buf_mixpos - npos;
614 flen = dsb->freqAcc;
615 nlen = len / dsb->pwfx->nBlockAlign;
616 nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
617 nlen *= dsb->dsound->pwfx->nBlockAlign;
618 writepos =
619 ((dsb->primary_mixpos < nlen) ? dsb->dsound->buflen : 0) +
620 dsb->primary_mixpos - nlen;
623 dsb->freqAcc -= flen;
624 dsb->buf_mixpos = npos;
625 dsb->primary_mixpos = writepos;
627 TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
628 dsb->buf_mixpos, dsb->primary_mixpos, len);
630 if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
633 void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
635 #if 0
636 DWORD i, size, flen, len, npos, nlen;
637 INT iAdvance = dsb->pwfx->nBlockAlign;
638 INT oAdvance = dsb->dsound->pwfx->nBlockAlign;
639 /* determine amount of premixed data to cancel */
640 DWORD buf_done =
641 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
642 dsb->buf_mixpos - buf_writepos;
643 #endif
645 WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);
646 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
647 * (which is faster anyway when there's only a single secondary buffer) */
648 dsb->dsound->need_remix = TRUE;
651 void DSOUND_ForceRemix(IDirectSoundBufferImpl *dsb)
653 TRACE("(%p)\n",dsb);
654 EnterCriticalSection(&dsb->lock);
655 if (dsb->state == STATE_PLAYING) {
656 #if 0 /* this may not be quite reliable yet */
657 dsb->need_remix = TRUE;
658 #else
659 dsb->dsound->need_remix = TRUE;
660 #endif
662 LeaveCriticalSection(&dsb->lock);
665 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
667 DWORD len, slen;
668 /* determine this buffer's write position */
669 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & dsb->dsound->state, writepos,
670 writepos, dsb->primary_mixpos, dsb->buf_mixpos);
671 /* determine how much already-mixed data exists */
672 DWORD buf_done =
673 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
674 dsb->buf_mixpos - buf_writepos;
675 DWORD primary_done =
676 ((dsb->primary_mixpos < writepos) ? dsb->dsound->buflen : 0) +
677 dsb->primary_mixpos - writepos;
678 DWORD adv_done =
679 ((dsb->dsound->mixpos < writepos) ? dsb->dsound->buflen : 0) +
680 dsb->dsound->mixpos - writepos;
681 DWORD played =
682 ((buf_writepos < dsb->playpos) ? dsb->buflen : 0) +
683 buf_writepos - dsb->playpos;
684 DWORD buf_left = dsb->buflen - buf_writepos;
685 int still_behind;
687 TRACE("(%p,%ld,%ld,%ld)\n",dsb,playpos,writepos,mixlen);
688 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
689 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
690 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
691 mixlen);
692 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
694 /* check for notification positions */
695 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&
696 dsb->state != STATE_STARTING) {
697 DSOUND_CheckEvent(dsb, played);
700 /* save write position for non-GETCURRENTPOSITION2... */
701 dsb->playpos = buf_writepos;
703 /* check whether CalcPlayPosition detected a mixing underrun */
704 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
705 /* it did, but did we have more to play? */
706 if ((dsb->playflags & DSBPLAY_LOOPING) ||
707 (dsb->buf_mixpos < dsb->buflen)) {
708 /* yes, have to recover */
709 ERR("underrun on sound buffer %p\n", dsb);
710 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
712 dsb->primary_mixpos = writepos;
713 primary_done = 0;
715 /* determine how far ahead we should mix */
716 if (((dsb->playflags & DSBPLAY_LOOPING) ||
717 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
718 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
719 /* if this is a streaming buffer, it typically means that
720 * we should defer mixing past probably_valid_to as long
721 * as we can, to avoid unnecessary remixing */
722 /* the heavy-looking calculations shouldn't be that bad,
723 * as any game isn't likely to be have more than 1 or 2
724 * streaming buffers in use at any time anyway... */
725 DWORD probably_valid_left =
726 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
727 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
728 dsb->probably_valid_to - buf_writepos;
729 /* check for leadin condition */
730 if ((probably_valid_left == 0) &&
731 (dsb->probably_valid_to == dsb->startpos) &&
732 dsb->leadin)
733 probably_valid_left = dsb->buflen;
734 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
735 dsb->probably_valid_to, probably_valid_left);
736 /* check whether the app's time is already up */
737 if (probably_valid_left < dsb->writelead) {
738 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
739 /* once we pass the point of no return,
740 * no reason to hold back anymore */
741 dsb->probably_valid_to = (DWORD)-1;
742 /* we just have to go ahead and mix what we have,
743 * there's no telling what the app is thinking anyway */
744 } else {
745 /* adjust for our frequency and our sample size */
746 probably_valid_left = MulDiv(probably_valid_left,
747 1 << DSOUND_FREQSHIFT,
748 dsb->pwfx->nBlockAlign * dsb->freqAdjust) *
749 dsb->dsound->pwfx->nBlockAlign;
750 /* check whether to clip mix_len */
751 if (probably_valid_left < mixlen) {
752 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
753 mixlen = probably_valid_left;
757 /* cut mixlen with what's already been mixed */
758 if (mixlen < primary_done) {
759 /* huh? and still CalcPlayPosition didn't
760 * detect an underrun? */
761 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
762 return 0;
764 len = mixlen - primary_done;
765 TRACE("remaining mixlen=%ld\n", len);
767 if (len < dsb->dsound->fraglen) {
768 /* smaller than a fragment, wait until it gets larger
769 * before we take the mixing overhead */
770 TRACE("mixlen not worth it, deferring mixing\n");
771 still_behind = 1;
772 goto post_mix;
775 /* ok, we know how much to mix, let's go */
776 still_behind = (adv_done > primary_done);
777 while (len) {
778 slen = dsb->dsound->buflen - dsb->primary_mixpos;
779 if (slen > len) slen = len;
780 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
782 if ((dsb->primary_mixpos < dsb->dsound->mixpos) &&
783 (dsb->primary_mixpos + slen >= dsb->dsound->mixpos))
784 still_behind = FALSE;
786 dsb->primary_mixpos += slen; len -= slen;
787 dsb->primary_mixpos %= dsb->dsound->buflen;
789 if ((dsb->state == STATE_STOPPED) || !slen) break;
791 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, dsb->dsound->mixpos);
792 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
794 post_mix:
795 /* check if buffer should be considered complete */
796 if (buf_left < dsb->writelead &&
797 !(dsb->playflags & DSBPLAY_LOOPING)) {
798 dsb->state = STATE_STOPPED;
799 dsb->playpos = 0;
800 dsb->last_playpos = 0;
801 dsb->buf_mixpos = 0;
802 dsb->leadin = FALSE;
803 DSOUND_CheckEvent(dsb, buf_left);
806 /* return how far we think the primary buffer can
807 * advance its underrun detector...*/
808 if (still_behind) return 0;
809 if ((mixlen - len) < primary_done) return 0;
810 slen = ((dsb->primary_mixpos < dsb->dsound->mixpos) ?
811 dsb->dsound->buflen : 0) + dsb->primary_mixpos -
812 dsb->dsound->mixpos;
813 if (slen > mixlen) {
814 /* the primary_done and still_behind checks above should have worked */
815 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
816 slen = 0;
818 return slen;
821 static DWORD DSOUND_MixToPrimary(IDirectSoundImpl *dsound, DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
823 INT i, len, maxlen = 0;
824 IDirectSoundBufferImpl *dsb;
826 TRACE("(%ld,%ld,%ld,%d)\n", playpos, writepos, mixlen, recover);
827 for (i = 0; i < dsound->nrofbuffers; i++) {
828 dsb = dsound->buffers[i];
830 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
831 TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
832 EnterCriticalSection(&(dsb->lock));
833 if (dsb->state == STATE_STOPPING) {
834 DSOUND_MixCancel(dsb, writepos, TRUE);
835 dsb->state = STATE_STOPPED;
836 DSOUND_CheckEvent(dsb, 0);
837 } else {
838 if ((dsb->state == STATE_STARTING) || recover) {
839 dsb->primary_mixpos = writepos;
840 dsb->cvolpan = dsb->volpan;
841 dsb->need_remix = FALSE;
843 else if (dsb->need_remix) {
844 DSOUND_MixCancel(dsb, writepos, TRUE);
845 dsb->cvolpan = dsb->volpan;
846 dsb->need_remix = FALSE;
848 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
849 if (dsb->state == STATE_STARTING)
850 dsb->state = STATE_PLAYING;
851 maxlen = (len > maxlen) ? len : maxlen;
853 LeaveCriticalSection(&(dsb->lock));
857 return maxlen;
860 static void DSOUND_MixReset(IDirectSoundImpl *dsound, DWORD writepos)
862 INT i;
863 IDirectSoundBufferImpl *dsb;
864 int nfiller;
866 TRACE("(%ld)\n", writepos);
868 /* the sound of silence */
869 nfiller = dsound->pwfx->wBitsPerSample == 8 ? 128 : 0;
871 /* reset all buffer mix positions */
872 for (i = 0; i < dsound->nrofbuffers; i++) {
873 dsb = dsound->buffers[i];
875 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
876 TRACE("Resetting %p\n", dsb);
877 EnterCriticalSection(&(dsb->lock));
878 if (dsb->state == STATE_STOPPING) {
879 dsb->state = STATE_STOPPED;
881 else if (dsb->state == STATE_STARTING) {
882 /* nothing */
883 } else {
884 DSOUND_MixCancel(dsb, writepos, FALSE);
885 dsb->cvolpan = dsb->volpan;
886 dsb->need_remix = FALSE;
888 LeaveCriticalSection(&(dsb->lock));
892 /* wipe out premixed data */
893 if (dsound->mixpos < writepos) {
894 FillMemory(dsound->buffer + writepos, dsound->buflen - writepos, nfiller);
895 FillMemory(dsound->buffer, dsound->mixpos, nfiller);
896 } else {
897 FillMemory(dsound->buffer + writepos, dsound->mixpos - writepos, nfiller);
900 /* reset primary mix position */
901 dsound->mixpos = writepos;
904 static void DSOUND_CheckReset(IDirectSoundImpl *dsound, DWORD writepos)
906 TRACE("(%p,%ld)\n",dsound,writepos);
907 if (dsound->need_remix) {
908 DSOUND_MixReset(dsound, writepos);
909 dsound->need_remix = FALSE;
910 /* maximize Half-Life performance */
911 dsound->prebuf = ds_snd_queue_min;
912 dsound->precount = 0;
913 } else {
914 dsound->precount++;
915 if (dsound->precount >= 4) {
916 if (dsound->prebuf < ds_snd_queue_max)
917 dsound->prebuf++;
918 dsound->precount = 0;
921 TRACE("premix adjust: %d\n", dsound->prebuf);
924 void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq)
926 TRACE("(%p,%ld)\n",dsound,mixq);
927 if (mixq + dsound->pwqueue > ds_hel_queue) mixq = ds_hel_queue - dsound->pwqueue;
928 TRACE("queueing %ld buffers, starting at %d\n", mixq, dsound->pwwrite);
929 for (; mixq; mixq--) {
930 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
931 dsound->pwwrite++;
932 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
933 dsound->pwqueue++;
937 /* #define SYNC_CALLBACK */
939 void DSOUND_PerformMix(IDirectSoundImpl *dsound)
941 int nfiller;
942 BOOL forced;
943 HRESULT hres;
945 TRACE("(%p)\n", dsound);
947 /* the sound of silence */
948 nfiller = dsound->pwfx->wBitsPerSample == 8 ? 128 : 0;
950 /* whether the primary is forced to play even without secondary buffers */
951 forced = ((dsound->state == STATE_PLAYING) || (dsound->state == STATE_STARTING));
953 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
954 BOOL paused = ((dsound->state == STATE_STOPPED) || (dsound->state == STATE_STARTING));
955 /* FIXME: document variables */
956 DWORD playpos, writepos, inq, maxq, frag;
957 if (dsound->hwbuf) {
958 hres = IDsDriverBuffer_GetPosition(dsound->hwbuf, &playpos, &writepos);
959 if (hres) {
960 WARN("IDsDriverBuffer_GetPosition failed\n");
961 return;
963 /* Well, we *could* do Just-In-Time mixing using the writepos,
964 * but that's a little bit ambitious and unnecessary... */
965 /* rather add our safety margin to the writepos, if we're playing */
966 if (!paused) {
967 writepos += dsound->writelead;
968 writepos %= dsound->buflen;
969 } else writepos = playpos;
970 } else {
971 playpos = dsound->pwplay * dsound->fraglen;
972 writepos = playpos;
973 if (!paused) {
974 writepos += ds_hel_margin * dsound->fraglen;
975 writepos %= dsound->buflen;
978 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld, buflen=%ld\n",
979 playpos,writepos,dsound->playpos,dsound->mixpos,dsound->buflen);
980 assert(dsound->playpos < dsound->buflen);
981 /* wipe out just-played sound data */
982 if (playpos < dsound->playpos) {
983 FillMemory(dsound->buffer + dsound->playpos, dsound->buflen - dsound->playpos, nfiller);
984 FillMemory(dsound->buffer, playpos, nfiller);
985 } else {
986 FillMemory(dsound->buffer + dsound->playpos, playpos - dsound->playpos, nfiller);
988 dsound->playpos = playpos;
990 EnterCriticalSection(&(dsound->mixlock));
992 /* reset mixing if necessary */
993 DSOUND_CheckReset(dsound, writepos);
995 /* check how much prebuffering is left */
996 inq = dsound->mixpos;
997 if (inq < writepos)
998 inq += dsound->buflen;
999 inq -= writepos;
1001 /* find the maximum we can prebuffer */
1002 if (!paused) {
1003 maxq = playpos;
1004 if (maxq < writepos)
1005 maxq += dsound->buflen;
1006 maxq -= writepos;
1007 } else maxq = dsound->buflen;
1009 /* clip maxq to dsound->prebuf */
1010 frag = dsound->prebuf * dsound->fraglen;
1011 if (maxq > frag) maxq = frag;
1013 /* check for consistency */
1014 if (inq > maxq) {
1015 /* the playback position must have passed our last
1016 * mixed position, i.e. it's an underrun, or we have
1017 * nothing more to play */
1018 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
1019 inq = 0;
1020 /* stop the playback now, to allow buffers to refill */
1021 if (dsound->state == STATE_PLAYING) {
1022 dsound->state = STATE_STARTING;
1024 else if (dsound->state == STATE_STOPPING) {
1025 dsound->state = STATE_STOPPED;
1027 else {
1028 /* how can we have an underrun if we aren't playing? */
1029 WARN("unexpected primary state (%ld)\n", dsound->state);
1031 #ifdef SYNC_CALLBACK
1032 /* DSOUND_callback may need this lock */
1033 LeaveCriticalSection(&(dsound->mixlock));
1034 #endif
1035 if (DSOUND_PrimaryStop(dsound) != DS_OK)
1036 WARN("DSOUND_PrimaryStop failed\n");
1037 #ifdef SYNC_CALLBACK
1038 EnterCriticalSection(&(dsound->mixlock));
1039 #endif
1040 if (dsound->hwbuf) {
1041 /* the Stop is supposed to reset play position to beginning of buffer */
1042 /* unfortunately, OSS is not able to do so, so get current pointer */
1043 hres = IDsDriverBuffer_GetPosition(dsound->hwbuf, &playpos, NULL);
1044 if (hres) {
1045 LeaveCriticalSection(&(dsound->mixlock));
1046 WARN("IDsDriverBuffer_GetPosition failed\n");
1047 return;
1049 } else {
1050 playpos = dsound->pwplay * dsound->fraglen;
1052 writepos = playpos;
1053 dsound->playpos = playpos;
1054 dsound->mixpos = writepos;
1055 inq = 0;
1056 maxq = dsound->buflen;
1057 if (maxq > frag) maxq = frag;
1058 FillMemory(dsound->buffer, dsound->buflen, nfiller);
1059 paused = TRUE;
1062 /* do the mixing */
1063 frag = DSOUND_MixToPrimary(dsound, playpos, writepos, maxq, paused);
1064 if (forced) frag = maxq - inq;
1065 dsound->mixpos += frag;
1066 dsound->mixpos %= dsound->buflen;
1068 if (frag) {
1069 /* buffers have been filled, restart playback */
1070 if (dsound->state == STATE_STARTING) {
1071 dsound->state = STATE_PLAYING;
1073 else if (dsound->state == STATE_STOPPED) {
1074 /* the dsound is supposed to play if there's something to play
1075 * even if it is reported as stopped, so don't let this confuse you */
1076 dsound->state = STATE_STOPPING;
1078 LeaveCriticalSection(&(dsound->mixlock));
1079 if (paused) {
1080 if (DSOUND_PrimaryPlay(dsound) != DS_OK)
1081 WARN("DSOUND_PrimaryPlay failed\n");
1082 else
1083 TRACE("starting playback\n");
1086 else
1087 LeaveCriticalSection(&(dsound->mixlock));
1088 } else {
1089 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
1090 if (dsound->state == STATE_STARTING) {
1091 if (DSOUND_PrimaryPlay(dsound) != DS_OK)
1092 WARN("DSOUND_PrimaryPlay failed\n");
1093 else
1094 dsound->state = STATE_PLAYING;
1096 else if (dsound->state == STATE_STOPPING) {
1097 if (DSOUND_PrimaryStop(dsound) != DS_OK)
1098 WARN("DSOUND_PrimaryStop failed\n");
1099 else
1100 dsound->state = STATE_STOPPED;
1105 void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
1107 IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
1108 DWORD start_time = GetTickCount();
1109 DWORD end_time;
1110 TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
1111 TRACE("entering at %ld\n", start_time);
1113 if (dsound != This) {
1114 ERR("dsound died without killing us?\n");
1115 timeKillEvent(timerID);
1116 timeEndPeriod(DS_TIME_RES);
1117 return;
1120 RtlAcquireResourceShared(&(This->buffer_list_lock), TRUE);
1122 if (This->ref)
1123 DSOUND_PerformMix(This);
1125 RtlReleaseResource(&(This->buffer_list_lock));
1127 end_time = GetTickCount();
1128 TRACE("completed processing at %ld, duration = %ld\n", end_time, end_time - start_time);
1131 void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
1133 IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
1134 TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo,msg,dwUser,dw1,dw2);
1135 TRACE("entering at %ld, msg=%08x(%s)\n", GetTickCount(), msg,
1136 msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" :
1137 msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN");
1138 if (msg == MM_WOM_DONE) {
1139 DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
1140 if (This->pwqueue == (DWORD)-1) {
1141 TRACE("completed due to reset\n");
1142 return;
1144 /* it could be a bad idea to enter critical section here... if there's lock contention,
1145 * the resulting scheduling delays might obstruct the winmm player thread */
1146 #ifdef SYNC_CALLBACK
1147 EnterCriticalSection(&(This->mixlock));
1148 #endif
1149 /* retrieve current values */
1150 fraglen = This->fraglen;
1151 buflen = This->buflen;
1152 pwplay = This->pwplay;
1153 playpos = pwplay * fraglen;
1154 mixpos = This->mixpos;
1155 /* check remaining mixed data */
1156 inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
1157 mixq = inq / fraglen;
1158 if ((inq - (mixq * fraglen)) > 0) mixq++;
1159 /* complete the playing buffer */
1160 TRACE("done playing primary pos=%ld\n", playpos);
1161 pwplay++;
1162 if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
1163 /* write new values */
1164 This->pwplay = pwplay;
1165 This->pwqueue--;
1166 /* queue new buffer if we have data for it */
1167 if (inq>1) DSOUND_WaveQueue(This, inq-1);
1168 #ifdef SYNC_CALLBACK
1169 LeaveCriticalSection(&(This->mixlock));
1170 #endif
1172 TRACE("completed\n");