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
24 #include <math.h> /* Insomnia - pow() function */
26 #define NONAMELESSSTRUCT
27 #define NONAMELESSUNION
33 #include "wine/debug.h"
36 #include "dsound_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(dsound
);
40 void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan
)
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
)
62 TRACE("(%p)\n",volpan
);
64 TRACE("left=%lx, right=%lx\n",volpan
->dwTotalLeftAmpFactor
,volpan
->dwTotalRightAmpFactor
);
65 if (volpan
->dwTotalLeftAmpFactor
==0)
68 left
=600 * log(((double)volpan
->dwTotalLeftAmpFactor
) / 0xffff) / log(2);
69 if (volpan
->dwTotalRightAmpFactor
==0)
72 right
=600 * log(((double)volpan
->dwTotalRightAmpFactor
) / 0xffff) / log(2);
75 volpan
->lVolume
=right
;
76 volpan
->dwVolAmpFactor
=volpan
->dwTotalRightAmpFactor
;
81 volpan
->dwVolAmpFactor
=volpan
->dwTotalLeftAmpFactor
;
83 if (volpan
->lVolume
< -10000)
84 volpan
->lVolume
=-10000;
85 volpan
->lPan
=right
-left
;
86 if (volpan
->lPan
< -10000)
89 TRACE("Vol=%ld Pan=%ld\n", volpan
->lVolume
, volpan
->lPan
);
92 void DSOUND_RecalcFormat(IDirectSoundBufferImpl
*dsb
)
96 /* calculate the 10ms write lead */
97 dsb
->writelead
= (dsb
->freq
/ 100) * dsb
->pwfx
->nBlockAlign
;
100 void DSOUND_CheckEvent(IDirectSoundBufferImpl
*dsb
, int len
)
104 LPDSBPOSITIONNOTIFY event
;
105 TRACE("(%p,%d)\n",dsb
,len
);
107 if (dsb
->nrofnotifies
== 0)
110 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
111 dsb
, dsb
->buflen
, dsb
->playpos
, len
);
112 for (i
= 0; i
< dsb
->nrofnotifies
; i
++) {
113 event
= dsb
->notifies
+ i
;
114 offset
= event
->dwOffset
;
115 TRACE("checking %d, position %ld, event = %p\n",
116 i
, offset
, event
->hEventNotify
);
117 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
118 /* OK. [Inside DirectX, p274] */
120 /* This also means we can't sort the entries by offset, */
121 /* because DSBPN_OFFSETSTOP == -1 */
122 if (offset
== DSBPN_OFFSETSTOP
) {
123 if (dsb
->state
== STATE_STOPPED
) {
124 SetEvent(event
->hEventNotify
);
125 TRACE("signalled event %p (%d)\n", event
->hEventNotify
, i
);
130 if ((dsb
->playpos
+ len
) >= dsb
->buflen
) {
131 if ((offset
< ((dsb
->playpos
+ len
) % dsb
->buflen
)) ||
132 (offset
>= dsb
->playpos
)) {
133 TRACE("signalled event %p (%d)\n", event
->hEventNotify
, i
);
134 SetEvent(event
->hEventNotify
);
137 if ((offset
>= dsb
->playpos
) && (offset
< (dsb
->playpos
+ len
))) {
138 TRACE("signalled event %p (%d)\n", event
->hEventNotify
, i
);
139 SetEvent(event
->hEventNotify
);
145 /* WAV format info can be found at:
147 * http://www.cwi.nl/ftp/audio/AudioFormats.part2
148 * ftp://ftp.cwi.nl/pub/audio/RIFF-format
150 * Import points to remember:
151 * 8-bit WAV is unsigned
152 * 16-bit WAV is signed
154 /* Use the same formulas as pcmconverter.c */
155 static inline INT16
cvtU8toS16(BYTE b
)
157 return (short)((b
+(b
<< 8))-32768);
160 static inline BYTE
cvtS16toU8(INT16 s
)
162 return (s
>> 8) ^ (unsigned char)0x80;
165 static inline void cp_fields(const IDirectSoundBufferImpl
*dsb
, BYTE
*ibuf
, BYTE
*obuf
)
167 DirectSoundDevice
* device
= dsb
->dsound
->device
;
170 if (dsb
->pwfx
->wBitsPerSample
== 8) {
171 if (device
->pwfx
->wBitsPerSample
== 8 &&
172 device
->pwfx
->nChannels
== dsb
->pwfx
->nChannels
) {
173 /* avoid needless 8->16->8 conversion */
175 if (dsb
->pwfx
->nChannels
==2)
179 fl
= cvtU8toS16(*ibuf
);
180 fr
= (dsb
->pwfx
->nChannels
==2 ? cvtU8toS16(*(ibuf
+ 1)) : fl
);
182 fl
= *((INT16
*)ibuf
);
183 fr
= (dsb
->pwfx
->nChannels
==2 ? *(((INT16
*)ibuf
) + 1) : fl
);
186 if (device
->pwfx
->nChannels
== 2) {
187 if (device
->pwfx
->wBitsPerSample
== 8) {
188 *obuf
= cvtS16toU8(fl
);
189 *(obuf
+ 1) = cvtS16toU8(fr
);
192 if (device
->pwfx
->wBitsPerSample
== 16) {
193 *((INT16
*)obuf
) = fl
;
194 *(((INT16
*)obuf
) + 1) = fr
;
198 if (device
->pwfx
->nChannels
== 1) {
200 if (device
->pwfx
->wBitsPerSample
== 8) {
201 *obuf
= cvtS16toU8(fl
);
204 if (device
->pwfx
->wBitsPerSample
== 16) {
205 *((INT16
*)obuf
) = fl
;
211 /* Now with PerfectPitch (tm) technology */
212 static INT
DSOUND_MixerNorm(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
214 INT i
, size
, ipos
, ilen
;
216 INT iAdvance
= dsb
->pwfx
->nBlockAlign
;
217 INT oAdvance
= dsb
->dsound
->device
->pwfx
->nBlockAlign
;
219 ibp
= dsb
->buffer
->memory
+ dsb
->buf_mixpos
;
222 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb
, ibp
, obp
, dsb
->buf_mixpos
);
223 /* Check for the best case */
224 if ((dsb
->freq
== dsb
->dsound
->device
->pwfx
->nSamplesPerSec
) &&
225 (dsb
->pwfx
->wBitsPerSample
== dsb
->dsound
->device
->pwfx
->wBitsPerSample
) &&
226 (dsb
->pwfx
->nChannels
== dsb
->dsound
->device
->pwfx
->nChannels
)) {
227 INT bytesleft
= dsb
->buflen
- dsb
->buf_mixpos
;
228 TRACE("(%p) Best case\n", dsb
);
229 if (len
<= bytesleft
)
230 CopyMemory(obp
, ibp
, len
);
232 CopyMemory(obp
, ibp
, bytesleft
);
233 CopyMemory(obp
+ bytesleft
, dsb
->buffer
->memory
, len
- bytesleft
);
238 /* Check for same sample rate */
239 if (dsb
->freq
== dsb
->dsound
->device
->pwfx
->nSamplesPerSec
) {
240 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb
,
241 dsb
->freq
, dsb
->dsound
->device
->pwfx
->nSamplesPerSec
);
243 for (i
= 0; i
< len
; i
+= oAdvance
) {
244 cp_fields(dsb
, ibp
, obp
);
248 if (ibp
>= (BYTE
*)(dsb
->buffer
->memory
+ dsb
->buflen
))
249 ibp
= dsb
->buffer
->memory
; /* wrap */
254 /* Mix in different sample rates */
256 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
257 /* Patent Pending :-] */
259 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
260 * TransGaming Technologies Inc. */
262 /* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
263 dsb, dsb->freq, dsb->dsound->device->pwfx->nSamplesPerSec); */
265 size
= len
/ oAdvance
;
267 ipos
= dsb
->buf_mixpos
;
268 for (i
= 0; i
< size
; i
++) {
269 cp_fields(dsb
, (dsb
->buffer
->memory
+ ipos
), obp
);
271 dsb
->freqAcc
+= dsb
->freqAdjust
;
272 if (dsb
->freqAcc
>= (1<<DSOUND_FREQSHIFT
)) {
273 ULONG adv
= (dsb
->freqAcc
>>DSOUND_FREQSHIFT
) * iAdvance
;
274 dsb
->freqAcc
&= (1<<DSOUND_FREQSHIFT
)-1;
275 ipos
+= adv
; ilen
+= adv
;
282 static void DSOUND_MixerVol(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
286 INT16
*bps
= (INT16
*) buf
;
288 TRACE("(%p,%p,%d)\n",dsb
,buf
,len
);
289 TRACE("left = %lx, right = %lx\n", dsb
->cvolpan
.dwTotalLeftAmpFactor
,
290 dsb
->cvolpan
.dwTotalRightAmpFactor
);
292 if ((!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) || (dsb
->cvolpan
.lPan
== 0)) &&
293 (!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
) || (dsb
->cvolpan
.lVolume
== 0)) &&
294 !(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
))
295 return; /* Nothing to do */
297 /* If we end up with some bozo coder using panning or 3D sound */
298 /* with a mono primary buffer, it could sound very weird using */
299 /* this method. Oh well, tough patooties. */
301 switch (dsb
->dsound
->device
->pwfx
->wBitsPerSample
) {
303 /* 8-bit WAV is unsigned, but we need to operate */
304 /* on signed data for this to work properly */
305 switch (dsb
->dsound
->device
->pwfx
->nChannels
) {
307 for (i
= 0; i
< len
; i
++) {
308 INT val
= *bpc
- 128;
309 val
= (val
* dsb
->cvolpan
.dwTotalLeftAmpFactor
) >> 16;
315 for (i
= 0; i
< len
; i
+=2) {
316 INT val
= *bpc
- 128;
317 val
= (val
* dsb
->cvolpan
.dwTotalLeftAmpFactor
) >> 16;
320 val
= (val
* dsb
->cvolpan
.dwTotalRightAmpFactor
) >> 16;
326 FIXME("doesn't support %d channels\n", dsb
->dsound
->device
->pwfx
->nChannels
);
331 /* 16-bit WAV is signed -- much better */
332 switch (dsb
->dsound
->device
->pwfx
->nChannels
) {
334 for (i
= 0; i
< len
; i
+= 2) {
335 *bps
= (*bps
* dsb
->cvolpan
.dwTotalLeftAmpFactor
) >> 16;
340 for (i
= 0; i
< len
; i
+= 4) {
341 *bps
= (*bps
* dsb
->cvolpan
.dwTotalLeftAmpFactor
) >> 16;
343 *bps
= (*bps
* dsb
->cvolpan
.dwTotalRightAmpFactor
) >> 16;
348 FIXME("doesn't support %d channels\n", dsb
->dsound
->device
->pwfx
->nChannels
);
353 FIXME("doesn't support %d bit samples\n", dsb
->dsound
->device
->pwfx
->wBitsPerSample
);
358 static LPBYTE
DSOUND_tmpbuffer(DirectSoundDevice
*device
, DWORD len
)
360 TRACE("(%p,%ld)\n", device
, len
);
362 if (len
> device
->tmp_buffer_len
) {
363 if (device
->tmp_buffer
)
364 device
->tmp_buffer
= HeapReAlloc(GetProcessHeap(), 0, device
->tmp_buffer
, len
);
366 device
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0, len
);
368 device
->tmp_buffer_len
= len
;
371 return device
->tmp_buffer
;
374 static DWORD
DSOUND_MixInBuffer(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, DWORD fraglen
)
376 INT i
, len
, ilen
, field
, todo
;
379 TRACE("(%p,%ld,%ld)\n",dsb
,writepos
,fraglen
);
382 if (!(dsb
->playflags
& DSBPLAY_LOOPING
)) {
383 int secondary_remainder
= dsb
->buflen
- dsb
->buf_mixpos
;
384 int adjusted_remainder
= MulDiv(dsb
->dsound
->device
->pwfx
->nAvgBytesPerSec
, secondary_remainder
, dsb
->nAvgBytesPerSec
);
385 assert(adjusted_remainder
>= 0);
386 TRACE("secondary_remainder = %d, adjusted_remainder = %d, len = %d\n", secondary_remainder
, adjusted_remainder
, len
);
387 if (adjusted_remainder
< len
) {
388 TRACE("clipping len to remainder of secondary buffer\n");
389 len
= adjusted_remainder
;
395 if (len
% dsb
->dsound
->device
->pwfx
->nBlockAlign
) {
396 INT nBlockAlign
= dsb
->dsound
->device
->pwfx
->nBlockAlign
;
397 ERR("length not a multiple of block size, len = %d, block size = %d\n", len
, nBlockAlign
);
398 len
= (len
/ nBlockAlign
) * nBlockAlign
; /* data alignment */
401 if ((buf
= ibuf
= DSOUND_tmpbuffer(dsb
->dsound
->device
, len
)) == NULL
)
404 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb
, len
, writepos
);
406 ilen
= DSOUND_MixerNorm(dsb
, ibuf
, len
);
407 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
408 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
) ||
409 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
))
410 DSOUND_MixerVol(dsb
, ibuf
, len
);
412 if (dsb
->dsound
->device
->pwfx
->wBitsPerSample
== 8) {
413 BYTE
*obuf
= dsb
->dsound
->device
->buffer
+ writepos
;
415 if ((writepos
+ len
) <= dsb
->dsound
->device
->buflen
)
418 todo
= dsb
->dsound
->device
->buflen
- writepos
;
420 for (i
= 0; i
< todo
; i
++) {
421 /* 8-bit WAV is unsigned */
422 field
= (*ibuf
++ - 128);
423 field
+= (*obuf
- 128);
424 if (field
> 127) field
= 127;
425 else if (field
< -128) field
= -128;
426 *obuf
++ = field
+ 128;
431 obuf
= dsb
->dsound
->device
->buffer
;
433 for (i
= 0; i
< todo
; i
++) {
434 /* 8-bit WAV is unsigned */
435 field
= (*ibuf
++ - 128);
436 field
+= (*obuf
- 128);
437 if (field
> 127) field
= 127;
438 else if (field
< -128) field
= -128;
439 *obuf
++ = field
+ 128;
443 INT16
*ibufs
, *obufs
;
445 ibufs
= (INT16
*) ibuf
;
446 obufs
= (INT16
*)(dsb
->dsound
->device
->buffer
+ writepos
);
448 if ((writepos
+ len
) <= dsb
->dsound
->device
->buflen
)
451 todo
= (dsb
->dsound
->device
->buflen
- writepos
) / 2;
453 for (i
= 0; i
< todo
; i
++) {
454 /* 16-bit WAV is signed */
457 if (field
> 32767) field
= 32767;
458 else if (field
< -32768) field
= -32768;
462 if (todo
< (len
/ 2)) {
463 todo
= (len
/ 2) - todo
;
464 obufs
= (INT16
*)dsb
->dsound
->device
->buffer
;
466 for (i
= 0; i
< todo
; i
++) {
467 /* 16-bit WAV is signed */
470 if (field
> 32767) field
= 32767;
471 else if (field
< -32768) field
= -32768;
477 if (dsb
->leadin
&& (dsb
->startpos
> dsb
->buf_mixpos
) && (dsb
->startpos
<= dsb
->buf_mixpos
+ ilen
)) {
478 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
479 * not the MIX position... but if the sound buffer is bigger than our prebuffering
480 * (which must be the case for the streaming buffers that need this hack anyway)
481 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
485 dsb
->buf_mixpos
+= ilen
;
487 if (dsb
->buf_mixpos
>= dsb
->buflen
) {
488 if (dsb
->playflags
& DSBPLAY_LOOPING
) {
490 dsb
->buf_mixpos
%= dsb
->buflen
;
491 if (dsb
->leadin
&& (dsb
->startpos
<= dsb
->buf_mixpos
))
492 dsb
->leadin
= FALSE
; /* HACK: see above */
493 } else if (dsb
->buf_mixpos
> dsb
->buflen
) {
494 ERR("Mixpos (%lu) past buflen (%lu), capping...\n", dsb
->buf_mixpos
, dsb
->buflen
);
495 dsb
->buf_mixpos
= dsb
->buflen
;
502 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, DWORD len
)
508 TRACE("(%p,%ld,%ld)\n",dsb
,writepos
,len
);
510 if (len
% dsb
->dsound
->device
->pwfx
->nBlockAlign
) {
511 INT nBlockAlign
= dsb
->dsound
->device
->pwfx
->nBlockAlign
;
512 ERR("length not a multiple of block size, len = %ld, block size = %d\n", len
, nBlockAlign
);
513 len
= (len
/ nBlockAlign
) * nBlockAlign
; /* data alignment */
516 if ((buf
= ibuf
= DSOUND_tmpbuffer(dsb
->dsound
->device
, len
)) == NULL
)
519 TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb
, len
, writepos
);
521 ilen
= DSOUND_MixerNorm(dsb
, ibuf
, len
);
522 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
523 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
) ||
524 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
))
525 DSOUND_MixerVol(dsb
, ibuf
, len
);
527 /* subtract instead of add, to phase out premixed data */
528 if (dsb
->dsound
->device
->pwfx
->wBitsPerSample
== 8) {
529 BYTE
*obuf
= dsb
->dsound
->device
->buffer
+ writepos
;
531 if ((writepos
+ len
) <= dsb
->dsound
->device
->buflen
)
534 todo
= dsb
->dsound
->device
->buflen
- writepos
;
536 for (i
= 0; i
< todo
; i
++) {
537 /* 8-bit WAV is unsigned */
538 field
= (*ibuf
++ - 128);
539 field
-= (*obuf
- 128);
540 if (field
> 127) field
= 127;
541 else if (field
< -128) field
= -128;
542 *obuf
++ = field
+ 128;
547 obuf
= dsb
->dsound
->device
->buffer
;
549 for (i
= 0; i
< todo
; i
++) {
550 /* 8-bit WAV is unsigned */
551 field
= (*ibuf
++ - 128);
552 field
-= (*obuf
- 128);
553 if (field
> 127) field
= 127;
554 else if (field
< -128) field
= -128;
555 *obuf
++ = field
+ 128;
559 INT16
*ibufs
, *obufs
;
561 ibufs
= (INT16
*) ibuf
;
562 obufs
= (INT16
*)(dsb
->dsound
->device
->buffer
+ writepos
);
564 if ((writepos
+ len
) <= dsb
->dsound
->device
->buflen
)
567 todo
= (dsb
->dsound
->device
->buflen
- writepos
) / 2;
569 for (i
= 0; i
< todo
; i
++) {
570 /* 16-bit WAV is signed */
573 if (field
> 32767) field
= 32767;
574 else if (field
< -32768) field
= -32768;
578 if (todo
< (len
/ 2)) {
579 todo
= (len
/ 2) - todo
;
580 obufs
= (INT16
*)dsb
->dsound
->device
->buffer
;
582 for (i
= 0; i
< todo
; i
++) {
583 /* 16-bit WAV is signed */
586 if (field
> 32767) field
= 32767;
587 else if (field
< -32768) field
= -32768;
594 static void DSOUND_MixCancel(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, BOOL cancel
)
596 DWORD size
, flen
, len
, npos
, nlen
;
597 INT iAdvance
= dsb
->pwfx
->nBlockAlign
;
598 INT oAdvance
= dsb
->dsound
->device
->pwfx
->nBlockAlign
;
599 /* determine amount of premixed data to cancel */
601 ((dsb
->primary_mixpos
< writepos
) ? dsb
->dsound
->device
->buflen
: 0) +
602 dsb
->primary_mixpos
- writepos
;
604 TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb
, writepos
, dsb
->buf_mixpos
);
606 /* backtrack the mix position */
607 size
= primary_done
/ oAdvance
;
608 flen
= size
* dsb
->freqAdjust
;
609 len
= (flen
>> DSOUND_FREQSHIFT
) * iAdvance
;
610 flen
&= (1<<DSOUND_FREQSHIFT
)-1;
611 while (dsb
->freqAcc
< flen
) {
613 dsb
->freqAcc
+= 1<<DSOUND_FREQSHIFT
;
616 npos
= ((dsb
->buf_mixpos
< len
) ? dsb
->buflen
: 0) +
617 dsb
->buf_mixpos
- len
;
618 if (dsb
->leadin
&& (dsb
->startpos
> npos
) && (dsb
->startpos
<= npos
+ len
)) {
619 /* stop backtracking at startpos */
620 npos
= dsb
->startpos
;
621 len
= ((dsb
->buf_mixpos
< npos
) ? dsb
->buflen
: 0) +
622 dsb
->buf_mixpos
- npos
;
624 nlen
= len
/ dsb
->pwfx
->nBlockAlign
;
625 nlen
= ((nlen
<< DSOUND_FREQSHIFT
) + flen
) / dsb
->freqAdjust
;
626 nlen
*= dsb
->dsound
->device
->pwfx
->nBlockAlign
;
628 ((dsb
->primary_mixpos
< nlen
) ? dsb
->dsound
->device
->buflen
: 0) +
629 dsb
->primary_mixpos
- nlen
;
632 dsb
->freqAcc
-= flen
;
633 dsb
->buf_mixpos
= npos
;
634 dsb
->primary_mixpos
= writepos
;
636 TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
637 dsb
->buf_mixpos
, dsb
->primary_mixpos
, len
);
639 if (cancel
) DSOUND_PhaseCancel(dsb
, writepos
, len
);
642 void DSOUND_MixCancelAt(IDirectSoundBufferImpl
*dsb
, DWORD buf_writepos
)
645 DWORD i
, size
, flen
, len
, npos
, nlen
;
646 INT iAdvance
= dsb
->pwfx
->nBlockAlign
;
647 INT oAdvance
= dsb
->dsound
->device
->pwfx
->nBlockAlign
;
648 /* determine amount of premixed data to cancel */
650 ((dsb
->buf_mixpos
< buf_writepos
) ? dsb
->buflen
: 0) +
651 dsb
->buf_mixpos
- buf_writepos
;
654 WARN("(%p, %ld), buf_mixpos=%ld\n", dsb
, buf_writepos
, dsb
->buf_mixpos
);
655 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
656 * (which is faster anyway when there's only a single secondary buffer) */
657 dsb
->dsound
->device
->need_remix
= TRUE
;
660 void DSOUND_ForceRemix(IDirectSoundBufferImpl
*dsb
)
663 EnterCriticalSection(&dsb
->lock
);
664 if (dsb
->state
== STATE_PLAYING
)
665 dsb
->dsound
->device
->need_remix
= TRUE
;
666 LeaveCriticalSection(&dsb
->lock
);
669 static DWORD
DSOUND_MixOne(IDirectSoundBufferImpl
*dsb
, DWORD playpos
, DWORD writepos
, DWORD mixlen
)
672 /* determine this buffer's write position */
673 DWORD buf_writepos
= DSOUND_CalcPlayPosition(dsb
, writepos
, writepos
);
674 /* determine how much already-mixed data exists */
676 ((dsb
->buf_mixpos
< buf_writepos
) ? dsb
->buflen
: 0) +
677 dsb
->buf_mixpos
- buf_writepos
;
679 ((dsb
->primary_mixpos
< writepos
) ? dsb
->dsound
->device
->buflen
: 0) +
680 dsb
->primary_mixpos
- writepos
;
682 ((dsb
->dsound
->device
->mixpos
< writepos
) ? dsb
->dsound
->device
->buflen
: 0) +
683 dsb
->dsound
->device
->mixpos
- writepos
;
685 ((buf_writepos
< dsb
->playpos
) ? dsb
->buflen
: 0) +
686 buf_writepos
- dsb
->playpos
;
687 DWORD buf_left
= dsb
->buflen
- buf_writepos
;
690 TRACE("(%p,%ld,%ld,%ld)\n",dsb
,playpos
,writepos
,mixlen
);
691 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos
, writepos
);
692 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done
, primary_done
);
693 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb
->buf_mixpos
, dsb
->primary_mixpos
,
695 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb
->playflags
, dsb
->startpos
, dsb
->leadin
);
697 /* check for notification positions */
698 if (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPOSITIONNOTIFY
&&
699 dsb
->state
!= STATE_STARTING
) {
700 DSOUND_CheckEvent(dsb
, played
);
703 /* save write position for non-GETCURRENTPOSITION2... */
704 dsb
->playpos
= buf_writepos
;
706 /* check whether CalcPlayPosition detected a mixing underrun */
707 if ((buf_done
== 0) && (dsb
->primary_mixpos
!= writepos
)) {
708 /* it did, but did we have more to play? */
709 if ((dsb
->playflags
& DSBPLAY_LOOPING
) ||
710 (dsb
->buf_mixpos
< dsb
->buflen
)) {
711 /* yes, have to recover */
712 ERR("underrun on sound buffer %p\n", dsb
);
713 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos
);
715 dsb
->primary_mixpos
= writepos
;
718 /* determine how far ahead we should mix */
719 if (((dsb
->playflags
& DSBPLAY_LOOPING
) ||
720 (dsb
->leadin
&& (dsb
->probably_valid_to
!= 0))) &&
721 !(dsb
->dsbd
.dwFlags
& DSBCAPS_STATIC
)) {
722 /* if this is a streaming buffer, it typically means that
723 * we should defer mixing past probably_valid_to as long
724 * as we can, to avoid unnecessary remixing */
725 /* the heavy-looking calculations shouldn't be that bad,
726 * as any game isn't likely to be have more than 1 or 2
727 * streaming buffers in use at any time anyway... */
728 DWORD probably_valid_left
=
729 (dsb
->probably_valid_to
== (DWORD
)-1) ? dsb
->buflen
:
730 ((dsb
->probably_valid_to
< buf_writepos
) ? dsb
->buflen
: 0) +
731 dsb
->probably_valid_to
- buf_writepos
;
732 /* check for leadin condition */
733 if ((probably_valid_left
== 0) &&
734 (dsb
->probably_valid_to
== dsb
->startpos
) &&
736 probably_valid_left
= dsb
->buflen
;
737 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
738 dsb
->probably_valid_to
, probably_valid_left
);
739 /* check whether the app's time is already up */
740 if (probably_valid_left
< dsb
->writelead
) {
741 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
742 /* once we pass the point of no return,
743 * no reason to hold back anymore */
744 dsb
->probably_valid_to
= (DWORD
)-1;
745 /* we just have to go ahead and mix what we have,
746 * there's no telling what the app is thinking anyway */
748 /* adjust for our frequency and our sample size */
749 probably_valid_left
= MulDiv(probably_valid_left
,
750 1 << DSOUND_FREQSHIFT
,
751 dsb
->pwfx
->nBlockAlign
* dsb
->freqAdjust
) *
752 dsb
->dsound
->device
->pwfx
->nBlockAlign
;
753 /* check whether to clip mix_len */
754 if (probably_valid_left
< mixlen
) {
755 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left
);
756 mixlen
= probably_valid_left
;
760 /* cut mixlen with what's already been mixed */
761 if (mixlen
< primary_done
) {
762 /* huh? and still CalcPlayPosition didn't
763 * detect an underrun? */
764 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen
, primary_done
);
767 len
= mixlen
- primary_done
;
768 TRACE("remaining mixlen=%ld\n", len
);
770 if (len
< dsb
->dsound
->device
->fraglen
) {
771 /* smaller than a fragment, wait until it gets larger
772 * before we take the mixing overhead */
773 TRACE("mixlen not worth it, deferring mixing\n");
778 /* ok, we know how much to mix, let's go */
779 still_behind
= (adv_done
> primary_done
);
781 slen
= dsb
->dsound
->device
->buflen
- dsb
->primary_mixpos
;
782 if (slen
> len
) slen
= len
;
783 slen
= DSOUND_MixInBuffer(dsb
, dsb
->primary_mixpos
, slen
);
785 if ((dsb
->primary_mixpos
< dsb
->dsound
->device
->mixpos
) &&
786 (dsb
->primary_mixpos
+ slen
>= dsb
->dsound
->device
->mixpos
))
787 still_behind
= FALSE
;
789 dsb
->primary_mixpos
+= slen
; len
-= slen
;
790 dsb
->primary_mixpos
%= dsb
->dsound
->device
->buflen
;
792 if ((dsb
->state
== STATE_STOPPED
) || !slen
) break;
794 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb
->primary_mixpos
, dsb
->dsound
->device
->mixpos
);
795 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen
-len
, still_behind
);
798 /* check if buffer should be considered complete */
799 if (buf_left
< dsb
->writelead
&&
800 !(dsb
->playflags
& DSBPLAY_LOOPING
)) {
801 dsb
->state
= STATE_STOPPED
;
803 dsb
->last_playpos
= 0;
806 dsb
->need_remix
= FALSE
;
807 DSOUND_CheckEvent(dsb
, buf_left
);
810 /* return how far we think the primary buffer can
811 * advance its underrun detector...*/
812 if (still_behind
) return 0;
813 if ((mixlen
- len
) < primary_done
) return 0;
814 slen
= ((dsb
->primary_mixpos
< dsb
->dsound
->device
->mixpos
) ?
815 dsb
->dsound
->device
->buflen
: 0) + dsb
->primary_mixpos
-
816 dsb
->dsound
->device
->mixpos
;
818 /* the primary_done and still_behind checks above should have worked */
819 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen
, mixlen
);
825 static DWORD
DSOUND_MixToPrimary(DirectSoundDevice
*device
, DWORD playpos
, DWORD writepos
, DWORD mixlen
, BOOL recover
)
827 INT i
, len
, maxlen
= 0;
828 IDirectSoundBufferImpl
*dsb
;
830 TRACE("(%ld,%ld,%ld,%d)\n", playpos
, writepos
, mixlen
, recover
);
831 for (i
= 0; i
< device
->nrofbuffers
; i
++) {
832 dsb
= device
->buffers
[i
];
834 if (dsb
->buflen
&& dsb
->state
&& !dsb
->hwbuf
) {
835 TRACE("Checking %p, mixlen=%ld\n", dsb
, mixlen
);
836 EnterCriticalSection(&(dsb
->lock
));
837 if (dsb
->state
== STATE_STOPPING
) {
838 DSOUND_MixCancel(dsb
, writepos
, TRUE
);
839 dsb
->state
= STATE_STOPPED
;
840 DSOUND_CheckEvent(dsb
, 0);
842 if ((dsb
->state
== STATE_STARTING
) || recover
) {
843 dsb
->primary_mixpos
= writepos
;
844 dsb
->cvolpan
= dsb
->volpan
;
845 dsb
->need_remix
= FALSE
;
847 else if (dsb
->need_remix
) {
848 DSOUND_MixCancel(dsb
, writepos
, TRUE
);
849 dsb
->cvolpan
= dsb
->volpan
;
850 dsb
->need_remix
= FALSE
;
852 len
= DSOUND_MixOne(dsb
, playpos
, writepos
, mixlen
);
853 if (dsb
->state
== STATE_STARTING
)
854 dsb
->state
= STATE_PLAYING
;
855 maxlen
= (len
> maxlen
) ? len
: maxlen
;
857 LeaveCriticalSection(&(dsb
->lock
));
864 static void DSOUND_MixReset(DirectSoundDevice
*device
, DWORD writepos
)
867 IDirectSoundBufferImpl
*dsb
;
870 TRACE("(%p,%ld)\n", device
, writepos
);
872 /* the sound of silence */
873 nfiller
= device
->pwfx
->wBitsPerSample
== 8 ? 128 : 0;
875 /* reset all buffer mix positions */
876 for (i
= 0; i
< device
->nrofbuffers
; i
++) {
877 dsb
= device
->buffers
[i
];
879 if (dsb
->buflen
&& dsb
->state
&& !dsb
->hwbuf
) {
880 TRACE("Resetting %p\n", dsb
);
881 EnterCriticalSection(&(dsb
->lock
));
882 if (dsb
->state
== STATE_STOPPING
) {
883 dsb
->state
= STATE_STOPPED
;
885 else if (dsb
->state
== STATE_STARTING
) {
888 DSOUND_MixCancel(dsb
, writepos
, FALSE
);
889 dsb
->cvolpan
= dsb
->volpan
;
890 dsb
->need_remix
= FALSE
;
892 LeaveCriticalSection(&(dsb
->lock
));
896 /* wipe out premixed data */
897 if (device
->mixpos
< writepos
) {
898 FillMemory(device
->buffer
+ writepos
, device
->buflen
- writepos
, nfiller
);
899 FillMemory(device
->buffer
, device
->mixpos
, nfiller
);
901 FillMemory(device
->buffer
+ writepos
, device
->mixpos
- writepos
, nfiller
);
904 /* reset primary mix position */
905 device
->mixpos
= writepos
;
908 static void DSOUND_CheckReset(DirectSoundDevice
*device
, DWORD writepos
)
910 TRACE("(%p,%ld)\n",device
,writepos
);
911 if (device
->need_remix
) {
912 DSOUND_MixReset(device
, writepos
);
913 device
->need_remix
= FALSE
;
914 /* maximize Half-Life performance */
915 device
->prebuf
= ds_snd_queue_min
;
916 device
->precount
= 0;
919 if (device
->precount
>= 4) {
920 if (device
->prebuf
< ds_snd_queue_max
)
922 device
->precount
= 0;
925 TRACE("premix adjust: %d\n", device
->prebuf
);
928 void DSOUND_WaveQueue(DirectSoundDevice
*device
, DWORD mixq
)
930 TRACE("(%p,%ld)\n", device
, mixq
);
931 if (mixq
+ device
->pwqueue
> ds_hel_queue
) mixq
= ds_hel_queue
- device
->pwqueue
;
932 TRACE("queueing %ld buffers, starting at %d\n", mixq
, device
->pwwrite
);
933 for (; mixq
; mixq
--) {
934 waveOutWrite(device
->hwo
, device
->pwave
[device
->pwwrite
], sizeof(WAVEHDR
));
936 if (device
->pwwrite
>= DS_HEL_FRAGS
) device
->pwwrite
= 0;
941 /* #define SYNC_CALLBACK */
943 void DSOUND_PerformMix(DirectSoundDevice
*device
)
949 TRACE("(%p)\n", device
);
951 /* the sound of silence */
952 nfiller
= device
->pwfx
->wBitsPerSample
== 8 ? 128 : 0;
954 /* whether the primary is forced to play even without secondary buffers */
955 forced
= ((device
->state
== STATE_PLAYING
) || (device
->state
== STATE_STARTING
));
957 if (device
->priolevel
!= DSSCL_WRITEPRIMARY
) {
958 BOOL paused
= ((device
->state
== STATE_STOPPED
) || (device
->state
== STATE_STARTING
));
959 /* FIXME: document variables */
960 DWORD playpos
, writepos
, inq
, maxq
, frag
;
962 hres
= IDsDriverBuffer_GetPosition(device
->hwbuf
, &playpos
, &writepos
);
964 WARN("IDsDriverBuffer_GetPosition failed\n");
967 /* Well, we *could* do Just-In-Time mixing using the writepos,
968 * but that's a little bit ambitious and unnecessary... */
969 /* rather add our safety margin to the writepos, if we're playing */
971 writepos
+= device
->writelead
;
972 writepos
%= device
->buflen
;
973 } else writepos
= playpos
;
975 playpos
= device
->pwplay
* device
->fraglen
;
978 writepos
+= ds_hel_margin
* device
->fraglen
;
979 writepos
%= device
->buflen
;
982 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld, buflen=%ld\n",
983 playpos
,writepos
,device
->playpos
,device
->mixpos
,device
->buflen
);
984 assert(device
->playpos
< device
->buflen
);
985 /* wipe out just-played sound data */
986 if (playpos
< device
->playpos
) {
987 FillMemory(device
->buffer
+ device
->playpos
, device
->buflen
- device
->playpos
, nfiller
);
988 FillMemory(device
->buffer
, playpos
, nfiller
);
990 FillMemory(device
->buffer
+ device
->playpos
, playpos
- device
->playpos
, nfiller
);
992 device
->playpos
= playpos
;
994 EnterCriticalSection(&(device
->mixlock
));
996 /* reset mixing if necessary */
997 DSOUND_CheckReset(device
, writepos
);
999 /* check how much prebuffering is left */
1000 inq
= device
->mixpos
;
1002 inq
+= device
->buflen
;
1005 /* find the maximum we can prebuffer */
1008 if (maxq
< writepos
)
1009 maxq
+= device
->buflen
;
1011 } else maxq
= device
->buflen
;
1013 /* clip maxq to device->prebuf */
1014 frag
= device
->prebuf
* device
->fraglen
;
1015 if (maxq
> frag
) maxq
= frag
;
1017 /* check for consistency */
1019 /* the playback position must have passed our last
1020 * mixed position, i.e. it's an underrun, or we have
1021 * nothing more to play */
1022 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq
, maxq
);
1024 /* stop the playback now, to allow buffers to refill */
1025 if (device
->state
== STATE_PLAYING
) {
1026 device
->state
= STATE_STARTING
;
1028 else if (device
->state
== STATE_STOPPING
) {
1029 device
->state
= STATE_STOPPED
;
1032 /* how can we have an underrun if we aren't playing? */
1033 WARN("unexpected primary state (%ld)\n", device
->state
);
1035 #ifdef SYNC_CALLBACK
1036 /* DSOUND_callback may need this lock */
1037 LeaveCriticalSection(&(device
->mixlock
));
1039 if (DSOUND_PrimaryStop(device
) != DS_OK
)
1040 WARN("DSOUND_PrimaryStop failed\n");
1041 #ifdef SYNC_CALLBACK
1042 EnterCriticalSection(&(device
->mixlock
));
1044 if (device
->hwbuf
) {
1045 /* the Stop is supposed to reset play position to beginning of buffer */
1046 /* unfortunately, OSS is not able to do so, so get current pointer */
1047 hres
= IDsDriverBuffer_GetPosition(device
->hwbuf
, &playpos
, NULL
);
1049 LeaveCriticalSection(&(device
->mixlock
));
1050 WARN("IDsDriverBuffer_GetPosition failed\n");
1054 playpos
= device
->pwplay
* device
->fraglen
;
1057 device
->playpos
= playpos
;
1058 device
->mixpos
= writepos
;
1060 maxq
= device
->buflen
;
1061 if (maxq
> frag
) maxq
= frag
;
1062 FillMemory(device
->buffer
, device
->buflen
, nfiller
);
1067 frag
= DSOUND_MixToPrimary(device
, playpos
, writepos
, maxq
, paused
);
1068 if (forced
) frag
= maxq
- inq
;
1069 device
->mixpos
+= frag
;
1070 device
->mixpos
%= device
->buflen
;
1073 /* buffers have been filled, restart playback */
1074 if (device
->state
== STATE_STARTING
) {
1075 device
->state
= STATE_PLAYING
;
1077 else if (device
->state
== STATE_STOPPED
) {
1078 /* the dsound is supposed to play if there's something to play
1079 * even if it is reported as stopped, so don't let this confuse you */
1080 device
->state
= STATE_STOPPING
;
1082 LeaveCriticalSection(&(device
->mixlock
));
1084 if (DSOUND_PrimaryPlay(device
) != DS_OK
)
1085 WARN("DSOUND_PrimaryPlay failed\n");
1087 TRACE("starting playback\n");
1091 LeaveCriticalSection(&(device
->mixlock
));
1093 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
1094 if (device
->state
== STATE_STARTING
) {
1095 if (DSOUND_PrimaryPlay(device
) != DS_OK
)
1096 WARN("DSOUND_PrimaryPlay failed\n");
1098 device
->state
= STATE_PLAYING
;
1100 else if (device
->state
== STATE_STOPPING
) {
1101 if (DSOUND_PrimaryStop(device
) != DS_OK
)
1102 WARN("DSOUND_PrimaryStop failed\n");
1104 device
->state
= STATE_STOPPED
;
1109 void CALLBACK
DSOUND_timer(UINT timerID
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
)
1111 DirectSoundDevice
* device
= (DirectSoundDevice
*)dwUser
;
1112 DWORD start_time
= GetTickCount();
1114 TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID
,msg
,dwUser
,dw1
,dw2
);
1115 TRACE("entering at %ld\n", start_time
);
1117 if (DSOUND_renderer
[device
->drvdesc
.dnDevNode
] != device
) {
1118 ERR("dsound died without killing us?\n");
1119 timeKillEvent(timerID
);
1120 timeEndPeriod(DS_TIME_RES
);
1124 RtlAcquireResourceShared(&(device
->buffer_list_lock
), TRUE
);
1127 DSOUND_PerformMix(device
);
1129 RtlReleaseResource(&(device
->buffer_list_lock
));
1131 end_time
= GetTickCount();
1132 TRACE("completed processing at %ld, duration = %ld\n", end_time
, end_time
- start_time
);
1135 void CALLBACK
DSOUND_callback(HWAVEOUT hwo
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
)
1137 DirectSoundDevice
* device
= (DirectSoundDevice
*)dwUser
;
1138 TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo
,msg
,dwUser
,dw1
,dw2
);
1139 TRACE("entering at %ld, msg=%08x(%s)\n", GetTickCount(), msg
,
1140 msg
==MM_WOM_DONE
? "MM_WOM_DONE" : msg
==MM_WOM_CLOSE
? "MM_WOM_CLOSE" :
1141 msg
==MM_WOM_OPEN
? "MM_WOM_OPEN" : "UNKNOWN");
1142 if (msg
== MM_WOM_DONE
) {
1143 DWORD inq
, mixq
, fraglen
, buflen
, pwplay
, playpos
, mixpos
;
1144 if (device
->pwqueue
== (DWORD
)-1) {
1145 TRACE("completed due to reset\n");
1148 /* it could be a bad idea to enter critical section here... if there's lock contention,
1149 * the resulting scheduling delays might obstruct the winmm player thread */
1150 #ifdef SYNC_CALLBACK
1151 EnterCriticalSection(&(device
->mixlock
));
1153 /* retrieve current values */
1154 fraglen
= device
->fraglen
;
1155 buflen
= device
->buflen
;
1156 pwplay
= device
->pwplay
;
1157 playpos
= pwplay
* fraglen
;
1158 mixpos
= device
->mixpos
;
1159 /* check remaining mixed data */
1160 inq
= ((mixpos
< playpos
) ? buflen
: 0) + mixpos
- playpos
;
1161 mixq
= inq
/ fraglen
;
1162 if ((inq
- (mixq
* fraglen
)) > 0) mixq
++;
1163 /* complete the playing buffer */
1164 TRACE("done playing primary pos=%ld\n", playpos
);
1166 if (pwplay
>= DS_HEL_FRAGS
) pwplay
= 0;
1167 /* write new values */
1168 device
->pwplay
= pwplay
;
1170 /* queue new buffer if we have data for it */
1171 if (inq
>1) DSOUND_WaveQueue(device
, inq
-1);
1172 #ifdef SYNC_CALLBACK
1173 LeaveCriticalSection(&(device
->mixlock
));
1176 TRACE("completed\n");