Prevent crash when no URL is specified.
[wine] / dlls / dsound / mixer.c
1 /*                      DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <math.h>       /* Insomnia - pow() function */
25
26 #define NONAMELESSSTRUCT
27 #define NONAMELESSUNION
28 #include "windef.h"
29 #include "winbase.h"
30 #include "mmsystem.h"
31 #include "winreg.h"
32 #include "winternl.h"
33 #include "wine/debug.h"
34 #include "dsound.h"
35 #include "dsdriver.h"
36 #include "dsound_private.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
39
40 void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
41 {
42         double temp;
43         TRACE("(%p)\n",volpan);
44
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 */
49
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);
55
56         TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
57 }
58
59 void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan)
60 {
61     double left,right;
62     TRACE("(%p)\n",volpan);
63
64     TRACE("left=%lx, right=%lx\n",volpan->dwTotalLeftAmpFactor,volpan->dwTotalRightAmpFactor);
65     if (volpan->dwTotalLeftAmpFactor==0)
66         left=-10000;
67     else
68         left=600 * log(((double)volpan->dwTotalLeftAmpFactor) / 0xffff) / log(2);
69     if (volpan->dwTotalRightAmpFactor==0)
70         right=-10000;
71     else
72         right=600 * log(((double)volpan->dwTotalRightAmpFactor) / 0xffff) / log(2);
73     if (left<right)
74     {
75         volpan->lVolume=right;
76         volpan->dwVolAmpFactor=volpan->dwTotalRightAmpFactor;
77     }
78     else
79     {
80         volpan->lVolume=left;
81         volpan->dwVolAmpFactor=volpan->dwTotalLeftAmpFactor;
82     }
83     if (volpan->lVolume < -10000)
84         volpan->lVolume=-10000;
85     volpan->lPan=right-left;
86     if (volpan->lPan < -10000)
87         volpan->lPan=-10000;
88
89     TRACE("Vol=%ld Pan=%ld\n", volpan->lVolume, volpan->lPan);
90 }
91
92 void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
93 {
94         TRACE("(%p)\n",dsb);
95
96         /* calculate the 10ms write lead */
97         dsb->writelead = (dsb->freq / 100) * dsb->pwfx->nBlockAlign;
98 }
99
100 void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
101 {
102         int                     i;
103         DWORD                   offset;
104         LPDSBPOSITIONNOTIFY     event;
105         TRACE("(%p,%d)\n",dsb,len);
106
107         if (dsb->nrofnotifies == 0)
108                 return;
109
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] */
119                 /*  */
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);
126                                 return;
127                         } else
128                                 return;
129                 }
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);
135                         }
136                 } else {
137                         if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
138                                 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
139                                 SetEvent(event->hEventNotify);
140                         }
141                 }
142         }
143 }
144
145 /* WAV format info can be found at:
146  *
147  *    http://www.cwi.nl/ftp/audio/AudioFormats.part2
148  *    ftp://ftp.cwi.nl/pub/audio/RIFF-format
149  *
150  * Import points to remember:
151  *    8-bit WAV is unsigned
152  *    16-bit WAV is signed
153  */
154  /* Use the same formulas as pcmconverter.c */
155 static inline INT16 cvtU8toS16(BYTE b)
156 {
157     return (short)((b+(b << 8))-32768);
158 }
159
160 static inline BYTE cvtS16toU8(INT16 s)
161 {
162     return (s >> 8) ^ (unsigned char)0x80;
163 }
164
165 static inline void cp_fields(const IDirectSoundBufferImpl *dsb, BYTE *ibuf, BYTE *obuf )
166 {
167         INT fl,fr;
168
169         if (dsb->pwfx->wBitsPerSample == 8)  {
170                 if (dsb->dsound->pwfx->wBitsPerSample == 8 &&
171                     dsb->dsound->pwfx->nChannels == dsb->pwfx->nChannels) {
172                         /* avoid needless 8->16->8 conversion */
173                         *obuf=*ibuf;
174                         if (dsb->pwfx->nChannels==2)
175                                 *(obuf+1)=*(ibuf+1);
176                         return;
177                 }
178                 fl = cvtU8toS16(*ibuf);
179                 fr = (dsb->pwfx->nChannels==2 ? cvtU8toS16(*(ibuf + 1)) : fl);
180         } else {
181                 fl = *((INT16 *)ibuf);
182                 fr = (dsb->pwfx->nChannels==2 ? *(((INT16 *)ibuf) + 1)  : fl);
183         }
184
185         if (dsb->dsound->pwfx->nChannels == 2) {
186                 if (dsb->dsound->pwfx->wBitsPerSample == 8) {
187                         *obuf = cvtS16toU8(fl);
188                         *(obuf + 1) = cvtS16toU8(fr);
189                         return;
190                 }
191                 if (dsb->dsound->pwfx->wBitsPerSample == 16) {
192                         *((INT16 *)obuf) = fl;
193                         *(((INT16 *)obuf) + 1) = fr;
194                         return;
195                 }
196         }
197         if (dsb->dsound->pwfx->nChannels == 1) {
198                 fl = (fl + fr) >> 1;
199                 if (dsb->dsound->pwfx->wBitsPerSample == 8) {
200                         *obuf = cvtS16toU8(fl);
201                         return;
202                 }
203                 if (dsb->dsound->pwfx->wBitsPerSample == 16) {
204                         *((INT16 *)obuf) = fl;
205                         return;
206                 }
207         }
208 }
209
210 /* Now with PerfectPitch (tm) technology */
211 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
212 {
213         INT     i, size, ipos, ilen;
214         BYTE    *ibp, *obp;
215         INT     iAdvance = dsb->pwfx->nBlockAlign;
216         INT     oAdvance = dsb->dsound->pwfx->nBlockAlign;
217
218         ibp = dsb->buffer->memory + dsb->buf_mixpos;
219         obp = buf;
220
221         TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
222         /* Check for the best case */
223         if ((dsb->freq == dsb->dsound->pwfx->nSamplesPerSec) &&
224             (dsb->pwfx->wBitsPerSample == dsb->dsound->pwfx->wBitsPerSample) &&
225             (dsb->pwfx->nChannels == dsb->dsound->pwfx->nChannels)) {
226                 INT bytesleft = dsb->buflen - dsb->buf_mixpos;
227                 TRACE("(%p) Best case\n", dsb);
228                 if (len <= bytesleft )
229                         CopyMemory(obp, ibp, len);
230                 else { /* wrap */
231                         CopyMemory(obp, ibp, bytesleft);
232                         CopyMemory(obp + bytesleft, dsb->buffer->memory, len - bytesleft);
233                 }
234                 return len;
235         }
236
237         /* Check for same sample rate */
238         if (dsb->freq == dsb->dsound->pwfx->nSamplesPerSec) {
239                 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
240                         dsb->freq, dsb->dsound->pwfx->nSamplesPerSec);
241                 ilen = 0;
242                 for (i = 0; i < len; i += oAdvance) {
243                         cp_fields(dsb, ibp, obp );
244                         ibp += iAdvance;
245                         ilen += iAdvance;
246                         obp += oAdvance;
247                         if (ibp >= (BYTE *)(dsb->buffer->memory + dsb->buflen))
248                                 ibp = dsb->buffer->memory;      /* wrap */
249                 }
250                 return (ilen);
251         }
252
253         /* Mix in different sample rates */
254         /* */
255         /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
256         /* Patent Pending :-] */
257
258         /* Patent enhancements (c) 2000 Ove KÃ¥ven,
259          * TransGaming Technologies Inc. */
260
261         /* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
262            dsb, dsb->freq, dsb->dsound->pwfx->nSamplesPerSec); */
263
264         size = len / oAdvance;
265         ilen = 0;
266         ipos = dsb->buf_mixpos;
267         for (i = 0; i < size; i++) {
268                 cp_fields(dsb, (dsb->buffer->memory + ipos), obp);
269                 obp += oAdvance;
270                 dsb->freqAcc += dsb->freqAdjust;
271                 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
272                         ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
273                         dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
274                         ipos += adv; ilen += adv;
275                         ipos %= dsb->buflen;
276                 }
277         }
278         return ilen;
279 }
280
281 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
282 {
283         INT     i;
284         BYTE    *bpc = buf;
285         INT16   *bps = (INT16 *) buf;
286
287         TRACE("(%p,%p,%d)\n",dsb,buf,len);
288         TRACE("left = %lx, right = %lx\n", dsb->cvolpan.dwTotalLeftAmpFactor, 
289                 dsb->cvolpan.dwTotalRightAmpFactor);
290
291         if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->cvolpan.lPan == 0)) &&
292             (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->cvolpan.lVolume == 0)) &&
293             !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
294                 return;         /* Nothing to do */
295
296         /* If we end up with some bozo coder using panning or 3D sound */
297         /* with a mono primary buffer, it could sound very weird using */
298         /* this method. Oh well, tough patooties. */
299
300         switch (dsb->dsound->pwfx->wBitsPerSample) {
301         case 8:
302                 /* 8-bit WAV is unsigned, but we need to operate */
303                 /* on signed data for this to work properly */
304                 switch (dsb->dsound->pwfx->nChannels) {
305                 case 1:
306                         for (i = 0; i < len; i++) {
307                                 INT val = *bpc - 128;
308                                 val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
309                                 *bpc = val + 128;
310                                 bpc++;
311                         }
312                         break;
313                 case 2:
314                         for (i = 0; i < len; i+=2) {
315                                 INT val = *bpc - 128;
316                                 val = (val * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
317                                 *bpc++ = val + 128;
318                                 val = *bpc - 128;
319                                 val = (val * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
320                                 *bpc = val + 128;
321                                 bpc++;
322                         }
323                         break;
324                 default:
325                         FIXME("doesn't support %d channels\n", dsb->dsound->pwfx->nChannels);
326                         break;
327                 }
328                 break;
329         case 16:
330                 /* 16-bit WAV is signed -- much better */
331                 switch (dsb->dsound->pwfx->nChannels) {
332                 case 1:
333                         for (i = 0; i < len; i += 2) {
334                                 *bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
335                                 bps++;
336                         }
337                         break;
338                 case 2:
339                         for (i = 0; i < len; i += 4) {
340                                 *bps = (*bps * dsb->cvolpan.dwTotalLeftAmpFactor) >> 16;
341                                 bps++;
342                                 *bps = (*bps * dsb->cvolpan.dwTotalRightAmpFactor) >> 16;
343                                 bps++;
344                         }
345                         break;
346                 default:
347                         FIXME("doesn't support %d channels\n", dsb->dsound->pwfx->nChannels);
348                         break;
349                 }
350                 break;
351         default:
352                 FIXME("doesn't support %d bit samples\n", dsb->dsound->pwfx->wBitsPerSample);
353                 break;
354         }
355 }
356
357 static LPBYTE DSOUND_tmpbuffer(IDirectSoundImpl *dsound, DWORD len)
358 {
359     TRACE("(%p,%ld)\n",dsound,len);
360
361     if (len > dsound->tmp_buffer_len) {
362         if (dsound->tmp_buffer)
363             dsound->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsound->tmp_buffer, len);
364         else
365             dsound->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, len);
366
367         dsound->tmp_buffer_len = len;
368     }
369
370     return dsound->tmp_buffer;
371 }
372
373 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
374 {
375         INT     i, len, ilen, field, todo;
376         BYTE    *buf, *ibuf;
377
378         TRACE("(%p,%ld,%ld)\n",dsb,writepos,fraglen);
379
380         len = fraglen;
381         if (!(dsb->playflags & DSBPLAY_LOOPING)) {
382                 INT temp = MulDiv(dsb->dsound->pwfx->nAvgBytesPerSec, dsb->buflen,
383                         dsb->nAvgBytesPerSec) -
384                         MulDiv(dsb->dsound->pwfx->nAvgBytesPerSec, dsb->buf_mixpos,
385                         dsb->nAvgBytesPerSec);
386                 len = min(len, temp);
387         }
388
389         if (len % dsb->dsound->pwfx->nBlockAlign) {
390                 INT nBlockAlign = dsb->dsound->pwfx->nBlockAlign;
391                 ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign);
392                 len = (len / nBlockAlign) * nBlockAlign;        /* data alignment */
393         }
394
395         if (len == 0) {
396                 /* This should only happen if we aren't looping and temp < nBlockAlign */
397                 return 0;
398         }
399
400         if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound, len)) == NULL)
401                 return 0;
402
403         TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
404
405         ilen = DSOUND_MixerNorm(dsb, ibuf, len);
406         if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
407             (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
408             (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
409                 DSOUND_MixerVol(dsb, ibuf, len);
410
411         if (dsb->dsound->pwfx->wBitsPerSample == 8) {
412                 BYTE    *obuf = dsb->dsound->buffer + writepos;
413
414                 if ((writepos + len) <= dsb->dsound->buflen)
415                         todo = len;
416                 else
417                         todo = dsb->dsound->buflen - writepos;
418
419                 for (i = 0; i < todo; i++) {
420                         /* 8-bit WAV is unsigned */
421                         field = (*ibuf++ - 128);
422                         field += (*obuf - 128);
423                         if (field > 127) field = 127;
424                         else if (field < -128) field = -128;
425                         *obuf++ = field + 128;
426                 }
427  
428                 if (todo < len) {
429                         todo = len - todo;
430                         obuf = dsb->dsound->buffer;
431
432                         for (i = 0; i < todo; i++) {
433                                 /* 8-bit WAV is unsigned */
434                                 field = (*ibuf++ - 128);
435                                 field += (*obuf - 128);
436                                 if (field > 127) field = 127;
437                                 else if (field < -128) field = -128;
438                                 *obuf++ = field + 128;
439                         }
440                 }
441         } else {
442                 INT16   *ibufs, *obufs;
443
444                 ibufs = (INT16 *) ibuf;
445                 obufs = (INT16 *)(dsb->dsound->buffer + writepos);
446
447                 if ((writepos + len) <= dsb->dsound->buflen)
448                         todo = len / 2;
449                 else
450                         todo = (dsb->dsound->buflen - writepos) / 2;
451
452                 for (i = 0; i < todo; i++) {
453                         /* 16-bit WAV is signed */
454                         field = *ibufs++;
455                         field += *obufs;
456                         if (field > 32767) field = 32767;
457                         else if (field < -32768) field = -32768;
458                         *obufs++ = field;
459                 }
460
461                 if (todo < (len / 2)) {
462                         todo = (len / 2) - todo;
463                         obufs = (INT16 *)dsb->dsound->buffer;
464
465                         for (i = 0; i < todo; i++) {
466                                 /* 16-bit WAV is signed */
467                                 field = *ibufs++;
468                                 field += *obufs;
469                                 if (field > 32767) field = 32767;
470                                 else if (field < -32768) field = -32768;
471                                 *obufs++ = field;
472                         }
473                 }
474         }
475
476         if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
477                 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
478                  * not the MIX position... but if the sound buffer is bigger than our prebuffering
479                  * (which must be the case for the streaming buffers that need this hack anyway)
480                  * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
481                 dsb->leadin = FALSE;
482         }
483
484         dsb->buf_mixpos += ilen;
485
486         if (dsb->buf_mixpos >= dsb->buflen) {
487                 if (dsb->playflags & DSBPLAY_LOOPING) {
488                         /* wrap */
489                         dsb->buf_mixpos %= dsb->buflen;
490                         if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
491                                 dsb->leadin = FALSE; /* HACK: see above */
492                 }
493         }
494
495         return len;
496 }
497
498 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
499 {
500         INT     ilen, field;
501         UINT    i, todo;
502         BYTE    *buf, *ibuf;
503
504         TRACE("(%p,%ld,%ld)\n",dsb,writepos,len);
505
506         if (len % dsb->dsound->pwfx->nBlockAlign) {
507                 INT nBlockAlign = dsb->dsound->pwfx->nBlockAlign;
508                 ERR("length not a multiple of block size, len = %ld, block size = %d\n", len, nBlockAlign);
509                 len = (len / nBlockAlign) * nBlockAlign;        /* data alignment */
510         }
511
512         if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound, len)) == NULL)
513                 return;
514
515         TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);
516
517         ilen = DSOUND_MixerNorm(dsb, ibuf, len);
518         if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
519             (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||
520             (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
521                 DSOUND_MixerVol(dsb, ibuf, len);
522
523         /* subtract instead of add, to phase out premixed data */
524         if (dsb->dsound->pwfx->wBitsPerSample == 8) {
525                 BYTE    *obuf = dsb->dsound->buffer + writepos;
526
527                 if ((writepos + len) <= dsb->dsound->buflen)
528                         todo = len;
529                 else
530                         todo = dsb->dsound->buflen - writepos;
531
532                 for (i = 0; i < todo; i++) {
533                         /* 8-bit WAV is unsigned */
534                         field = (*ibuf++ - 128);
535                         field -= (*obuf - 128);
536                         if (field > 127) field = 127;
537                         else if (field < -128) field = -128;
538                         *obuf++ = field + 128;
539                 }
540  
541                 if (todo < len) {
542                         todo = len - todo;
543                         obuf = dsb->dsound->buffer;
544
545                         for (i = 0; i < todo; i++) {
546                                 /* 8-bit WAV is unsigned */
547                                 field = (*ibuf++ - 128);
548                                 field -= (*obuf - 128);
549                                 if (field > 127) field = 127;
550                                 else if (field < -128) field = -128;
551                                 *obuf++ = field + 128;
552                         }
553                 }
554         } else {
555                 INT16   *ibufs, *obufs;
556
557                 ibufs = (INT16 *) ibuf;
558                 obufs = (INT16 *)(dsb->dsound->buffer + writepos);
559
560                 if ((writepos + len) <= dsb->dsound->buflen)
561                         todo = len / 2;
562                 else
563                         todo = (dsb->dsound->buflen - writepos) / 2;
564
565                 for (i = 0; i < todo; i++) {
566                         /* 16-bit WAV is signed */
567                         field = *ibufs++;
568                         field -= *obufs;
569                         if (field > 32767) field = 32767;
570                         else if (field < -32768) field = -32768;
571                         *obufs++ = field;
572                 }
573
574                 if (todo < (len / 2)) {
575                         todo = (len / 2) - todo;
576                         obufs = (INT16 *)dsb->dsound->buffer;
577
578                         for (i = 0; i < todo; i++) {
579                                 /* 16-bit WAV is signed */
580                                 field = *ibufs++;
581                                 field -= *obufs;
582                                 if (field > 32767) field = 32767;
583                                 else if (field < -32768) field = -32768;
584                                 *obufs++ = field;
585                         }
586                 }
587         }
588 }
589
590 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
591 {
592         DWORD   size, flen, len, npos, nlen;
593         INT     iAdvance = dsb->pwfx->nBlockAlign;
594         INT     oAdvance = dsb->dsound->pwfx->nBlockAlign;
595         /* determine amount of premixed data to cancel */
596         DWORD primary_done =
597                 ((dsb->primary_mixpos < writepos) ? dsb->dsound->buflen : 0) +
598                 dsb->primary_mixpos - writepos;
599
600         TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);
601
602         /* backtrack the mix position */
603         size = primary_done / oAdvance;
604         flen = size * dsb->freqAdjust;
605         len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
606         flen &= (1<<DSOUND_FREQSHIFT)-1;
607         while (dsb->freqAcc < flen) {
608                 len += iAdvance;
609                 dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
610         }
611         len %= dsb->buflen;
612         npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
613                 dsb->buf_mixpos - len;
614         if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
615                 /* stop backtracking at startpos */
616                 npos = dsb->startpos;
617                 len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
618                         dsb->buf_mixpos - npos;
619                 flen = dsb->freqAcc;
620                 nlen = len / dsb->pwfx->nBlockAlign;
621                 nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
622                 nlen *= dsb->dsound->pwfx->nBlockAlign;
623                 writepos =
624                         ((dsb->primary_mixpos < nlen) ? dsb->dsound->buflen : 0) +
625                         dsb->primary_mixpos - nlen;
626         }
627
628         dsb->freqAcc -= flen;
629         dsb->buf_mixpos = npos;
630         dsb->primary_mixpos = writepos;
631
632         TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
633               dsb->buf_mixpos, dsb->primary_mixpos, len);
634
635         if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
636 }
637
638 void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
639 {
640 #if 0
641         DWORD   i, size, flen, len, npos, nlen;
642         INT     iAdvance = dsb->pwfx->nBlockAlign;
643         INT     oAdvance = dsb->dsound->pwfx->nBlockAlign;
644         /* determine amount of premixed data to cancel */
645         DWORD buf_done =
646                 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
647                 dsb->buf_mixpos - buf_writepos;
648 #endif
649
650         WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);
651         /* since this is not implemented yet, just cancel *ALL* prebuffering for now
652          * (which is faster anyway when there's only a single secondary buffer) */
653         dsb->dsound->need_remix = TRUE;
654 }
655
656 void DSOUND_ForceRemix(IDirectSoundBufferImpl *dsb)
657 {
658         TRACE("(%p)\n",dsb);
659         EnterCriticalSection(&dsb->lock);
660         if (dsb->state == STATE_PLAYING) {
661 #if 0 /* this may not be quite reliable yet */
662                 dsb->need_remix = TRUE;
663 #else
664                 dsb->dsound->need_remix = TRUE;
665 #endif
666         }
667         LeaveCriticalSection(&dsb->lock);
668 }
669
670 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
671 {
672         DWORD len, slen;
673         /* determine this buffer's write position */
674         DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & dsb->dsound->state, writepos,
675                                                      writepos, dsb->primary_mixpos, dsb->buf_mixpos);
676         /* determine how much already-mixed data exists */
677         DWORD buf_done =
678                 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
679                 dsb->buf_mixpos - buf_writepos;
680         DWORD primary_done =
681                 ((dsb->primary_mixpos < writepos) ? dsb->dsound->buflen : 0) +
682                 dsb->primary_mixpos - writepos;
683         DWORD adv_done =
684                 ((dsb->dsound->mixpos < writepos) ? dsb->dsound->buflen : 0) +
685                 dsb->dsound->mixpos - writepos;
686         DWORD played =
687                 ((buf_writepos < dsb->playpos) ? dsb->buflen : 0) +
688                 buf_writepos - dsb->playpos;
689         DWORD buf_left = dsb->buflen - buf_writepos;
690         int still_behind;
691
692         TRACE("(%p,%ld,%ld,%ld)\n",dsb,playpos,writepos,mixlen);
693         TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
694         TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
695         TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
696               mixlen);
697         TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
698
699         /* check for notification positions */
700         if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&
701             dsb->state != STATE_STARTING) {
702                 DSOUND_CheckEvent(dsb, played);
703         }
704
705         /* save write position for non-GETCURRENTPOSITION2... */
706         dsb->playpos = buf_writepos;
707
708         /* check whether CalcPlayPosition detected a mixing underrun */
709         if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
710                 /* it did, but did we have more to play? */
711                 if ((dsb->playflags & DSBPLAY_LOOPING) ||
712                     (dsb->buf_mixpos < dsb->buflen)) {
713                         /* yes, have to recover */
714                         ERR("underrun on sound buffer %p\n", dsb);
715                         TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
716                 }
717                 dsb->primary_mixpos = writepos;
718                 primary_done = 0;
719         }
720         /* determine how far ahead we should mix */
721         if (((dsb->playflags & DSBPLAY_LOOPING) ||
722              (dsb->leadin && (dsb->probably_valid_to != 0))) &&
723             !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
724                 /* if this is a streaming buffer, it typically means that
725                  * we should defer mixing past probably_valid_to as long
726                  * as we can, to avoid unnecessary remixing */
727                 /* the heavy-looking calculations shouldn't be that bad,
728                  * as any game isn't likely to be have more than 1 or 2
729                  * streaming buffers in use at any time anyway... */
730                 DWORD probably_valid_left =
731                         (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
732                         ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
733                         dsb->probably_valid_to - buf_writepos;
734                 /* check for leadin condition */
735                 if ((probably_valid_left == 0) &&
736                     (dsb->probably_valid_to == dsb->startpos) &&
737                     dsb->leadin)
738                         probably_valid_left = dsb->buflen;
739                 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
740                       dsb->probably_valid_to, probably_valid_left);
741                 /* check whether the app's time is already up */
742                 if (probably_valid_left < dsb->writelead) {
743                         WARN("probably_valid_to now within writelead, possible streaming underrun\n");
744                         /* once we pass the point of no return,
745                          * no reason to hold back anymore */
746                         dsb->probably_valid_to = (DWORD)-1;
747                         /* we just have to go ahead and mix what we have,
748                          * there's no telling what the app is thinking anyway */
749                 } else {
750                         /* adjust for our frequency and our sample size */
751                         probably_valid_left = MulDiv(probably_valid_left,
752                                                      1 << DSOUND_FREQSHIFT,
753                                                      dsb->pwfx->nBlockAlign * dsb->freqAdjust) *
754                                               dsb->dsound->pwfx->nBlockAlign;
755                         /* check whether to clip mix_len */
756                         if (probably_valid_left < mixlen) {
757                                 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
758                                 mixlen = probably_valid_left;
759                         }
760                 }
761         }
762         /* cut mixlen with what's already been mixed */
763         if (mixlen < primary_done) {
764                 /* huh? and still CalcPlayPosition didn't
765                  * detect an underrun? */
766                 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
767                 return 0;
768         }
769         len = mixlen - primary_done;
770         TRACE("remaining mixlen=%ld\n", len);
771
772         if (len < dsb->dsound->fraglen) {
773                 /* smaller than a fragment, wait until it gets larger
774                  * before we take the mixing overhead */
775                 TRACE("mixlen not worth it, deferring mixing\n");
776                 still_behind = 1;
777                 goto post_mix;
778         }
779
780         /* ok, we know how much to mix, let's go */
781         still_behind = (adv_done > primary_done);
782         while (len) {
783                 slen = dsb->dsound->buflen - dsb->primary_mixpos;
784                 if (slen > len) slen = len;
785                 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
786
787                 if ((dsb->primary_mixpos < dsb->dsound->mixpos) &&
788                     (dsb->primary_mixpos + slen >= dsb->dsound->mixpos))
789                         still_behind = FALSE;
790
791                 dsb->primary_mixpos += slen; len -= slen;
792                 dsb->primary_mixpos %= dsb->dsound->buflen;
793
794                 if ((dsb->state == STATE_STOPPED) || !slen) break;
795         }
796         TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, dsb->dsound->mixpos);
797         TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
798
799 post_mix:
800         /* check if buffer should be considered complete */
801         if (buf_left < dsb->writelead &&
802             !(dsb->playflags & DSBPLAY_LOOPING)) {
803                 dsb->state = STATE_STOPPED;
804                 dsb->playpos = 0;
805                 dsb->last_playpos = 0;
806                 dsb->buf_mixpos = 0;
807                 dsb->leadin = FALSE;
808                 DSOUND_CheckEvent(dsb, buf_left);
809         }
810
811         /* return how far we think the primary buffer can
812          * advance its underrun detector...*/
813         if (still_behind) return 0;
814         if ((mixlen - len) < primary_done) return 0;
815         slen = ((dsb->primary_mixpos < dsb->dsound->mixpos) ?
816                 dsb->dsound->buflen : 0) + dsb->primary_mixpos -
817                 dsb->dsound->mixpos;
818         if (slen > mixlen) {
819                 /* the primary_done and still_behind checks above should have worked */
820                 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
821                 slen = 0;
822         }
823         return slen;
824 }
825
826 static DWORD DSOUND_MixToPrimary(IDirectSoundImpl *dsound, DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
827 {
828         INT                     i, len, maxlen = 0;
829         IDirectSoundBufferImpl  *dsb;
830
831         TRACE("(%ld,%ld,%ld,%d)\n", playpos, writepos, mixlen, recover);
832         for (i = 0; i < dsound->nrofbuffers; i++) {
833                 dsb = dsound->buffers[i];
834
835                 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
836                         TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
837                         EnterCriticalSection(&(dsb->lock));
838                         if (dsb->state == STATE_STOPPING) {
839                                 DSOUND_MixCancel(dsb, writepos, TRUE);
840                                 dsb->state = STATE_STOPPED;
841                                 DSOUND_CheckEvent(dsb, 0);
842                         } else {
843                                 if ((dsb->state == STATE_STARTING) || recover) {
844                                         dsb->primary_mixpos = writepos;
845                                         dsb->cvolpan = dsb->volpan;
846                                         dsb->need_remix = FALSE;
847                                 }
848                                 else if (dsb->need_remix) {
849                                         DSOUND_MixCancel(dsb, writepos, TRUE);
850                                         dsb->cvolpan = dsb->volpan;
851                                         dsb->need_remix = FALSE;
852                                 }
853                                 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
854                                 if (dsb->state == STATE_STARTING)
855                                         dsb->state = STATE_PLAYING;
856                                 maxlen = (len > maxlen) ? len : maxlen;
857                         }
858                         LeaveCriticalSection(&(dsb->lock));
859                 }
860         }
861
862         return maxlen;
863 }
864
865 static void DSOUND_MixReset(IDirectSoundImpl *dsound, DWORD writepos)
866 {
867         INT                     i;
868         IDirectSoundBufferImpl  *dsb;
869         int nfiller;
870
871         TRACE("(%ld)\n", writepos);
872
873         /* the sound of silence */
874         nfiller = dsound->pwfx->wBitsPerSample == 8 ? 128 : 0;
875
876         /* reset all buffer mix positions */
877         for (i = 0; i < dsound->nrofbuffers; i++) {
878                 dsb = dsound->buffers[i];
879
880                 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
881                         TRACE("Resetting %p\n", dsb);
882                         EnterCriticalSection(&(dsb->lock));
883                         if (dsb->state == STATE_STOPPING) {
884                                 dsb->state = STATE_STOPPED;
885                         }
886                         else if (dsb->state == STATE_STARTING) {
887                                 /* nothing */
888                         } else {
889                                 DSOUND_MixCancel(dsb, writepos, FALSE);
890                                 dsb->cvolpan = dsb->volpan;
891                                 dsb->need_remix = FALSE;
892                         }
893                         LeaveCriticalSection(&(dsb->lock));
894                 }
895         }
896
897         /* wipe out premixed data */
898         if (dsound->mixpos < writepos) {
899                 FillMemory(dsound->buffer + writepos, dsound->buflen - writepos, nfiller);
900                 FillMemory(dsound->buffer, dsound->mixpos, nfiller);
901         } else {
902                 FillMemory(dsound->buffer + writepos, dsound->mixpos - writepos, nfiller);
903         }
904
905         /* reset primary mix position */
906         dsound->mixpos = writepos;
907 }
908
909 static void DSOUND_CheckReset(IDirectSoundImpl *dsound, DWORD writepos)
910 {
911         TRACE("(%p,%ld)\n",dsound,writepos);
912         if (dsound->need_remix) {
913                 DSOUND_MixReset(dsound, writepos);
914                 dsound->need_remix = FALSE;
915                 /* maximize Half-Life performance */
916                 dsound->prebuf = ds_snd_queue_min;
917                 dsound->precount = 0;
918         } else {
919                 dsound->precount++;
920                 if (dsound->precount >= 4) {
921                         if (dsound->prebuf < ds_snd_queue_max)
922                                 dsound->prebuf++;
923                         dsound->precount = 0;
924                 }
925         }
926         TRACE("premix adjust: %d\n", dsound->prebuf);
927 }
928
929 void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq)
930 {
931         TRACE("(%p,%ld)\n",dsound,mixq);
932         if (mixq + dsound->pwqueue > ds_hel_queue) mixq = ds_hel_queue - dsound->pwqueue;
933         TRACE("queueing %ld buffers, starting at %d\n", mixq, dsound->pwwrite);
934         for (; mixq; mixq--) {
935                 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
936                 dsound->pwwrite++;
937                 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
938                 dsound->pwqueue++;
939         }
940 }
941
942 /* #define SYNC_CALLBACK */
943
944 void DSOUND_PerformMix(IDirectSoundImpl *dsound)
945 {
946         int nfiller;
947         BOOL forced;
948         HRESULT hres;
949
950         TRACE("(%p)\n", dsound);
951
952         /* the sound of silence */
953         nfiller = dsound->pwfx->wBitsPerSample == 8 ? 128 : 0;
954
955         /* whether the primary is forced to play even without secondary buffers */
956         forced = ((dsound->state == STATE_PLAYING) || (dsound->state == STATE_STARTING));
957
958         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
959                 BOOL paused = ((dsound->state == STATE_STOPPED) || (dsound->state == STATE_STARTING));
960                 /* FIXME: document variables */
961                 DWORD playpos, writepos, inq, maxq, frag;
962                 if (dsound->hwbuf) {
963                         hres = IDsDriverBuffer_GetPosition(dsound->hwbuf, &playpos, &writepos);
964                         if (hres) {
965                             WARN("IDsDriverBuffer_GetPosition failed\n");
966                             return;
967                         }
968                         /* Well, we *could* do Just-In-Time mixing using the writepos,
969                          * but that's a little bit ambitious and unnecessary... */
970                         /* rather add our safety margin to the writepos, if we're playing */
971                         if (!paused) {
972                                 writepos += dsound->writelead;
973                                 writepos %= dsound->buflen;
974                         } else writepos = playpos;
975                 } else {
976                         playpos = dsound->pwplay * dsound->fraglen;
977                         writepos = playpos;
978                         if (!paused) {
979                                 writepos += ds_hel_margin * dsound->fraglen;
980                                 writepos %= dsound->buflen;
981                         }
982                 }
983                 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld, buflen=%ld\n",
984                       playpos,writepos,dsound->playpos,dsound->mixpos,dsound->buflen);
985                 assert(dsound->playpos < dsound->buflen);
986                 /* wipe out just-played sound data */
987                 if (playpos < dsound->playpos) {
988                         FillMemory(dsound->buffer + dsound->playpos, dsound->buflen - dsound->playpos, nfiller);
989                         FillMemory(dsound->buffer, playpos, nfiller);
990                 } else {
991                         FillMemory(dsound->buffer + dsound->playpos, playpos - dsound->playpos, nfiller);
992                 }
993                 dsound->playpos = playpos;
994
995                 EnterCriticalSection(&(dsound->mixlock));
996
997                 /* reset mixing if necessary */
998                 DSOUND_CheckReset(dsound, writepos);
999
1000                 /* check how much prebuffering is left */
1001                 inq = dsound->mixpos;
1002                 if (inq < writepos)
1003                         inq += dsound->buflen;
1004                 inq -= writepos;
1005
1006                 /* find the maximum we can prebuffer */
1007                 if (!paused) {
1008                         maxq = playpos;
1009                         if (maxq < writepos)
1010                                 maxq += dsound->buflen;
1011                         maxq -= writepos;
1012                 } else maxq = dsound->buflen;
1013
1014                 /* clip maxq to dsound->prebuf */
1015                 frag = dsound->prebuf * dsound->fraglen;
1016                 if (maxq > frag) maxq = frag;
1017
1018                 /* check for consistency */
1019                 if (inq > maxq) {
1020                         /* the playback position must have passed our last
1021                          * mixed position, i.e. it's an underrun, or we have
1022                          * nothing more to play */
1023                         TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
1024                         inq = 0;
1025                         /* stop the playback now, to allow buffers to refill */
1026                         if (dsound->state == STATE_PLAYING) {
1027                                 dsound->state = STATE_STARTING;
1028                         }
1029                         else if (dsound->state == STATE_STOPPING) {
1030                                 dsound->state = STATE_STOPPED;
1031                         }
1032                         else {
1033                                 /* how can we have an underrun if we aren't playing? */
1034                                 WARN("unexpected primary state (%ld)\n", dsound->state);
1035                         }
1036 #ifdef SYNC_CALLBACK
1037                         /* DSOUND_callback may need this lock */
1038                         LeaveCriticalSection(&(dsound->mixlock));
1039 #endif
1040                         if (DSOUND_PrimaryStop(dsound) != DS_OK)
1041                                 WARN("DSOUND_PrimaryStop failed\n");
1042 #ifdef SYNC_CALLBACK
1043                         EnterCriticalSection(&(dsound->mixlock));
1044 #endif
1045                         if (dsound->hwbuf) {
1046                                 /* the Stop is supposed to reset play position to beginning of buffer */
1047                                 /* unfortunately, OSS is not able to do so, so get current pointer */
1048                                 hres = IDsDriverBuffer_GetPosition(dsound->hwbuf, &playpos, NULL);
1049                                 if (hres) {
1050                                         LeaveCriticalSection(&(dsound->mixlock));
1051                                         WARN("IDsDriverBuffer_GetPosition failed\n");
1052                                         return;
1053                                 }
1054                         } else {
1055                                 playpos = dsound->pwplay * dsound->fraglen;
1056                         }
1057                         writepos = playpos;
1058                         dsound->playpos = playpos;
1059                         dsound->mixpos = writepos;
1060                         inq = 0;
1061                         maxq = dsound->buflen;
1062                         if (maxq > frag) maxq = frag;
1063                         FillMemory(dsound->buffer, dsound->buflen, nfiller);
1064                         paused = TRUE;
1065                 }
1066
1067                 /* do the mixing */
1068                 frag = DSOUND_MixToPrimary(dsound, playpos, writepos, maxq, paused);
1069                 if (forced) frag = maxq - inq;
1070                 dsound->mixpos += frag;
1071                 dsound->mixpos %= dsound->buflen;
1072
1073                 if (frag) {
1074                         /* buffers have been filled, restart playback */
1075                         if (dsound->state == STATE_STARTING) {
1076                                 dsound->state = STATE_PLAYING;
1077                         }
1078                         else if (dsound->state == STATE_STOPPED) {
1079                                 /* the dsound is supposed to play if there's something to play
1080                                  * even if it is reported as stopped, so don't let this confuse you */
1081                                 dsound->state = STATE_STOPPING;
1082                         }
1083                         LeaveCriticalSection(&(dsound->mixlock));
1084                         if (paused) {
1085                                 if (DSOUND_PrimaryPlay(dsound) != DS_OK)
1086                                         WARN("DSOUND_PrimaryPlay failed\n");
1087                                 else
1088                                         TRACE("starting playback\n");
1089                         }
1090                 }
1091                 else
1092                         LeaveCriticalSection(&(dsound->mixlock));
1093         } else {
1094                 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
1095                 if (dsound->state == STATE_STARTING) {
1096                         if (DSOUND_PrimaryPlay(dsound) != DS_OK)
1097                                 WARN("DSOUND_PrimaryPlay failed\n");
1098                         else
1099                                 dsound->state = STATE_PLAYING;
1100                 }
1101                 else if (dsound->state == STATE_STOPPING) {
1102                         if (DSOUND_PrimaryStop(dsound) != DS_OK)
1103                                 WARN("DSOUND_PrimaryStop failed\n");
1104                         else
1105                                 dsound->state = STATE_STOPPED;
1106                 }
1107         }
1108 }
1109
1110 void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
1111 {
1112         IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
1113         DWORD start_time =  GetTickCount();
1114         DWORD end_time;
1115         TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
1116         TRACE("entering at %ld\n", start_time);
1117
1118         if (DSOUND_renderer != This) {
1119                 ERR("dsound died without killing us?\n");
1120                 timeKillEvent(timerID);
1121                 timeEndPeriod(DS_TIME_RES);
1122                 return;
1123         }
1124
1125         RtlAcquireResourceShared(&(This->buffer_list_lock), TRUE);
1126
1127         if (This->ref)
1128                 DSOUND_PerformMix(This);
1129
1130         RtlReleaseResource(&(This->buffer_list_lock));
1131
1132         end_time = GetTickCount();
1133         TRACE("completed processing at %ld, duration = %ld\n", end_time, end_time - start_time);
1134 }
1135
1136 void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
1137 {
1138         IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
1139         TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo,msg,dwUser,dw1,dw2);
1140         TRACE("entering at %ld, msg=%08x(%s)\n", GetTickCount(), msg, 
1141                 msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" : 
1142                 msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN");
1143         if (msg == MM_WOM_DONE) {
1144                 DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
1145                 if (This->pwqueue == (DWORD)-1) {
1146                         TRACE("completed due to reset\n");
1147                         return;
1148                 }
1149 /* it could be a bad idea to enter critical section here... if there's lock contention,
1150  * the resulting scheduling delays might obstruct the winmm player thread */
1151 #ifdef SYNC_CALLBACK
1152                 EnterCriticalSection(&(This->mixlock));
1153 #endif
1154                 /* retrieve current values */
1155                 fraglen = This->fraglen;
1156                 buflen = This->buflen;
1157                 pwplay = This->pwplay;
1158                 playpos = pwplay * fraglen;
1159                 mixpos = This->mixpos;
1160                 /* check remaining mixed data */
1161                 inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
1162                 mixq = inq / fraglen;
1163                 if ((inq - (mixq * fraglen)) > 0) mixq++;
1164                 /* complete the playing buffer */
1165                 TRACE("done playing primary pos=%ld\n", playpos);
1166                 pwplay++;
1167                 if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
1168                 /* write new values */
1169                 This->pwplay = pwplay;
1170                 This->pwqueue--;
1171                 /* queue new buffer if we have data for it */
1172                 if (inq>1) DSOUND_WaveQueue(This, inq-1);
1173 #ifdef SYNC_CALLBACK
1174                 LeaveCriticalSection(&(This->mixlock));
1175 #endif
1176         }
1177         TRACE("completed\n");
1178 }