3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2002 TransGaming Technologies, Inc.
6 * Copyright 2007 Peter Dons Tychsen
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include <math.h> /* Insomnia - pow() function */
27 #define NONAMELESSSTRUCT
28 #define NONAMELESSUNION
34 #include "wine/debug.h"
37 #include "dsound_private.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(dsound
);
41 void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan
)
44 TRACE("(%p)\n",volpan
);
46 TRACE("Vol=%d Pan=%d\n", volpan
->lVolume
, volpan
->lPan
);
47 /* the AmpFactors are expressed in 16.16 fixed point */
48 volpan
->dwVolAmpFactor
= (ULONG
) (pow(2.0, volpan
->lVolume
/ 600.0) * 0xffff);
49 /* FIXME: dwPan{Left|Right}AmpFactor */
51 /* FIXME: use calculated vol and pan ampfactors */
52 temp
= (double) (volpan
->lVolume
- (volpan
->lPan
> 0 ? volpan
->lPan
: 0));
53 volpan
->dwTotalLeftAmpFactor
= (ULONG
) (pow(2.0, temp
/ 600.0) * 0xffff);
54 temp
= (double) (volpan
->lVolume
+ (volpan
->lPan
< 0 ? volpan
->lPan
: 0));
55 volpan
->dwTotalRightAmpFactor
= (ULONG
) (pow(2.0, temp
/ 600.0) * 0xffff);
57 TRACE("left = %x, right = %x\n", volpan
->dwTotalLeftAmpFactor
, volpan
->dwTotalRightAmpFactor
);
60 void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan
)
63 TRACE("(%p)\n",volpan
);
65 TRACE("left=%x, right=%x\n",volpan
->dwTotalLeftAmpFactor
,volpan
->dwTotalRightAmpFactor
);
66 if (volpan
->dwTotalLeftAmpFactor
==0)
69 left
=600 * log(((double)volpan
->dwTotalLeftAmpFactor
) / 0xffff) / log(2);
70 if (volpan
->dwTotalRightAmpFactor
==0)
73 right
=600 * log(((double)volpan
->dwTotalRightAmpFactor
) / 0xffff) / log(2);
76 volpan
->lVolume
=right
;
77 volpan
->dwVolAmpFactor
=volpan
->dwTotalRightAmpFactor
;
82 volpan
->dwVolAmpFactor
=volpan
->dwTotalLeftAmpFactor
;
84 if (volpan
->lVolume
< -10000)
85 volpan
->lVolume
=-10000;
86 volpan
->lPan
=right
-left
;
87 if (volpan
->lPan
< -10000)
90 TRACE("Vol=%d Pan=%d\n", volpan
->lVolume
, volpan
->lPan
);
93 void DSOUND_RecalcFormat(IDirectSoundBufferImpl
*dsb
)
97 /* calculate the 10ms write lead */
98 dsb
->writelead
= (dsb
->freq
/ 100) * dsb
->pwfx
->nBlockAlign
;
102 * Check for application callback requests for when the play position
103 * reaches certain points.
105 * The offsets that will be triggered will be those between the recorded
106 * "last played" position for the buffer (i.e. dsb->playpos) and "len" bytes
107 * beyond that position.
109 void DSOUND_CheckEvent(IDirectSoundBufferImpl
*dsb
, DWORD playpos
, int len
)
113 LPDSBPOSITIONNOTIFY event
;
114 TRACE("(%p,%d)\n",dsb
,len
);
116 if (dsb
->nrofnotifies
== 0)
119 TRACE("(%p) buflen = %d, playpos = %d, len = %d\n",
120 dsb
, dsb
->buflen
, playpos
, len
);
121 for (i
= 0; i
< dsb
->nrofnotifies
; i
++) {
122 event
= dsb
->notifies
+ i
;
123 offset
= event
->dwOffset
;
124 TRACE("checking %d, position %d, event = %p\n",
125 i
, offset
, event
->hEventNotify
);
126 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
127 /* OK. [Inside DirectX, p274] */
129 /* This also means we can't sort the entries by offset, */
130 /* because DSBPN_OFFSETSTOP == -1 */
131 if (offset
== DSBPN_OFFSETSTOP
) {
132 if (dsb
->state
== STATE_STOPPED
) {
133 SetEvent(event
->hEventNotify
);
134 TRACE("signalled event %p (%d)\n", event
->hEventNotify
, i
);
139 if ((playpos
+ len
) >= dsb
->buflen
) {
140 if ((offset
< ((playpos
+ len
) % dsb
->buflen
)) ||
141 (offset
>= playpos
)) {
142 TRACE("signalled event %p (%d)\n", event
->hEventNotify
, i
);
143 SetEvent(event
->hEventNotify
);
146 if ((offset
>= playpos
) && (offset
< (playpos
+ len
))) {
147 TRACE("signalled event %p (%d)\n", event
->hEventNotify
, i
);
148 SetEvent(event
->hEventNotify
);
154 /* WAV format info can be found at:
156 * http://www.cwi.nl/ftp/audio/AudioFormats.part2
157 * ftp://ftp.cwi.nl/pub/audio/RIFF-format
159 * Import points to remember:
160 * 8-bit WAV is unsigned
161 * 16-bit WAV is signed
163 /* Use the same formulas as pcmconverter.c */
164 static inline INT16
cvtU8toS16(BYTE b
)
166 return (short)((b
+(b
<< 8))-32768);
169 static inline BYTE
cvtS16toU8(INT16 s
)
171 return (s
>> 8) ^ (unsigned char)0x80;
175 * Copy a single frame from the given input buffer to the given output buffer.
176 * Translate 8 <-> 16 bits and mono <-> stereo
178 static inline void cp_fields(const IDirectSoundBufferImpl
*dsb
, const BYTE
*ibuf
, BYTE
*obuf
)
180 DirectSoundDevice
* device
= dsb
->device
;
183 if (dsb
->pwfx
->wBitsPerSample
== 8) {
184 if (device
->pwfx
->wBitsPerSample
== 8 &&
185 device
->pwfx
->nChannels
== dsb
->pwfx
->nChannels
) {
186 /* avoid needless 8->16->8 conversion */
188 if (dsb
->pwfx
->nChannels
==2)
192 fl
= cvtU8toS16(*ibuf
);
193 fr
= (dsb
->pwfx
->nChannels
==2 ? cvtU8toS16(*(ibuf
+ 1)) : fl
);
195 fl
= *((const INT16
*)ibuf
);
196 fr
= (dsb
->pwfx
->nChannels
==2 ? *(((const INT16
*)ibuf
) + 1) : fl
);
199 if (device
->pwfx
->nChannels
== 2) {
200 if (device
->pwfx
->wBitsPerSample
== 8) {
201 *obuf
= cvtS16toU8(fl
);
202 *(obuf
+ 1) = cvtS16toU8(fr
);
205 if (device
->pwfx
->wBitsPerSample
== 16) {
206 *((INT16
*)obuf
) = fl
;
207 *(((INT16
*)obuf
) + 1) = fr
;
211 if (device
->pwfx
->nChannels
== 1) {
213 if (device
->pwfx
->wBitsPerSample
== 8) {
214 *obuf
= cvtS16toU8(fl
);
217 if (device
->pwfx
->wBitsPerSample
== 16) {
218 *((INT16
*)obuf
) = fl
;
225 * Mix at most the given amount of data into the given device buffer from the
226 * given secondary buffer, starting from the dsb's first currently unmixed
227 * frame (buf_mixpos), translating frequency (pitch), stereo/mono and
228 * bits-per-sample. The secondary buffer sample is looped if it is not
229 * long enough and it is a looping buffer.
230 * (Doesn't perform any mixing - this is a straight copy operation).
232 * Now with PerfectPitch (tm) technology
234 * dsb = the secondary buffer
235 * buf = the device buffer
236 * len = number of bytes to store in the device buffer
238 * Returns: the number of bytes read from the secondary buffer
239 * (ie. len, adjusted for frequency, number of channels and sample size,
240 * and limited by buffer length for non-looping buffers)
242 static INT
DSOUND_MixerNorm(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
244 INT i
, size
, ipos
, ilen
;
246 INT iAdvance
= dsb
->pwfx
->nBlockAlign
;
247 INT oAdvance
= dsb
->device
->pwfx
->nBlockAlign
;
249 ibp
= dsb
->buffer
->memory
+ dsb
->buf_mixpos
;
252 TRACE("(%p, %p, %p), buf_mixpos=%d\n", dsb
, ibp
, obp
, dsb
->buf_mixpos
);
253 /* Check for the best case */
254 if ((dsb
->freq
== dsb
->device
->pwfx
->nSamplesPerSec
) &&
255 (dsb
->pwfx
->wBitsPerSample
== dsb
->device
->pwfx
->wBitsPerSample
) &&
256 (dsb
->pwfx
->nChannels
== dsb
->device
->pwfx
->nChannels
)) {
257 INT bytesleft
= dsb
->buflen
- dsb
->buf_mixpos
;
258 TRACE("(%p) Best case\n", dsb
);
259 if (len
<= bytesleft
)
260 CopyMemory(obp
, ibp
, len
);
262 CopyMemory(obp
, ibp
, bytesleft
);
263 CopyMemory(obp
+ bytesleft
, dsb
->buffer
->memory
, len
- bytesleft
);
268 /* Check for same sample rate */
269 if (dsb
->freq
== dsb
->device
->pwfx
->nSamplesPerSec
) {
270 TRACE("(%p) Same sample rate %d = primary %d\n", dsb
,
271 dsb
->freq
, dsb
->device
->pwfx
->nSamplesPerSec
);
273 for (i
= 0; i
< len
; i
+= oAdvance
) {
274 cp_fields(dsb
, ibp
, obp
);
278 if (ibp
>= (BYTE
*)(dsb
->buffer
->memory
+ dsb
->buflen
))
279 ibp
= dsb
->buffer
->memory
; /* wrap */
284 /* Mix in different sample rates */
286 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
287 /* Patent Pending :-] */
289 /* Patent enhancements (c) 2000 Ove K�ven,
290 * TransGaming Technologies Inc. */
292 /* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
293 dsb, dsb->freq, dsb->device->pwfx->nSamplesPerSec); */
295 size
= len
/ oAdvance
;
297 ipos
= dsb
->buf_mixpos
;
298 for (i
= 0; i
< size
; i
++) {
299 cp_fields(dsb
, (dsb
->buffer
->memory
+ ipos
), obp
);
301 dsb
->freqAcc
+= dsb
->freqAdjust
;
302 if (dsb
->freqAcc
>= (1<<DSOUND_FREQSHIFT
)) {
303 ULONG adv
= (dsb
->freqAcc
>>DSOUND_FREQSHIFT
) * iAdvance
;
304 dsb
->freqAcc
&= (1<<DSOUND_FREQSHIFT
)-1;
305 ipos
+= adv
; ilen
+= adv
;
312 static void DSOUND_MixerVol(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
316 INT16
*bps
= (INT16
*) buf
;
318 TRACE("(%p,%p,%d)\n",dsb
,buf
,len
);
319 TRACE("left = %x, right = %x\n", dsb
->volpan
.dwTotalLeftAmpFactor
,
320 dsb
->volpan
.dwTotalRightAmpFactor
);
322 if ((!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) || (dsb
->volpan
.lPan
== 0)) &&
323 (!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
) || (dsb
->volpan
.lVolume
== 0)) &&
324 !(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
))
325 return; /* Nothing to do */
327 /* If we end up with some bozo coder using panning or 3D sound */
328 /* with a mono primary buffer, it could sound very weird using */
329 /* this method. Oh well, tough patooties. */
331 switch (dsb
->device
->pwfx
->wBitsPerSample
) {
333 /* 8-bit WAV is unsigned, but we need to operate */
334 /* on signed data for this to work properly */
335 switch (dsb
->device
->pwfx
->nChannels
) {
337 for (i
= 0; i
< len
; i
++) {
338 INT val
= *bpc
- 128;
339 val
= (val
* dsb
->volpan
.dwTotalLeftAmpFactor
) >> 16;
345 for (i
= 0; i
< len
; i
+=2) {
346 INT val
= *bpc
- 128;
347 val
= (val
* dsb
->volpan
.dwTotalLeftAmpFactor
) >> 16;
350 val
= (val
* dsb
->volpan
.dwTotalRightAmpFactor
) >> 16;
356 FIXME("doesn't support %d channels\n", dsb
->device
->pwfx
->nChannels
);
361 /* 16-bit WAV is signed -- much better */
362 switch (dsb
->device
->pwfx
->nChannels
) {
364 for (i
= 0; i
< len
; i
+= 2) {
365 *bps
= (*bps
* dsb
->volpan
.dwTotalLeftAmpFactor
) >> 16;
370 for (i
= 0; i
< len
; i
+= 4) {
371 *bps
= (*bps
* dsb
->volpan
.dwTotalLeftAmpFactor
) >> 16;
373 *bps
= (*bps
* dsb
->volpan
.dwTotalRightAmpFactor
) >> 16;
378 FIXME("doesn't support %d channels\n", dsb
->device
->pwfx
->nChannels
);
383 FIXME("doesn't support %d bit samples\n", dsb
->device
->pwfx
->wBitsPerSample
);
389 * Make sure the device's tmp_buffer is at least the given size. Return a
392 static LPBYTE
DSOUND_tmpbuffer(DirectSoundDevice
*device
, DWORD len
)
394 TRACE("(%p,%d)\n", device
, len
);
396 if (len
> device
->tmp_buffer_len
) {
397 if (device
->tmp_buffer
)
398 device
->tmp_buffer
= HeapReAlloc(GetProcessHeap(), 0, device
->tmp_buffer
, len
);
400 device
->tmp_buffer
= HeapAlloc(GetProcessHeap(), 0, len
);
402 device
->tmp_buffer_len
= len
;
405 return device
->tmp_buffer
;
409 * Mix (at most) the given number of bytes into the given position of the
410 * device buffer, from the secondary buffer "dsb" (starting at the current
411 * mix position for that buffer).
413 * Returns the number of bytes actually mixed into the device buffer. This
414 * will match fraglen unless the end of the secondary buffer is reached
415 * (and it is not looping).
417 * dsb = the secondary buffer to mix from
418 * writepos = position (offset) in device buffer to write at
419 * fraglen = number of bytes to mix
421 static DWORD
DSOUND_MixInBuffer(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, DWORD fraglen
)
423 INT i
, len
, ilen
, field
, todo
;
426 TRACE("(%p,%d,%d)\n",dsb
,writepos
,fraglen
);
429 if (!(dsb
->playflags
& DSBPLAY_LOOPING
)) {
430 /* This buffer is not looping, so make sure the requested
431 * length will not take us past the end of the buffer */
432 int secondary_remainder
= dsb
->buflen
- dsb
->buf_mixpos
;
433 int adjusted_remainder
= MulDiv(dsb
->device
->pwfx
->nAvgBytesPerSec
, secondary_remainder
, dsb
->nAvgBytesPerSec
);
434 assert(adjusted_remainder
>= 0);
435 adjusted_remainder
-= adjusted_remainder
% dsb
->device
->pwfx
->nBlockAlign
; /* data alignment */
436 /* The adjusted remainder must be at least one sample,
437 * otherwise we will never reach the end of the
438 * secondary buffer, as there will perpetually be a
439 * fractional remainder */
440 TRACE("secondary_remainder = %d, adjusted_remainder = %d, len = %d\n", secondary_remainder
, adjusted_remainder
, len
);
441 if (adjusted_remainder
< len
) {
442 TRACE("clipping len to remainder of secondary buffer\n");
443 len
= adjusted_remainder
;
449 if (len
% dsb
->device
->pwfx
->nBlockAlign
) {
450 INT nBlockAlign
= dsb
->device
->pwfx
->nBlockAlign
;
451 ERR("length not a multiple of block size, len = %d, block size = %d\n", len
, nBlockAlign
);
452 len
-= len
% nBlockAlign
; /* data alignment */
455 /* Create temp buffer to hold actual resulting data */
456 if ((buf
= ibuf
= DSOUND_tmpbuffer(dsb
->device
, len
)) == NULL
)
459 TRACE("MixInBuffer (%p) len = %d, dest = %d\n", dsb
, len
, writepos
);
461 /* first, copy the data from the DirectSoundBuffer into the temporary
462 buffer, translating frequency/bits-per-sample/number-of-channels
463 to match the device settings */
464 ilen
= DSOUND_MixerNorm(dsb
, ibuf
, len
);
466 /* then apply the correct volume, if necessary */
467 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
468 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
) ||
469 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
))
470 DSOUND_MixerVol(dsb
, ibuf
, len
);
472 /* Now mix the temporary buffer into the devices main buffer */
473 if (dsb
->device
->pwfx
->wBitsPerSample
== 8) {
474 BYTE
*obuf
= dsb
->device
->buffer
+ writepos
;
476 if ((writepos
+ len
) <= dsb
->device
->buflen
)
479 todo
= dsb
->device
->buflen
- writepos
;
481 for (i
= 0; i
< todo
; i
++) {
482 /* 8-bit WAV is unsigned */
483 field
= (*ibuf
++ - 128);
484 field
+= (*obuf
- 128);
485 if (field
> 127) field
= 127;
486 else if (field
< -128) field
= -128;
487 *obuf
++ = field
+ 128;
492 obuf
= dsb
->device
->buffer
;
494 for (i
= 0; i
< todo
; i
++) {
495 /* 8-bit WAV is unsigned */
496 field
= (*ibuf
++ - 128);
497 field
+= (*obuf
- 128);
498 if (field
> 127) field
= 127;
499 else if (field
< -128) field
= -128;
500 *obuf
++ = field
+ 128;
504 INT16
*ibufs
, *obufs
;
506 ibufs
= (INT16
*) ibuf
;
507 obufs
= (INT16
*)(dsb
->device
->buffer
+ writepos
);
509 if ((writepos
+ len
) <= dsb
->device
->buflen
)
512 todo
= (dsb
->device
->buflen
- writepos
) / 2;
514 for (i
= 0; i
< todo
; i
++) {
515 /* 16-bit WAV is signed */
518 if (field
> 32767) field
= 32767;
519 else if (field
< -32768) field
= -32768;
523 if (todo
< (len
/ 2)) {
524 todo
= (len
/ 2) - todo
;
525 obufs
= (INT16
*)dsb
->device
->buffer
;
527 for (i
= 0; i
< todo
; i
++) {
528 /* 16-bit WAV is signed */
531 if (field
> 32767) field
= 32767;
532 else if (field
< -32768) field
= -32768;
538 if (dsb
->leadin
&& (dsb
->startpos
> dsb
->buf_mixpos
) && (dsb
->startpos
<= dsb
->buf_mixpos
+ ilen
)) {
539 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
540 * not the MIX position... but if the sound buffer is bigger than our prebuffering
541 * (which must be the case for the streaming buffers that need this hack anyway)
542 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
546 dsb
->buf_mixpos
+= ilen
;
548 if (dsb
->buf_mixpos
>= dsb
->buflen
) {
549 if (dsb
->playflags
& DSBPLAY_LOOPING
) {
551 dsb
->buf_mixpos
%= dsb
->buflen
;
552 if (dsb
->leadin
&& (dsb
->startpos
<= dsb
->buf_mixpos
))
553 dsb
->leadin
= FALSE
; /* HACK: see above */
554 } else if (dsb
->buf_mixpos
> dsb
->buflen
) {
555 ERR("Mixpos (%u) past buflen (%u), capping...\n", dsb
->buf_mixpos
, dsb
->buflen
);
556 dsb
->buf_mixpos
= dsb
->buflen
;
564 * Calculate the distance between two buffer offsets, taking wraparound
567 static inline DWORD
DSOUND_BufPtrDiff(DWORD buflen
, DWORD ptr1
, DWORD ptr2
)
572 return buflen
+ ptr1
- ptr2
;
577 * Mix some frames from the given secondary buffer "dsb" into the device
580 * dsb = the secondary buffer
581 * playpos = the current play position in the device buffer (primary buffer)
582 * writepos = the current safe-to-write position in the device buffer
583 * mixlen = the maximum number of bytes in the primary buffer to mix, from the
586 * Returns: the number of bytes beyond the writepos that were mixed.
588 static DWORD
DSOUND_MixOne(IDirectSoundBufferImpl
*dsb
, DWORD playpos
, DWORD writepos
, DWORD mixlen
)
590 /* The buffer's primary_mixpos may be before or after the the device
591 * buffer's mixpos, but both must be ahead of writepos. */
592 DWORD primary_done
, buflen
= dsb
->buflen
/ dsb
->pwfx
->nBlockAlign
* dsb
->device
->pwfx
->nBlockAlign
;
594 TRACE("(%p,%d,%d,%d)\n",dsb
,playpos
,writepos
,mixlen
);
595 TRACE("writepos=%d, buf_mixpos=%d, primary_mixpos=%d, mixlen=%d\n", writepos
, dsb
->buf_mixpos
, dsb
->primary_mixpos
, mixlen
);
596 TRACE("looping=%d, startpos=%d, leadin=%d, buflen=%d\n", dsb
->playflags
, dsb
->startpos
, dsb
->leadin
, dsb
->buflen
);
598 /* calculate how much pre-buffering has already been done for this buffer */
599 primary_done
= DSOUND_BufPtrDiff(dsb
->device
->buflen
, dsb
->primary_mixpos
, writepos
);
602 if(mixlen
< primary_done
)
604 /* Should *NEVER* happen */
605 ERR("Fatal error. Under/Overflow? primary_done=%d, mixpos=%d, primary_mixpos=%d, writepos=%d, playpos=%d\n", primary_done
,dsb
->buf_mixpos
,dsb
->primary_mixpos
, writepos
, playpos
);
609 /* take into acount already mixed data */
610 mixlen
= mixlen
- primary_done
;
612 TRACE("mixlen (primary) = %i\n", mixlen
);
614 /* clip to valid length */
615 mixlen
= (buflen
< mixlen
) ? buflen
: mixlen
;
617 TRACE("primary_done=%d, mixlen (buffer)=%d\n", primary_done
, mixlen
);
620 mixlen
= DSOUND_MixInBuffer(dsb
, dsb
->primary_mixpos
, mixlen
);
622 /* check for notification positions */
623 if (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPOSITIONNOTIFY
&&
624 dsb
->state
!= STATE_STARTING
) {
625 DSOUND_CheckEvent(dsb
, writepos
, mixlen
/ dsb
->device
->pwfx
->nBlockAlign
* dsb
->pwfx
->nBlockAlign
);
628 /* increase mix position */
629 dsb
->primary_mixpos
+= mixlen
;
630 dsb
->primary_mixpos
%= dsb
->device
->buflen
;
632 TRACE("new primary_mixpos=%d, mixed data len=%d, buffer left = %d\n",
633 dsb
->primary_mixpos
, mixlen
, (dsb
->buflen
- dsb
->buf_mixpos
));
635 /* re-calculate the primary done */
636 primary_done
= DSOUND_BufPtrDiff(dsb
->device
->buflen
, dsb
->primary_mixpos
, writepos
);
638 /* check if buffer should be considered complete */
639 if (((dsb
->buflen
- dsb
->buf_mixpos
) < dsb
->writelead
) &&
640 !(dsb
->playflags
& DSBPLAY_LOOPING
)) {
642 TRACE("Buffer reached end. Stopped\n");
644 dsb
->state
= STATE_STOPPED
;
649 /* Report back the total prebuffered amount for this buffer */
654 * For a DirectSoundDevice, go through all the currently playing buffers and
655 * mix them in to the device buffer.
657 * playpos = the current play position in the primary buffer
658 * writepos = the current safe-to-write position in the primary buffer
659 * mixlen = the maximum amount to mix into the primary buffer
660 * (beyond the current writepos)
661 * recover = true if the sound device may have been reset and the write
662 * position in the device buffer changed
663 * all_stopped = reports back if all buffers have stopped
665 * Returns: the length beyond the writepos that was mixed to.
668 static DWORD
DSOUND_MixToPrimary(const DirectSoundDevice
*device
, DWORD playpos
, DWORD writepos
,
669 DWORD mixlen
, BOOL recover
, BOOL
*all_stopped
)
673 IDirectSoundBufferImpl
*dsb
;
675 /* unless we find a running buffer, all have stopped */
678 TRACE("(%d,%d,%d,%d)\n", playpos
, writepos
, mixlen
, recover
);
679 for (i
= 0; i
< device
->nrofbuffers
; i
++) {
680 dsb
= device
->buffers
[i
];
682 TRACE("MixToPrimary for %p, state=%d\n", dsb
, dsb
->state
);
684 if (dsb
->buflen
&& dsb
->state
&& !dsb
->hwbuf
) {
685 TRACE("Checking %p, mixlen=%d\n", dsb
, mixlen
);
686 EnterCriticalSection(&(dsb
->lock
));
688 /* if buffer is stopping it is stopped now */
689 if (dsb
->state
== STATE_STOPPING
) {
690 dsb
->state
= STATE_STOPPED
;
691 DSOUND_CheckEvent(dsb
, 0, 0);
694 /* if recovering, reset the mix position */
695 if ((dsb
->state
== STATE_STARTING
) || recover
) {
696 dsb
->primary_mixpos
= writepos
;
699 /* mix next buffer into the main buffer */
700 len
= DSOUND_MixOne(dsb
, playpos
, writepos
, mixlen
);
702 /* if the buffer was starting, it must be playing now */
703 if (dsb
->state
== STATE_STARTING
)
704 dsb
->state
= STATE_PLAYING
;
706 /* check if min-len should be initialized */
707 if(minlen
== 0) minlen
= len
;
709 /* record the minimum length mixed from all buffers */
710 /* we only want to return the length which *all* buffers have mixed */
711 if(len
!= 0) minlen
= (len
< minlen
) ? len
: minlen
;
714 if(dsb
->state
!= STATE_STOPPED
){
715 *all_stopped
= FALSE
;
718 LeaveCriticalSection(&(dsb
->lock
));
722 TRACE("Mixed at least %d from all buffers\n", minlen
);
728 * Add buffers to the emulated wave device system.
730 * device = The current dsound playback device
731 * force = If TRUE, the function will buffer up as many frags as possible,
732 * even though and will ignore the actual state of the primary buffer.
737 static void DSOUND_WaveQueue(DirectSoundDevice
*device
, BOOL force
)
739 DWORD prebuf_frags
, wave_writepos
, wave_fragpos
, i
;
740 TRACE("(%p)\n", device
);
742 /* calculate the current wave frag position */
743 wave_fragpos
= (device
->pwplay
+ device
->pwqueue
) % DS_HEL_FRAGS
;
745 /* calculte the current wave write position */
746 wave_writepos
= wave_fragpos
* device
->fraglen
;
748 TRACE("wave_fragpos = %i, wave_writepos = %i, pwqueue = %i, prebuf = %i\n",
749 wave_fragpos
, wave_writepos
, device
->pwqueue
, device
->prebuf
);
752 /* check remaining prebuffered frags */
753 prebuf_frags
= DSOUND_BufPtrDiff(device
->buflen
, device
->mixpos
, wave_writepos
);
754 prebuf_frags
= prebuf_frags
/ device
->fraglen
;
757 /* buffer the maximum amount of frags */
758 prebuf_frags
= device
->prebuf
;
761 /* limit to the queue we have left */
762 if((prebuf_frags
+ device
->pwqueue
) > device
->prebuf
)
763 prebuf_frags
= device
->prebuf
- device
->pwqueue
;
765 TRACE("prebuf_frags = %i\n", prebuf_frags
);
768 device
->pwqueue
+= prebuf_frags
;
770 /* get out of CS when calling the wave system */
771 LeaveCriticalSection(&(device
->mixlock
));
774 /* queue up the new buffers */
775 for(i
=0; i
<prebuf_frags
; i
++){
776 TRACE("queueing wave buffer %i\n", wave_fragpos
);
777 waveOutWrite(device
->hwo
, device
->pwave
[wave_fragpos
], sizeof(WAVEHDR
));
779 wave_fragpos
%= DS_HEL_FRAGS
;
783 EnterCriticalSection(&(device
->mixlock
));
785 TRACE("queue now = %i\n", device
->pwqueue
);
789 * Perform mixing for a Direct Sound device. That is, go through all the
790 * secondary buffers (the sound bites currently playing) and mix them in
791 * to the primary buffer (the device buffer).
793 static void DSOUND_PerformMix(DirectSoundDevice
*device
)
796 TRACE("(%p)\n", device
);
799 EnterCriticalSection(&(device
->mixlock
));
801 if (device
->priolevel
!= DSSCL_WRITEPRIMARY
) {
802 BOOL recover
= FALSE
, all_stopped
= FALSE
;
803 DWORD playpos
, writepos
, writelead
, maxq
, frag
, prebuff_max
, prebuff_left
, size1
, size2
;
805 BOOL lock
= (device
->hwbuf
&& !(device
->drvdesc
.dwFlags
& DSDDESC_DONTNEEDPRIMARYLOCK
));
808 /* the sound of silence */
809 nfiller
= device
->pwfx
->wBitsPerSample
== 8 ? 128 : 0;
811 /* get the position in the primary buffer */
812 if (DSOUND_PrimaryGetPosition(device
, &playpos
, &writepos
) != 0){
813 LeaveCriticalSection(&(device
->mixlock
));
817 TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n",
818 playpos
,writepos
,device
->playpos
,device
->mixpos
,device
->buflen
);
819 assert(device
->playpos
< device
->buflen
);
821 /* wipe out just-played sound data */
822 if (playpos
< device
->playpos
) {
823 buf1
= device
->buffer
+ device
->playpos
;
824 buf2
= device
->buffer
;
825 size1
= device
->buflen
- device
->playpos
;
828 IDsDriverBuffer_Lock(device
->hwbuf
, &buf1
, &size1
, &buf2
, &size2
, device
->playpos
, size1
+size2
, 0);
829 FillMemory(buf1
, size1
, nfiller
);
830 if (playpos
&& (!buf2
|| !size2
))
831 FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__
, device
->playpos
, device
->mixpos
, playpos
, writepos
);
832 FillMemory(buf2
, size2
, nfiller
);
834 IDsDriverBuffer_Unlock(device
->hwbuf
, buf1
, size1
, buf2
, size2
);
836 buf1
= device
->buffer
+ device
->playpos
;
838 size1
= playpos
- device
->playpos
;
841 IDsDriverBuffer_Lock(device
->hwbuf
, &buf1
, &size1
, &buf2
, &size2
, device
->playpos
, size1
+size2
, 0);
842 FillMemory(buf1
, size1
, nfiller
);
845 FIXME("%d: There should be no additional buffer here!!\n", __LINE__
);
846 FillMemory(buf2
, size2
, nfiller
);
849 IDsDriverBuffer_Unlock(device
->hwbuf
, buf1
, size1
, buf2
, size2
);
851 device
->playpos
= playpos
;
853 /* calc maximum prebuff */
854 prebuff_max
= (device
->prebuf
* device
->fraglen
);
856 /* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */
857 prebuff_left
= DSOUND_BufPtrDiff(device
->buflen
, device
->mixpos
, playpos
);
859 writelead
= DSOUND_BufPtrDiff(device
->buflen
, writepos
, playpos
);
861 /* find the maximum we can prebuffer from current write position */
862 maxq
= prebuff_max
- prebuff_left
;
863 maxq
= (writelead
< prebuff_max
) ? (prebuff_max
- writelead
) : 0;
865 TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n",
866 prebuff_left
, device
->prebuf
, device
->fraglen
, prebuff_max
, writelead
);
868 /* check for underrun. underrun occurs when the write position passes the mix position */
869 if((prebuff_left
> prebuff_max
) || (device
->state
== STATE_STOPPED
) || (device
->state
== STATE_STARTING
)){
870 TRACE("Buffer starting or buffer underrun\n");
872 /* recover mixing for all buffers */
875 /* reset mix position to write position */
876 device
->mixpos
= writepos
;
880 IDsDriverBuffer_Lock(device
->hwbuf
, &buf1
, &size1
, &buf2
, &size2
, device
->mixpos
, maxq
, 0);
883 frag
= DSOUND_MixToPrimary(device
, playpos
, writepos
, maxq
, recover
, &all_stopped
);
885 /* update the mix position, taking wrap-around into acount */
886 device
->mixpos
= writepos
+ frag
;
887 device
->mixpos
%= device
->buflen
;
891 DWORD frag2
= (frag
> size1
? frag
- size1
: 0);
895 FIXME("Buffering too much! (%d, %d, %d, %d)\n", maxq
, frag
, size2
, frag2
- size2
);
898 IDsDriverBuffer_Unlock(device
->hwbuf
, buf1
, frag
, buf2
, frag2
);
901 /* update prebuff left */
902 prebuff_left
= DSOUND_BufPtrDiff(device
->buflen
, device
->mixpos
, playpos
);
904 /* check if have a whole fragment */
905 if (prebuff_left
>= device
->fraglen
){
907 /* update the wave queue if using wave system */
908 if(device
->hwbuf
== NULL
){
909 DSOUND_WaveQueue(device
,TRUE
);
912 /* buffers are full. start playing if applicable */
913 if(device
->state
== STATE_STARTING
){
914 TRACE("started primary buffer\n");
915 if(DSOUND_PrimaryPlay(device
) != DS_OK
){
916 WARN("DSOUND_PrimaryPlay failed\n");
919 /* we are playing now */
920 device
->state
= STATE_PLAYING
;
924 /* buffers are full. start stopping if applicable */
925 if(device
->state
== STATE_STOPPED
){
926 TRACE("restarting primary buffer\n");
927 if(DSOUND_PrimaryPlay(device
) != DS_OK
){
928 WARN("DSOUND_PrimaryPlay failed\n");
931 /* start stopping again. as soon as there is no more data, it will stop */
932 device
->state
= STATE_STOPPING
;
937 /* if device was stopping, its for sure stopped when all buffers have stopped */
938 else if((all_stopped
== TRUE
) && (device
->state
== STATE_STOPPING
)){
939 TRACE("All buffers have stopped. Stopping primary buffer\n");
940 device
->state
= STATE_STOPPED
;
942 /* stop the primary buffer now */
943 DSOUND_PrimaryStop(device
);
948 /* update the wave queue if using wave system */
949 if(device
->hwbuf
== NULL
){
950 DSOUND_WaveQueue(device
, TRUE
);
953 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
954 if (device
->state
== STATE_STARTING
) {
955 if (DSOUND_PrimaryPlay(device
) != DS_OK
)
956 WARN("DSOUND_PrimaryPlay failed\n");
958 device
->state
= STATE_PLAYING
;
960 else if (device
->state
== STATE_STOPPING
) {
961 if (DSOUND_PrimaryStop(device
) != DS_OK
)
962 WARN("DSOUND_PrimaryStop failed\n");
964 device
->state
= STATE_STOPPED
;
968 LeaveCriticalSection(&(device
->mixlock
));
972 void CALLBACK
DSOUND_timer(UINT timerID
, UINT msg
, DWORD_PTR dwUser
,
973 DWORD_PTR dw1
, DWORD_PTR dw2
)
975 DirectSoundDevice
* device
= (DirectSoundDevice
*)dwUser
;
976 DWORD start_time
= GetTickCount();
978 TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID
,msg
,dwUser
,dw1
,dw2
);
979 TRACE("entering at %d\n", start_time
);
981 if (DSOUND_renderer
[device
->drvdesc
.dnDevNode
] != device
) {
982 ERR("dsound died without killing us?\n");
983 timeKillEvent(timerID
);
984 timeEndPeriod(DS_TIME_RES
);
988 RtlAcquireResourceShared(&(device
->buffer_list_lock
), TRUE
);
991 DSOUND_PerformMix(device
);
993 RtlReleaseResource(&(device
->buffer_list_lock
));
995 end_time
= GetTickCount();
996 TRACE("completed processing at %d, duration = %d\n", end_time
, end_time
- start_time
);
999 void CALLBACK
DSOUND_callback(HWAVEOUT hwo
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
)
1001 DirectSoundDevice
* device
= (DirectSoundDevice
*)dwUser
;
1002 TRACE("(%p,%x,%x,%x,%x)\n",hwo
,msg
,dwUser
,dw1
,dw2
);
1003 TRACE("entering at %d, msg=%08x(%s)\n", GetTickCount(), msg
,
1004 msg
==MM_WOM_DONE
? "MM_WOM_DONE" : msg
==MM_WOM_CLOSE
? "MM_WOM_CLOSE" :
1005 msg
==MM_WOM_OPEN
? "MM_WOM_OPEN" : "UNKNOWN");
1007 /* check if packet completed from wave driver */
1008 if (msg
== MM_WOM_DONE
) {
1011 EnterCriticalSection(&(device
->mixlock
));
1013 TRACE("done playing primary pos=%d\n", device
->pwplay
* device
->fraglen
);
1015 /* update playpos */
1017 device
->pwplay
%= DS_HEL_FRAGS
;
1020 if(device
->pwqueue
== 0){
1021 ERR("Wave queue corrupted!\n");
1027 LeaveCriticalSection(&(device
->mixlock
));
1030 TRACE("completed\n");