cmd.exe: Add support for cmd.exe /u (Unicode from internal pgms).
[wine/wine-kai.git] / dlls / dsound / mixer.c
blob1388e6f06ce469b6b7f17597cfcb335e60811fdf
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 "winuser.h"
31 #include "mmsystem.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=%d Pan=%d\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 = %x, right = %x\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
59 void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan)
61 double left,right;
62 TRACE("(%p)\n",volpan);
64 TRACE("left=%x, right=%x\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=%d Pan=%d\n", volpan->lVolume, volpan->lPan);
92 void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
94 TRACE("(%p)\n",dsb);
96 /* calculate the 10ms write lead */
97 dsb->writelead = (dsb->freq / 100) * dsb->pwfx->nBlockAlign;
101 * Check for application callback requests for when the play position
102 * reaches certain points.
104 * The offsets that will be triggered will be those between the recorded
105 * "last played" position for the buffer (i.e. dsb->playpos) and "len" bytes
106 * beyond that position.
108 void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
110 int i;
111 DWORD offset;
112 LPDSBPOSITIONNOTIFY event;
113 TRACE("(%p,%d)\n",dsb,len);
115 if (dsb->nrofnotifies == 0)
116 return;
118 TRACE("(%p) buflen = %d, playpos = %d, len = %d\n",
119 dsb, dsb->buflen, dsb->playpos, len);
120 for (i = 0; i < dsb->nrofnotifies ; i++) {
121 event = dsb->notifies + i;
122 offset = event->dwOffset;
123 TRACE("checking %d, position %d, event = %p\n",
124 i, offset, event->hEventNotify);
125 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
126 /* OK. [Inside DirectX, p274] */
127 /* */
128 /* This also means we can't sort the entries by offset, */
129 /* because DSBPN_OFFSETSTOP == -1 */
130 if (offset == DSBPN_OFFSETSTOP) {
131 if (dsb->state == STATE_STOPPED) {
132 SetEvent(event->hEventNotify);
133 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
134 return;
135 } else
136 return;
138 if ((dsb->playpos + len) >= dsb->buflen) {
139 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
140 (offset >= dsb->playpos)) {
141 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
142 SetEvent(event->hEventNotify);
144 } else {
145 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
146 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
147 SetEvent(event->hEventNotify);
153 /* WAV format info can be found at:
155 * http://www.cwi.nl/ftp/audio/AudioFormats.part2
156 * ftp://ftp.cwi.nl/pub/audio/RIFF-format
158 * Import points to remember:
159 * 8-bit WAV is unsigned
160 * 16-bit WAV is signed
162 /* Use the same formulas as pcmconverter.c */
163 static inline INT16 cvtU8toS16(BYTE b)
165 return (short)((b+(b << 8))-32768);
168 static inline BYTE cvtS16toU8(INT16 s)
170 return (s >> 8) ^ (unsigned char)0x80;
174 * Copy a single frame from the given input buffer to the given output buffer.
175 * Translate 8 <-> 16 bits and mono <-> stereo
177 static inline void cp_fields(const IDirectSoundBufferImpl *dsb, const BYTE *ibuf, BYTE *obuf )
179 DirectSoundDevice * device = dsb->device;
180 INT fl,fr;
182 if (dsb->pwfx->wBitsPerSample == 8) {
183 if (device->pwfx->wBitsPerSample == 8 &&
184 device->pwfx->nChannels == dsb->pwfx->nChannels) {
185 /* avoid needless 8->16->8 conversion */
186 *obuf=*ibuf;
187 if (dsb->pwfx->nChannels==2)
188 *(obuf+1)=*(ibuf+1);
189 return;
191 fl = cvtU8toS16(*ibuf);
192 fr = (dsb->pwfx->nChannels==2 ? cvtU8toS16(*(ibuf + 1)) : fl);
193 } else {
194 fl = *((const INT16 *)ibuf);
195 fr = (dsb->pwfx->nChannels==2 ? *(((const INT16 *)ibuf) + 1) : fl);
198 if (device->pwfx->nChannels == 2) {
199 if (device->pwfx->wBitsPerSample == 8) {
200 *obuf = cvtS16toU8(fl);
201 *(obuf + 1) = cvtS16toU8(fr);
202 return;
204 if (device->pwfx->wBitsPerSample == 16) {
205 *((INT16 *)obuf) = fl;
206 *(((INT16 *)obuf) + 1) = fr;
207 return;
210 if (device->pwfx->nChannels == 1) {
211 fl = (fl + fr) >> 1;
212 if (device->pwfx->wBitsPerSample == 8) {
213 *obuf = cvtS16toU8(fl);
214 return;
216 if (device->pwfx->wBitsPerSample == 16) {
217 *((INT16 *)obuf) = fl;
218 return;
224 * Mix at most the given amount of data into the given device buffer from the
225 * given secondary buffer, starting from the dsb's first currently unmixed
226 * frame (buf_mixpos), translating frequency (pitch), stereo/mono and
227 * bits-per-sample. The secondary buffer sample is looped if it is not
228 * long enough and it is a looping buffer.
229 * (Doesn't perform any mixing - this is a straight copy operation).
231 * Now with PerfectPitch (tm) technology
233 * dsb = the secondary buffer
234 * buf = the device buffer
235 * len = number of bytes to store in the device buffer
237 * Returns: the number of bytes read from the secondary buffer
238 * (ie. len, adjusted for frequency, number of channels and sample size,
239 * and limited by buffer length for non-looping buffers)
241 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
243 INT i, size, ipos, ilen;
244 BYTE *ibp, *obp;
245 INT iAdvance = dsb->pwfx->nBlockAlign;
246 INT oAdvance = dsb->device->pwfx->nBlockAlign;
248 ibp = dsb->buffer->memory + dsb->buf_mixpos;
249 obp = buf;
251 TRACE("(%p, %p, %p), buf_mixpos=%d\n", dsb, ibp, obp, dsb->buf_mixpos);
252 /* Check for the best case */
253 if ((dsb->freq == dsb->device->pwfx->nSamplesPerSec) &&
254 (dsb->pwfx->wBitsPerSample == dsb->device->pwfx->wBitsPerSample) &&
255 (dsb->pwfx->nChannels == dsb->device->pwfx->nChannels)) {
256 INT bytesleft = dsb->buflen - dsb->buf_mixpos;
257 TRACE("(%p) Best case\n", dsb);
258 if (len <= bytesleft )
259 CopyMemory(obp, ibp, len);
260 else { /* wrap */
261 CopyMemory(obp, ibp, bytesleft);
262 CopyMemory(obp + bytesleft, dsb->buffer->memory, len - bytesleft);
264 return len;
267 /* Check for same sample rate */
268 if (dsb->freq == dsb->device->pwfx->nSamplesPerSec) {
269 TRACE("(%p) Same sample rate %d = primary %d\n", dsb,
270 dsb->freq, dsb->device->pwfx->nSamplesPerSec);
271 ilen = 0;
272 for (i = 0; i < len; i += oAdvance) {
273 cp_fields(dsb, ibp, obp );
274 ibp += iAdvance;
275 ilen += iAdvance;
276 obp += oAdvance;
277 if (ibp >= (BYTE *)(dsb->buffer->memory + dsb->buflen))
278 ibp = dsb->buffer->memory; /* wrap */
280 return (ilen);
283 /* Mix in different sample rates */
284 /* */
285 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
286 /* Patent Pending :-] */
288 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
289 * TransGaming Technologies Inc. */
291 /* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
292 dsb, dsb->freq, dsb->device->pwfx->nSamplesPerSec); */
294 size = len / oAdvance;
295 ilen = 0;
296 ipos = dsb->buf_mixpos;
297 for (i = 0; i < size; i++) {
298 cp_fields(dsb, (dsb->buffer->memory + ipos), obp);
299 obp += oAdvance;
300 dsb->freqAcc += dsb->freqAdjust;
301 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
302 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
303 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
304 ipos += adv; ilen += adv;
305 ipos %= dsb->buflen;
308 return ilen;
311 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
313 INT i;
314 BYTE *bpc = buf;
315 INT16 *bps = (INT16 *) buf;
317 TRACE("(%p,%p,%d)\n",dsb,buf,len);
318 TRACE("left = %x, right = %x\n", dsb->cvolpan.dwTotalLeftAmpFactor,
319 dsb->cvolpan.dwTotalRightAmpFactor);
321 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->cvolpan.lPan == 0)) &&
322 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->cvolpan.lVolume == 0)) &&
323 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
324 return; /* Nothing to do */
326 /* If we end up with some bozo coder using panning or 3D sound */
327 /* with a mono primary buffer, it could sound very weird using */
328 /* this method. Oh well, tough patooties. */
330 switch (dsb->device->pwfx->wBitsPerSample) {
331 case 8:
332 /* 8-bit WAV is unsigned, but we need to operate */
333 /* on signed data for this to work properly */
334 switch (dsb->device->pwfx->nChannels) {
335 case 1:
336 for (i = 0; i < len; i++) {
337 INT val = *bpc - 128;
338 val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
339 *bpc = val + 128;
340 bpc++;
342 break;
343 case 2:
344 for (i = 0; i < len; i+=2) {
345 INT val = *bpc - 128;
346 val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
347 *bpc++ = val + 128;
348 val = *bpc - 128;
349 val = (val * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
350 *bpc = val + 128;
351 bpc++;
353 break;
354 default:
355 FIXME("doesn't support %d channels\n", dsb->device->pwfx->nChannels);
356 break;
358 break;
359 case 16:
360 /* 16-bit WAV is signed -- much better */
361 switch (dsb->device->pwfx->nChannels) {
362 case 1:
363 for (i = 0; i < len; i += 2) {
364 *bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
365 bps++;
367 break;
368 case 2:
369 for (i = 0; i < len; i += 4) {
370 *bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
371 bps++;
372 *bps = (*bps * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
373 bps++;
375 break;
376 default:
377 FIXME("doesn't support %d channels\n", dsb->device->pwfx->nChannels);
378 break;
380 break;
381 default:
382 FIXME("doesn't support %d bit samples\n", dsb->device->pwfx->wBitsPerSample);
383 break;
388 * Make sure the device's tmp_buffer is at least the given size. Return a
389 * pointer to it.
391 static LPBYTE DSOUND_tmpbuffer(DirectSoundDevice *device, DWORD len)
393 TRACE("(%p,%d)\n", device, len);
395 if (len > device->tmp_buffer_len) {
396 if (device->tmp_buffer)
397 device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, device->tmp_buffer, len);
398 else
399 device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, len);
401 device->tmp_buffer_len = len;
404 return device->tmp_buffer;
408 * Mix (at most) the given number of bytes into the given position of the
409 * device buffer, from the secondary buffer "dsb" (starting at the current
410 * mix position for that buffer).
412 * Returns the number of bytes actually mixed into the device buffer. This
413 * will match fraglen unless the end of the secondary buffer is reached
414 * (and it is not looping).
416 * dsb = the secondary buffer to mix from
417 * writepos = position (offset) in device buffer to write at
418 * fraglen = number of bytes to mix
420 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
422 INT i, len, ilen, field, todo;
423 BYTE *buf, *ibuf;
425 TRACE("(%p,%d,%d)\n",dsb,writepos,fraglen);
427 len = fraglen;
428 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
429 /* This buffer is not looping, so make sure the requested
430 * length will not take us past the end of the buffer */
431 int secondary_remainder = dsb->buflen - dsb->buf_mixpos;
432 int adjusted_remainder = MulDiv(dsb->device->pwfx->nAvgBytesPerSec, secondary_remainder, dsb->nAvgBytesPerSec);
433 assert(adjusted_remainder >= 0);
434 /* The adjusted remainder must be at least one sample,
435 * otherwise we will never reach the end of the
436 * secondary buffer, as there will perpetually be a
437 * fractional remainder */
438 TRACE("secondary_remainder = %d, adjusted_remainder = %d, len = %d\n", secondary_remainder, adjusted_remainder, len);
439 if (adjusted_remainder < len) {
440 TRACE("clipping len to remainder of secondary buffer\n");
441 len = adjusted_remainder;
443 if (len == 0)
444 return 0;
447 if (len % dsb->device->pwfx->nBlockAlign) {
448 INT nBlockAlign = dsb->device->pwfx->nBlockAlign;
449 ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign);
450 len = (len / nBlockAlign) * nBlockAlign; /* data alignment */
453 if ((buf = ibuf = DSOUND_tmpbuffer(dsb->device, len)) == NULL)
454 return 0;
456 TRACE("MixInBuffer (%p) len = %d, dest = %d\n", dsb, len, writepos);
458 /* first, copy the data from the DirectSoundBuffer into the temporary
459 buffer, translating frequency/bits-per-sample/number-of-channels
460 to match the device settings */
461 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
462 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
463 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
464 (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
465 DSOUND_MixerVol(dsb, ibuf, len);
467 /* Now mix the temporary buffer into the devices main buffer */
468 if (dsb->device->pwfx->wBitsPerSample == 8) {
469 BYTE *obuf = dsb->device->buffer + writepos;
471 if ((writepos + len) <= dsb->device->buflen)
472 todo = len;
473 else
474 todo = dsb->device->buflen - writepos;
476 for (i = 0; i < todo; i++) {
477 /* 8-bit WAV is unsigned */
478 field = (*ibuf++ - 128);
479 field += (*obuf - 128);
480 if (field > 127) field = 127;
481 else if (field < -128) field = -128;
482 *obuf++ = field + 128;
485 if (todo < len) {
486 todo = len - todo;
487 obuf = dsb->device->buffer;
489 for (i = 0; i < todo; i++) {
490 /* 8-bit WAV is unsigned */
491 field = (*ibuf++ - 128);
492 field += (*obuf - 128);
493 if (field > 127) field = 127;
494 else if (field < -128) field = -128;
495 *obuf++ = field + 128;
498 } else {
499 INT16 *ibufs, *obufs;
501 ibufs = (INT16 *) ibuf;
502 obufs = (INT16 *)(dsb->device->buffer + writepos);
504 if ((writepos + len) <= dsb->device->buflen)
505 todo = len / 2;
506 else
507 todo = (dsb->device->buflen - writepos) / 2;
509 for (i = 0; i < todo; i++) {
510 /* 16-bit WAV is signed */
511 field = *ibufs++;
512 field += *obufs;
513 if (field > 32767) field = 32767;
514 else if (field < -32768) field = -32768;
515 *obufs++ = field;
518 if (todo < (len / 2)) {
519 todo = (len / 2) - todo;
520 obufs = (INT16 *)dsb->device->buffer;
522 for (i = 0; i < todo; i++) {
523 /* 16-bit WAV is signed */
524 field = *ibufs++;
525 field += *obufs;
526 if (field > 32767) field = 32767;
527 else if (field < -32768) field = -32768;
528 *obufs++ = field;
533 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
534 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
535 * not the MIX position... but if the sound buffer is bigger than our prebuffering
536 * (which must be the case for the streaming buffers that need this hack anyway)
537 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
538 dsb->leadin = FALSE;
541 dsb->buf_mixpos += ilen;
543 if (dsb->buf_mixpos >= dsb->buflen) {
544 if (dsb->playflags & DSBPLAY_LOOPING) {
545 /* wrap */
546 dsb->buf_mixpos %= dsb->buflen;
547 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
548 dsb->leadin = FALSE; /* HACK: see above */
549 } else if (dsb->buf_mixpos > dsb->buflen) {
550 ERR("Mixpos (%u) past buflen (%u), capping...\n", dsb->buf_mixpos, dsb->buflen);
551 dsb->buf_mixpos = dsb->buflen;
555 return len;
558 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
560 INT ilen, field;
561 UINT i, todo;
562 BYTE *buf, *ibuf;
564 TRACE("(%p,%d,%d)\n",dsb,writepos,len);
566 if (len % dsb->device->pwfx->nBlockAlign) {
567 INT nBlockAlign = dsb->device->pwfx->nBlockAlign;
568 ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign);
569 len = (len / nBlockAlign) * nBlockAlign; /* data alignment */
572 if ((buf = ibuf = DSOUND_tmpbuffer(dsb->device, len)) == NULL)
573 return;
575 TRACE("PhaseCancel (%p) len = %d, dest = %d\n", dsb, len, writepos);
577 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
578 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
579 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
580 (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
581 DSOUND_MixerVol(dsb, ibuf, len);
583 /* subtract instead of add, to phase out premixed data */
584 if (dsb->device->pwfx->wBitsPerSample == 8) {
585 BYTE *obuf = dsb->device->buffer + writepos;
587 if ((writepos + len) <= dsb->device->buflen)
588 todo = len;
589 else
590 todo = dsb->device->buflen - writepos;
592 for (i = 0; i < todo; i++) {
593 /* 8-bit WAV is unsigned */
594 field = (*obuf - 128);
595 field -= (*ibuf++ - 128);
596 if (field > 127) field = 127;
597 else if (field < -128) field = -128;
598 *obuf++ = field + 128;
601 if (todo < len) {
602 todo = len - todo;
603 obuf = dsb->device->buffer;
605 for (i = 0; i < todo; i++) {
606 /* 8-bit WAV is unsigned */
607 field = (*obuf - 128);
608 field -= (*ibuf++ - 128);
609 if (field > 127) field = 127;
610 else if (field < -128) field = -128;
611 *obuf++ = field + 128;
614 } else {
615 INT16 *ibufs, *obufs;
617 ibufs = (INT16 *) ibuf;
618 obufs = (INT16 *)(dsb->device->buffer + writepos);
620 if ((writepos + len) <= dsb->device->buflen)
621 todo = len / 2;
622 else
623 todo = (dsb->device->buflen - writepos) / 2;
625 for (i = 0; i < todo; i++) {
626 /* 16-bit WAV is signed */
627 field = *obufs;
628 field -= *ibufs++;
629 if (field > 32767) field = 32767;
630 else if (field < -32768) field = -32768;
631 *obufs++ = field;
634 if (todo < (len / 2)) {
635 todo = (len / 2) - todo;
636 obufs = (INT16 *)dsb->device->buffer;
638 for (i = 0; i < todo; i++) {
639 /* 16-bit WAV is signed */
640 field = *obufs;
641 field -= *ibufs++;
642 if (field > 32767) field = 32767;
643 else if (field < -32768) field = -32768;
644 *obufs++ = field;
650 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
652 DWORD size, flen, len, npos, nlen;
653 INT iAdvance = dsb->pwfx->nBlockAlign;
654 INT oAdvance = dsb->device->pwfx->nBlockAlign;
655 /* determine amount of premixed data to cancel */
656 DWORD primary_done =
657 ((dsb->primary_mixpos < writepos) ? dsb->device->buflen : 0) +
658 dsb->primary_mixpos - writepos;
660 TRACE("(%p, %d), buf_mixpos=%d\n", dsb, writepos, dsb->buf_mixpos);
662 /* backtrack the mix position */
663 size = primary_done / oAdvance;
664 flen = size * dsb->freqAdjust;
665 len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
666 flen &= (1<<DSOUND_FREQSHIFT)-1;
667 while (dsb->freqAcc < flen) {
668 len += iAdvance;
669 dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
671 len %= dsb->buflen;
672 npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
673 dsb->buf_mixpos - len;
674 if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
675 /* stop backtracking at startpos */
676 npos = dsb->startpos;
677 len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
678 dsb->buf_mixpos - npos;
679 flen = dsb->freqAcc;
680 nlen = len / dsb->pwfx->nBlockAlign;
681 nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
682 nlen *= dsb->device->pwfx->nBlockAlign;
683 writepos =
684 ((dsb->primary_mixpos < nlen) ? dsb->device->buflen : 0) +
685 dsb->primary_mixpos - nlen;
688 dsb->freqAcc -= flen;
689 dsb->buf_mixpos = npos;
690 dsb->primary_mixpos = writepos;
692 TRACE("new buf_mixpos=%d, primary_mixpos=%d (len=%d)\n",
693 dsb->buf_mixpos, dsb->primary_mixpos, len);
695 if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
698 void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
700 #if 0
701 DWORD i, size, flen, len, npos, nlen;
702 INT iAdvance = dsb->pwfx->nBlockAlign;
703 INT oAdvance = dsb->device->pwfx->nBlockAlign;
704 /* determine amount of premixed data to cancel */
705 DWORD buf_done =
706 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
707 dsb->buf_mixpos - buf_writepos;
708 #endif
710 WARN("(%p, %d), buf_mixpos=%d\n", dsb, buf_writepos, dsb->buf_mixpos);
711 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
712 * (which is faster anyway when there's only a single secondary buffer) */
713 dsb->device->need_remix = TRUE;
716 void DSOUND_ForceRemix(IDirectSoundBufferImpl *dsb)
718 TRACE("(%p)\n",dsb);
719 EnterCriticalSection(&dsb->lock);
720 if (dsb->state == STATE_PLAYING)
721 dsb->device->need_remix = TRUE;
722 LeaveCriticalSection(&dsb->lock);
726 * Calculate the distance between two buffer offsets, taking wraparound
727 * into account.
729 static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2)
731 if (ptr1 >= ptr2) {
732 return ptr1 - ptr2;
733 } else {
734 return buflen + ptr1 - ptr2;
739 * Mix some frames from the given secondary buffer "dsb" into the device
740 * primary buffer.
742 * dsb = the secondary buffer
743 * playpos = the current play position in the device buffer (primary buffer)
744 * writepos = the current safe-to-write position in the device buffer
745 * mixlen = the maximum number of bytes in the primary buffer to mix, from the
746 * current writepos.
748 * Returns: the number of bytes beyond the writepos that were mixed.
750 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
752 /* The buffer's primary_mixpos may be before or after the the device
753 * buffer's mixpos, but both must be ahead of writepos. */
755 DWORD len, slen;
756 /* determine this buffer's write position */
757 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, writepos, writepos);
758 /* determine how much already-mixed data exists */
759 DWORD buf_done = DSOUND_BufPtrDiff(dsb->buflen, dsb->buf_mixpos, buf_writepos);
760 DWORD primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos);
761 DWORD adv_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->device->mixpos, writepos);
762 DWORD played = DSOUND_BufPtrDiff(dsb->buflen, buf_writepos, dsb->playpos);
763 DWORD buf_left = dsb->buflen - buf_writepos;
764 int still_behind;
766 TRACE("(%p,%d,%d,%d)\n",dsb,playpos,writepos,mixlen);
767 TRACE("buf_writepos=%d, primary_writepos=%d\n", buf_writepos, writepos);
768 TRACE("buf_done=%d, primary_done=%d\n", buf_done, primary_done);
769 TRACE("buf_mixpos=%d, primary_mixpos=%d, mixlen=%d\n", dsb->buf_mixpos, dsb->primary_mixpos,
770 mixlen);
771 TRACE("looping=%d, startpos=%d, leadin=%d\n", dsb->playflags, dsb->startpos, dsb->leadin);
773 /* check for notification positions */
774 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&
775 dsb->state != STATE_STARTING) {
776 DSOUND_CheckEvent(dsb, played);
779 /* save write position for non-GETCURRENTPOSITION2... */
780 dsb->playpos = buf_writepos;
782 /* check whether CalcPlayPosition detected a mixing underrun */
783 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
784 /* it did, but did we have more to play? */
785 if ((dsb->playflags & DSBPLAY_LOOPING) ||
786 (dsb->buf_mixpos < dsb->buflen)) {
787 /* yes, have to recover */
788 ERR("underrun on sound buffer %p\n", dsb);
789 TRACE("recovering from underrun: primary_mixpos=%d\n", writepos);
791 dsb->primary_mixpos = writepos;
792 primary_done = 0;
794 /* determine how far ahead we should mix */
795 if (((dsb->playflags & DSBPLAY_LOOPING) ||
796 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
797 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
798 /* if this is a streaming buffer, it typically means that
799 * we should defer mixing past probably_valid_to as long
800 * as we can, to avoid unnecessary remixing */
801 /* the heavy-looking calculations shouldn't be that bad,
802 * as any game isn't likely to be have more than 1 or 2
803 * streaming buffers in use at any time anyway... */
804 DWORD probably_valid_left =
805 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
806 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
807 dsb->probably_valid_to - buf_writepos;
808 /* check for leadin condition */
809 if ((probably_valid_left == 0) &&
810 (dsb->probably_valid_to == dsb->startpos) &&
811 dsb->leadin)
812 probably_valid_left = dsb->buflen;
813 TRACE("streaming buffer probably_valid_to=%d, probably_valid_left=%d\n",
814 dsb->probably_valid_to, probably_valid_left);
815 /* check whether the app's time is already up */
816 if (probably_valid_left < dsb->writelead) {
817 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
818 /* once we pass the point of no return,
819 * no reason to hold back anymore */
820 dsb->probably_valid_to = (DWORD)-1;
821 /* we just have to go ahead and mix what we have,
822 * there's no telling what the app is thinking anyway */
823 } else {
824 /* adjust for our frequency and our sample size */
825 probably_valid_left = MulDiv(probably_valid_left,
826 1 << DSOUND_FREQSHIFT,
827 dsb->pwfx->nBlockAlign * dsb->freqAdjust) *
828 dsb->device->pwfx->nBlockAlign;
829 /* check whether to clip mix_len */
830 if (probably_valid_left < mixlen) {
831 TRACE("clipping to probably_valid_left=%d\n", probably_valid_left);
832 mixlen = probably_valid_left;
836 /* cut mixlen with what's already been mixed */
837 if (mixlen < primary_done) {
838 /* huh? and still CalcPlayPosition didn't
839 * detect an underrun? */
840 FIXME("problem with underrun detection (mixlen=%d < primary_done=%d)\n", mixlen, primary_done);
841 return 0;
843 len = mixlen - primary_done;
844 TRACE("remaining mixlen=%d\n", len);
846 if (len < dsb->device->fraglen) {
847 /* smaller than a fragment, wait until it gets larger
848 * before we take the mixing overhead */
849 TRACE("mixlen not worth it, deferring mixing\n");
850 still_behind = 1;
851 goto post_mix;
854 /* ok, we know how much to mix, let's go */
855 still_behind = (adv_done > primary_done);
856 while (len) {
857 slen = dsb->device->buflen - dsb->primary_mixpos;
858 if (slen > len) slen = len;
859 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
861 if ((dsb->primary_mixpos < dsb->device->mixpos) &&
862 (dsb->primary_mixpos + slen >= dsb->device->mixpos))
863 still_behind = FALSE;
865 dsb->primary_mixpos += slen; len -= slen;
866 dsb->primary_mixpos %= dsb->device->buflen;
868 if ((dsb->state == STATE_STOPPED) || !slen) break;
870 TRACE("new primary_mixpos=%d, primary_advbase=%d\n", dsb->primary_mixpos, dsb->device->mixpos);
871 TRACE("mixed data len=%d, still_behind=%d\n", mixlen-len, still_behind);
873 post_mix:
874 /* check if buffer should be considered complete */
875 if (buf_left < dsb->writelead &&
876 !(dsb->playflags & DSBPLAY_LOOPING)) {
877 dsb->state = STATE_STOPPED;
878 dsb->playpos = 0;
879 dsb->last_playpos = 0;
880 dsb->buf_mixpos = 0;
881 dsb->leadin = FALSE;
882 dsb->need_remix = FALSE;
883 DSOUND_CheckEvent(dsb, buf_left);
886 /* return how far we think the primary buffer can
887 * advance its underrun detector...*/
888 if (still_behind) return 0;
889 if ((mixlen - len) < primary_done) return 0;
890 slen = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, dsb->device->mixpos);
891 if (slen > mixlen) {
892 /* the primary_done and still_behind checks above should have worked */
893 FIXME("problem with advancement calculation (advlen=%d > mixlen=%d)\n", slen, mixlen);
894 slen = 0;
896 return slen;
900 * For a DirectSoundDevice, go through all the currently playing buffers and
901 * mix them in to the device buffer.
903 * playpos = the current play position in the primary buffer
904 * writepos = the current safe-to-write position in the primary buffer
905 * mixlen = the maximum amount to mix into the primary buffer
906 * (beyond the current writepos)
907 * recover = true if the sound device may have been reset and the write
908 * position in the device buffer changed
910 * Returns: the length beyond the writepos that was mixed to.
912 static DWORD DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD playpos, DWORD writepos,
913 DWORD mixlen, BOOL recover)
915 INT i, len, maxlen = 0;
916 IDirectSoundBufferImpl *dsb;
918 TRACE("(%d,%d,%d,%d)\n", playpos, writepos, mixlen, recover);
919 for (i = 0; i < device->nrofbuffers; i++) {
920 dsb = device->buffers[i];
922 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
923 TRACE("Checking %p, mixlen=%d\n", dsb, mixlen);
924 EnterCriticalSection(&(dsb->lock));
925 if (dsb->state == STATE_STOPPING) {
926 DSOUND_MixCancel(dsb, writepos, TRUE);
927 dsb->state = STATE_STOPPED;
928 DSOUND_CheckEvent(dsb, 0);
929 } else {
930 if ((dsb->state == STATE_STARTING) || recover) {
931 dsb->primary_mixpos = writepos;
932 dsb->cvolpan = dsb->volpan;
933 dsb->need_remix = FALSE;
935 else if (dsb->need_remix) {
936 DSOUND_MixCancel(dsb, writepos, TRUE);
937 dsb->cvolpan = dsb->volpan;
938 dsb->need_remix = FALSE;
940 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
941 if (dsb->state == STATE_STARTING)
942 dsb->state = STATE_PLAYING;
943 maxlen = (len > maxlen) ? len : maxlen;
945 LeaveCriticalSection(&(dsb->lock));
949 return maxlen;
952 static void DSOUND_MixReset(DirectSoundDevice *device, DWORD writepos)
954 INT i;
955 IDirectSoundBufferImpl *dsb;
956 int nfiller;
958 TRACE("(%p,%d)\n", device, writepos);
960 /* the sound of silence */
961 nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
963 /* reset all buffer mix positions */
964 for (i = 0; i < device->nrofbuffers; i++) {
965 dsb = device->buffers[i];
967 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
968 TRACE("Resetting %p\n", dsb);
969 EnterCriticalSection(&(dsb->lock));
970 if (dsb->state == STATE_STOPPING) {
971 dsb->state = STATE_STOPPED;
973 else if (dsb->state == STATE_STARTING) {
974 /* nothing */
975 } else {
976 DSOUND_MixCancel(dsb, writepos, FALSE);
977 dsb->cvolpan = dsb->volpan;
978 dsb->need_remix = FALSE;
980 LeaveCriticalSection(&(dsb->lock));
984 /* wipe out premixed data */
985 if (device->mixpos < writepos) {
986 FillMemory(device->buffer + writepos, device->buflen - writepos, nfiller);
987 FillMemory(device->buffer, device->mixpos, nfiller);
988 } else {
989 FillMemory(device->buffer + writepos, device->mixpos - writepos, nfiller);
992 /* reset primary mix position */
993 device->mixpos = writepos;
996 static void DSOUND_CheckReset(DirectSoundDevice *device, DWORD writepos)
998 TRACE("(%p,%d)\n",device,writepos);
999 if (device->need_remix) {
1000 DSOUND_MixReset(device, writepos);
1001 device->need_remix = FALSE;
1002 /* maximize Half-Life performance */
1003 device->prebuf = ds_snd_queue_min;
1004 device->precount = 0;
1005 } else {
1006 device->precount++;
1007 if (device->precount >= 4) {
1008 if (device->prebuf < ds_snd_queue_max)
1009 device->prebuf++;
1010 device->precount = 0;
1013 TRACE("premix adjust: %d\n", device->prebuf);
1016 void DSOUND_WaveQueue(DirectSoundDevice *device, DWORD mixq)
1018 TRACE("(%p,%d)\n", device, mixq);
1019 if (mixq + device->pwqueue > ds_hel_queue) mixq = ds_hel_queue - device->pwqueue;
1020 TRACE("queueing %d buffers, starting at %d\n", mixq, device->pwwrite);
1021 for (; mixq; mixq--) {
1022 waveOutWrite(device->hwo, device->pwave[device->pwwrite], sizeof(WAVEHDR));
1023 device->pwwrite++;
1024 if (device->pwwrite >= DS_HEL_FRAGS) device->pwwrite = 0;
1025 device->pwqueue++;
1029 /* #define SYNC_CALLBACK */
1032 * Perform mixing for a Direct Sound device. That is, go through all the
1033 * secondary buffers (the sound bites currently playing) and mix them in
1034 * to the primary buffer (the device buffer).
1036 static void DSOUND_PerformMix(DirectSoundDevice *device)
1038 int nfiller;
1039 BOOL forced;
1040 HRESULT hres;
1042 TRACE("(%p)\n", device);
1044 /* the sound of silence */
1045 nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
1047 /* whether the primary is forced to play even without secondary buffers */
1048 forced = ((device->state == STATE_PLAYING) || (device->state == STATE_STARTING));
1050 if (device->priolevel != DSSCL_WRITEPRIMARY) {
1051 BOOL paused = ((device->state == STATE_STOPPED) || (device->state == STATE_STARTING));
1052 /* FIXME: document variables */
1053 DWORD playpos, writepos, inq, maxq, frag;
1054 if (device->hwbuf) {
1055 hres = IDsDriverBuffer_GetPosition(device->hwbuf, &playpos, &writepos);
1056 if (hres) {
1057 WARN("IDsDriverBuffer_GetPosition failed\n");
1058 return;
1060 /* Well, we *could* do Just-In-Time mixing using the writepos,
1061 * but that's a little bit ambitious and unnecessary... */
1062 /* rather add our safety margin to the writepos, if we're playing */
1063 if (!paused) {
1064 writepos += device->writelead;
1065 writepos %= device->buflen;
1066 } else writepos = playpos;
1067 } else {
1068 playpos = device->pwplay * device->fraglen;
1069 writepos = playpos;
1070 if (!paused) {
1071 writepos += ds_hel_margin * device->fraglen;
1072 writepos %= device->buflen;
1075 TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n",
1076 playpos,writepos,device->playpos,device->mixpos,device->buflen);
1077 assert(device->playpos < device->buflen);
1078 /* wipe out just-played sound data */
1079 if (playpos < device->playpos) {
1080 FillMemory(device->buffer + device->playpos, device->buflen - device->playpos, nfiller);
1081 FillMemory(device->buffer, playpos, nfiller);
1082 } else {
1083 FillMemory(device->buffer + device->playpos, playpos - device->playpos, nfiller);
1085 device->playpos = playpos;
1087 EnterCriticalSection(&(device->mixlock));
1089 /* reset mixing if necessary */
1090 DSOUND_CheckReset(device, writepos);
1092 /* check how much prebuffering is left */
1093 inq = DSOUND_BufPtrDiff(device->buflen, device->mixpos, writepos);
1095 /* find the maximum we can prebuffer */
1096 if (!paused)
1097 maxq = DSOUND_BufPtrDiff(device->buflen, playpos, writepos);
1098 /* If we get the whole buffer, difference is 0, so we need to set whole buffer then */
1099 if (paused || !maxq)
1100 maxq = device->buflen;
1102 /* clip maxq to device->prebuf */
1103 frag = device->prebuf * device->fraglen;
1104 if (maxq > frag)
1105 maxq = frag;
1107 /* check for consistency */
1108 if (inq > maxq) {
1109 /* the playback position must have passed our last
1110 * mixed position, i.e. it's an underrun, or we have
1111 * nothing more to play */
1112 TRACE("reached end of mixed data (inq=%d, maxq=%d)\n", inq, maxq);
1113 inq = 0;
1114 /* stop the playback now, to allow buffers to refill */
1115 if (device->state == STATE_PLAYING) {
1116 device->state = STATE_STARTING;
1118 else if (device->state == STATE_STOPPING) {
1119 device->state = STATE_STOPPED;
1121 else {
1122 /* how can we have an underrun if we aren't playing? */
1123 WARN("unexpected primary state (%d)\n", device->state);
1125 #ifdef SYNC_CALLBACK
1126 /* DSOUND_callback may need this lock */
1127 LeaveCriticalSection(&(device->mixlock));
1128 #endif
1129 if (DSOUND_PrimaryStop(device) != DS_OK)
1130 WARN("DSOUND_PrimaryStop failed\n");
1131 #ifdef SYNC_CALLBACK
1132 EnterCriticalSection(&(device->mixlock));
1133 #endif
1134 if (device->hwbuf) {
1135 /* the Stop is supposed to reset play position to beginning of buffer */
1136 /* unfortunately, OSS is not able to do so, so get current pointer */
1137 hres = IDsDriverBuffer_GetPosition(device->hwbuf, &playpos, NULL);
1138 if (hres) {
1139 LeaveCriticalSection(&(device->mixlock));
1140 WARN("IDsDriverBuffer_GetPosition failed\n");
1141 return;
1143 } else {
1144 playpos = device->pwplay * device->fraglen;
1146 writepos = playpos;
1147 device->playpos = playpos;
1148 device->mixpos = writepos;
1149 inq = 0;
1150 maxq = device->buflen;
1151 if (maxq > frag) maxq = frag;
1152 FillMemory(device->buffer, device->buflen, nfiller);
1153 paused = TRUE;
1156 /* do the mixing */
1157 frag = DSOUND_MixToPrimary(device, playpos, writepos, maxq, paused);
1158 if (forced) frag = maxq - inq;
1159 device->mixpos += frag;
1160 device->mixpos %= device->buflen;
1162 if (frag) {
1163 /* buffers have been filled, restart playback */
1164 if (device->state == STATE_STARTING) {
1165 device->state = STATE_PLAYING;
1167 else if (device->state == STATE_STOPPED) {
1168 /* the dsound is supposed to play if there's something to play
1169 * even if it is reported as stopped, so don't let this confuse you */
1170 device->state = STATE_STOPPING;
1172 LeaveCriticalSection(&(device->mixlock));
1173 if (paused) {
1174 if (DSOUND_PrimaryPlay(device) != DS_OK)
1175 WARN("DSOUND_PrimaryPlay failed\n");
1176 else
1177 TRACE("starting playback\n");
1180 else
1181 LeaveCriticalSection(&(device->mixlock));
1182 } else {
1183 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
1184 if (device->state == STATE_STARTING) {
1185 if (DSOUND_PrimaryPlay(device) != DS_OK)
1186 WARN("DSOUND_PrimaryPlay failed\n");
1187 else
1188 device->state = STATE_PLAYING;
1190 else if (device->state == STATE_STOPPING) {
1191 if (DSOUND_PrimaryStop(device) != DS_OK)
1192 WARN("DSOUND_PrimaryStop failed\n");
1193 else
1194 device->state = STATE_STOPPED;
1199 void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
1200 DWORD_PTR dw1, DWORD_PTR dw2)
1202 DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
1203 DWORD start_time = GetTickCount();
1204 DWORD end_time;
1205 TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
1206 TRACE("entering at %d\n", start_time);
1208 if (DSOUND_renderer[device->drvdesc.dnDevNode] != device) {
1209 ERR("dsound died without killing us?\n");
1210 timeKillEvent(timerID);
1211 timeEndPeriod(DS_TIME_RES);
1212 return;
1215 RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
1217 if (device->ref)
1218 DSOUND_PerformMix(device);
1220 RtlReleaseResource(&(device->buffer_list_lock));
1222 end_time = GetTickCount();
1223 TRACE("completed processing at %d, duration = %d\n", end_time, end_time - start_time);
1226 void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
1228 DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
1229 TRACE("(%p,%x,%x,%x,%x)\n",hwo,msg,dwUser,dw1,dw2);
1230 TRACE("entering at %d, msg=%08x(%s)\n", GetTickCount(), msg,
1231 msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" :
1232 msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN");
1233 if (msg == MM_WOM_DONE) {
1234 DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
1235 if (device->pwqueue == (DWORD)-1) {
1236 TRACE("completed due to reset\n");
1237 return;
1239 /* it could be a bad idea to enter critical section here... if there's lock contention,
1240 * the resulting scheduling delays might obstruct the winmm player thread */
1241 #ifdef SYNC_CALLBACK
1242 EnterCriticalSection(&(device->mixlock));
1243 #endif
1244 /* retrieve current values */
1245 fraglen = device->fraglen;
1246 buflen = device->buflen;
1247 pwplay = device->pwplay;
1248 playpos = pwplay * fraglen;
1249 mixpos = device->mixpos;
1250 /* check remaining mixed data */
1251 inq = DSOUND_BufPtrDiff(buflen, mixpos, playpos);
1252 mixq = inq / fraglen;
1253 if ((inq - (mixq * fraglen)) > 0) mixq++;
1254 /* complete the playing buffer */
1255 TRACE("done playing primary pos=%d\n", playpos);
1256 pwplay++;
1257 if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
1258 /* write new values */
1259 device->pwplay = pwplay;
1260 device->pwqueue--;
1261 /* queue new buffer if we have data for it */
1262 if (inq>1) DSOUND_WaveQueue(device, inq-1);
1263 #ifdef SYNC_CALLBACK
1264 LeaveCriticalSection(&(device->mixlock));
1265 #endif
1267 TRACE("completed\n");