dsound: Remove minlen, since we always mix full chunks anyway.
[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  * Copyright 2007 Peter Dons Tychsen
7  * Copyright 2007 Maarten Lankhorst
8  * Copyright 2011 Owen Rudge for CodeWeavers
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24
25 #include <assert.h>
26 #include <stdarg.h>
27 #include <math.h>       /* Insomnia - pow() function */
28
29 #define COBJMACROS
30 #define NONAMELESSSTRUCT
31 #define NONAMELESSUNION
32 #include "windef.h"
33 #include "winbase.h"
34 #include "mmsystem.h"
35 #include "wingdi.h"
36 #include "mmreg.h"
37 #include "winternl.h"
38 #include "wine/debug.h"
39 #include "dsound.h"
40 #include "ks.h"
41 #include "ksmedia.h"
42 #include "dsound_private.h"
43 #include "fir.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
46
47 void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
48 {
49         double temp;
50         TRACE("(%p)\n",volpan);
51
52         TRACE("Vol=%d Pan=%d\n", volpan->lVolume, volpan->lPan);
53         /* the AmpFactors are expressed in 16.16 fixed point */
54         volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 0xffff);
55         /* FIXME: dwPan{Left|Right}AmpFactor */
56
57         /* FIXME: use calculated vol and pan ampfactors */
58         temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
59         volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff);
60         temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
61         volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 0xffff);
62
63         TRACE("left = %x, right = %x\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
64 }
65
66 void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan)
67 {
68     double left,right;
69     TRACE("(%p)\n",volpan);
70
71     TRACE("left=%x, right=%x\n",volpan->dwTotalLeftAmpFactor,volpan->dwTotalRightAmpFactor);
72     if (volpan->dwTotalLeftAmpFactor==0)
73         left=-10000;
74     else
75         left=600 * log(((double)volpan->dwTotalLeftAmpFactor) / 0xffff) / log(2);
76     if (volpan->dwTotalRightAmpFactor==0)
77         right=-10000;
78     else
79         right=600 * log(((double)volpan->dwTotalRightAmpFactor) / 0xffff) / log(2);
80     if (left<right)
81     {
82         volpan->lVolume=right;
83         volpan->dwVolAmpFactor=volpan->dwTotalRightAmpFactor;
84     }
85     else
86     {
87         volpan->lVolume=left;
88         volpan->dwVolAmpFactor=volpan->dwTotalLeftAmpFactor;
89     }
90     if (volpan->lVolume < -10000)
91         volpan->lVolume=-10000;
92     volpan->lPan=right-left;
93     if (volpan->lPan < -10000)
94         volpan->lPan=-10000;
95
96     TRACE("Vol=%d Pan=%d\n", volpan->lVolume, volpan->lPan);
97 }
98
99 /** Convert a primary buffer position to a pointer position for device->mix_buffer
100  * device: DirectSoundDevice for which to calculate
101  * pos: Primary buffer position to converts
102  * Returns: Offset for mix_buffer
103  */
104 DWORD DSOUND_bufpos_to_mixpos(const DirectSoundDevice* device, DWORD pos)
105 {
106     DWORD ret = pos * 32 / device->pwfx->wBitsPerSample;
107     if (device->pwfx->wBitsPerSample == 32)
108         ret *= 2;
109     return ret;
110 }
111
112 /**
113  * Recalculate the size for temporary buffer, and new writelead
114  * Should be called when one of the following things occur:
115  * - Primary buffer format is changed
116  * - This buffer format (frequency) is changed
117  */
118 void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
119 {
120         DWORD ichannels = dsb->pwfx->nChannels;
121         DWORD ochannels = dsb->device->pwfx->nChannels;
122         WAVEFORMATEXTENSIBLE *pwfxe;
123         BOOL ieee = FALSE;
124
125         TRACE("(%p)\n",dsb);
126
127         pwfxe = (WAVEFORMATEXTENSIBLE *) dsb->pwfx;
128
129         if ((pwfxe->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) || ((pwfxe->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
130             && (IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))))
131                 ieee = TRUE;
132
133         /**
134          * Recalculate FIR step and gain.
135          *
136          * firstep says how many points of the FIR exist per one
137          * sample in the secondary buffer. firgain specifies what
138          * to multiply the FIR output by in order to attenuate it correctly.
139          */
140         if (dsb->freqAdjust > 1.0f) {
141                 /**
142                  * Yes, round it a bit to make sure that the
143                  * linear interpolation factor never changes.
144                  */
145                 dsb->firstep = ceil(fir_step / dsb->freqAdjust);
146         } else {
147                 dsb->firstep = fir_step;
148         }
149         dsb->firgain = (float)dsb->firstep / fir_step;
150
151         /* calculate the 10ms write lead */
152         dsb->writelead = (dsb->freq / 100) * dsb->pwfx->nBlockAlign;
153
154         dsb->freqAcc = 0;
155
156         dsb->get_aux = ieee ? getbpp[4] : getbpp[dsb->pwfx->wBitsPerSample/8 - 1];
157         dsb->put_aux = putbpp[dsb->device->pwfx->wBitsPerSample/8 - 1];
158
159         dsb->get = dsb->get_aux;
160         dsb->put = dsb->put_aux;
161
162         if (ichannels == ochannels)
163         {
164                 dsb->mix_channels = ichannels;
165                 if (ichannels > 32) {
166                         FIXME("Copying %u channels is unsupported, limiting to first 32\n", ichannels);
167                         dsb->mix_channels = 32;
168                 }
169         }
170         else if (ichannels == 1)
171         {
172                 dsb->mix_channels = 1;
173                 dsb->put = put_mono2stereo;
174         }
175         else if (ochannels == 1)
176         {
177                 dsb->mix_channels = 1;
178                 dsb->get = get_mono;
179         }
180         else
181         {
182                 if (ichannels > 2)
183                         FIXME("Conversion from %u to %u channels is not implemented, falling back to stereo\n", ichannels, ochannels);
184                 dsb->mix_channels = 2;
185         }
186 }
187
188 /**
189  * Check for application callback requests for when the play position
190  * reaches certain points.
191  *
192  * The offsets that will be triggered will be those between the recorded
193  * "last played" position for the buffer (i.e. dsb->playpos) and "len" bytes
194  * beyond that position.
195  */
196 void DSOUND_CheckEvent(const IDirectSoundBufferImpl *dsb, DWORD playpos, int len)
197 {
198         int                     i;
199         DWORD                   offset;
200         LPDSBPOSITIONNOTIFY     event;
201         TRACE("(%p,%d)\n",dsb,len);
202
203         if (dsb->nrofnotifies == 0)
204                 return;
205
206         TRACE("(%p) buflen = %d, playpos = %d, len = %d\n",
207                 dsb, dsb->buflen, playpos, len);
208         for (i = 0; i < dsb->nrofnotifies ; i++) {
209                 event = dsb->notifies + i;
210                 offset = event->dwOffset;
211                 TRACE("checking %d, position %d, event = %p\n",
212                         i, offset, event->hEventNotify);
213                 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
214                 /* OK. [Inside DirectX, p274] */
215                 /* Windows does not seem to enforce this, and some apps rely */
216                 /* on that, so we can't stop there. */
217                 /*  */
218                 /* This also means we can't sort the entries by offset, */
219                 /* because DSBPN_OFFSETSTOP == -1 */
220                 if (offset == DSBPN_OFFSETSTOP) {
221                         if (dsb->state == STATE_STOPPED) {
222                                 SetEvent(event->hEventNotify);
223                                 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
224                         }
225                         continue;
226                 }
227                 if ((playpos + len) >= dsb->buflen) {
228                         if ((offset < ((playpos + len) % dsb->buflen)) ||
229                             (offset >= playpos)) {
230                                 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
231                                 SetEvent(event->hEventNotify);
232                         }
233                 } else {
234                         if ((offset >= playpos) && (offset < (playpos + len))) {
235                                 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
236                                 SetEvent(event->hEventNotify);
237                         }
238                 }
239         }
240 }
241
242 static inline float get_current_sample(const IDirectSoundBufferImpl *dsb,
243         DWORD mixpos, DWORD channel)
244 {
245     if (mixpos >= dsb->buflen && !(dsb->playflags & DSBPLAY_LOOPING))
246         return 0.0f;
247     return dsb->get(dsb, mixpos % dsb->buflen, channel);
248 }
249
250 static UINT cp_fields_noresample(IDirectSoundBufferImpl *dsb,
251         UINT ostride, UINT count)
252 {
253     UINT istride = dsb->pwfx->nBlockAlign;
254     DWORD channel, i;
255     for (i = 0; i < count; i++)
256         for (channel = 0; channel < dsb->mix_channels; channel++)
257             dsb->put(dsb, i * ostride, channel, get_current_sample(dsb,
258                     dsb->sec_mixpos + i * istride, channel));
259     return count;
260 }
261
262 static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb,
263         UINT ostride, UINT count, float *freqAcc)
264 {
265     UINT i, channel;
266     UINT istride = dsb->pwfx->nBlockAlign;
267
268     float freqAdjust = dsb->freqAdjust;
269     float freqAcc_start = *freqAcc;
270     float freqAcc_end = freqAcc_start + count * freqAdjust;
271     UINT dsbfirstep = dsb->firstep;
272     UINT channels = dsb->mix_channels;
273     UINT max_ipos = freqAcc_start + count * freqAdjust;
274
275     UINT fir_cachesize = (fir_len + dsbfirstep - 2) / dsbfirstep;
276     UINT required_input = max_ipos + fir_cachesize;
277
278     float* intermediate = HeapAlloc(GetProcessHeap(), 0,
279             sizeof(float) * required_input * channels);
280
281     float* fir_copy = HeapAlloc(GetProcessHeap(), 0,
282             sizeof(float) * fir_cachesize);
283
284     /* Important: this buffer MUST be non-interleaved
285      * if you want -msse3 to have any effect.
286      * This is good for CPU cache effects, too.
287      */
288     float* itmp = intermediate;
289     for (channel = 0; channel < channels; channel++)
290         for (i = 0; i < required_input; i++)
291             *(itmp++) = get_current_sample(dsb,
292                     dsb->sec_mixpos + i * istride, channel);
293
294     for(i = 0; i < count; ++i) {
295         float total_fir_steps = (freqAcc_start + i * freqAdjust) * dsbfirstep;
296         UINT int_fir_steps = total_fir_steps;
297         UINT ipos = int_fir_steps / dsbfirstep;
298
299         UINT idx = (ipos + 1) * dsbfirstep - int_fir_steps - 1;
300         float rem = int_fir_steps + 1.0 - total_fir_steps;
301
302         int fir_used = 0;
303         while (idx < fir_len - 1) {
304             fir_copy[fir_used++] = fir[idx] * (1.0 - rem) + fir[idx + 1] * rem;
305             idx += dsb->firstep;
306         }
307
308         assert(fir_used <= fir_cachesize);
309         assert(ipos + fir_used <= required_input);
310
311         for (channel = 0; channel < dsb->mix_channels; channel++) {
312             int j;
313             float sum = 0.0;
314             float* cache = &intermediate[channel * required_input + ipos];
315             for (j = 0; j < fir_used; j++)
316                 sum += fir_copy[j] * cache[j];
317             dsb->put(dsb, i * ostride, channel, sum * dsb->firgain);
318         }
319     }
320
321     freqAcc_end -= (int)freqAcc_end;
322     *freqAcc = freqAcc_end;
323
324     HeapFree(GetProcessHeap(), 0, fir_copy);
325     HeapFree(GetProcessHeap(), 0, intermediate);
326
327     return max_ipos;
328 }
329
330 static void cp_fields(IDirectSoundBufferImpl *dsb,
331         UINT ostride, UINT count, float *freqAcc)
332 {
333     DWORD ipos, adv;
334
335     if (dsb->freqAdjust == 1.0)
336         adv = cp_fields_noresample(dsb, ostride, count); /* *freqAcc is unmodified */
337     else
338         adv = cp_fields_resample(dsb, ostride, count, freqAcc);
339
340     ipos = dsb->sec_mixpos + adv * dsb->pwfx->nBlockAlign;
341     if (ipos >= dsb->buflen) {
342         if (dsb->playflags & DSBPLAY_LOOPING)
343             ipos %= dsb->buflen;
344         else {
345             ipos = 0;
346             dsb->state = STATE_STOPPED;
347         }
348     }
349
350     dsb->sec_mixpos = ipos;
351 }
352
353 /**
354  * Calculate the distance between two buffer offsets, taking wraparound
355  * into account.
356  */
357 static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2)
358 {
359 /* If these asserts fail, the problem is not here, but in the underlying code */
360         assert(ptr1 < buflen);
361         assert(ptr2 < buflen);
362         if (ptr1 >= ptr2) {
363                 return ptr1 - ptr2;
364         } else {
365                 return buflen + ptr1 - ptr2;
366         }
367 }
368 /**
369  * Mix at most the given amount of data into the allocated temporary buffer
370  * of the given secondary buffer, starting from the dsb's first currently
371  * unsampled frame (writepos), translating frequency (pitch), stereo/mono
372  * and bits-per-sample so that it is ideal for the primary buffer.
373  * Doesn't perform any mixing - this is a straight copy/convert operation.
374  *
375  * dsb = the secondary buffer
376  * writepos = Starting position of changed buffer
377  * len = number of bytes to resample from writepos
378  *
379  * NOTE: writepos + len <= buflen. When called by mixer, MixOne makes sure of this.
380  */
381 static void DSOUND_MixToTemporary(IDirectSoundBufferImpl *dsb, DWORD tmp_len)
382 {
383         INT     oAdvance = dsb->device->pwfx->nBlockAlign;
384         INT     size = tmp_len / oAdvance;
385
386         if (dsb->device->tmp_buffer_len < tmp_len || !dsb->device->tmp_buffer)
387         {
388                 dsb->device->tmp_buffer_len = tmp_len;
389                 if (dsb->device->tmp_buffer)
390                         dsb->device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsb->device->tmp_buffer, tmp_len);
391                 else
392                         dsb->device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, tmp_len);
393         }
394
395         cp_fields(dsb, oAdvance, size, &dsb->freqAcc);
396 }
397
398 /** Apply volume to the given soundbuffer from (primary) position writepos and length len
399  * Returns: NULL if no volume needs to be applied
400  * or else a memory handle that holds 'len' volume adjusted buffer */
401 static LPBYTE DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, INT len)
402 {
403         INT     i;
404         BYTE    *bpc;
405         INT16   *bps, *mems;
406         DWORD vLeft, vRight;
407         INT nChannels = dsb->device->pwfx->nChannels;
408         LPBYTE mem = dsb->device->tmp_buffer;
409
410         TRACE("(%p,%d)\n",dsb,len);
411         TRACE("left = %x, right = %x\n", dsb->volpan.dwTotalLeftAmpFactor,
412                 dsb->volpan.dwTotalRightAmpFactor);
413
414         if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
415             (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
416              !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
417                 return NULL; /* Nothing to do */
418
419         if (nChannels != 1 && nChannels != 2)
420         {
421                 FIXME("There is no support for %d channels\n", nChannels);
422                 return NULL;
423         }
424
425         if (dsb->device->pwfx->wBitsPerSample != 8 && dsb->device->pwfx->wBitsPerSample != 16)
426         {
427                 FIXME("There is no support for %d bpp\n", dsb->device->pwfx->wBitsPerSample);
428                 return NULL;
429         }
430
431         assert(dsb->device->tmp_buffer_len >= len && dsb->device->tmp_buffer);
432
433         bpc = dsb->device->tmp_buffer;
434         bps = (INT16 *)bpc;
435         mems = (INT16 *)mem;
436         vLeft = dsb->volpan.dwTotalLeftAmpFactor;
437         if (nChannels > 1)
438                 vRight = dsb->volpan.dwTotalRightAmpFactor;
439         else
440                 vRight = vLeft;
441
442         switch (dsb->device->pwfx->wBitsPerSample) {
443         case 8:
444                 /* 8-bit WAV is unsigned, but we need to operate */
445                 /* on signed data for this to work properly */
446                 for (i = 0; i < len-1; i+=2) {
447                         *(bpc++) = (((*(mem++) - 128) * vLeft) >> 16) + 128;
448                         *(bpc++) = (((*(mem++) - 128) * vRight) >> 16) + 128;
449                 }
450                 if (len % 2 == 1 && nChannels == 1)
451                         *(bpc++) = (((*(mem++) - 128) * vLeft) >> 16) + 128;
452                 break;
453         case 16:
454                 /* 16-bit WAV is signed -- much better */
455                 for (i = 0; i < len-3; i += 4) {
456                         *(bps++) = (*(mems++) * vLeft) >> 16;
457                         *(bps++) = (*(mems++) * vRight) >> 16;
458                 }
459                 if (len % 4 == 2 && nChannels == 1)
460                         *(bps++) = ((INT)*(mems++) * vLeft) >> 16;
461                 break;
462         }
463         return dsb->device->tmp_buffer;
464 }
465
466 /**
467  * Mix (at most) the given number of bytes into the given position of the
468  * device buffer, from the secondary buffer "dsb" (starting at the current
469  * mix position for that buffer).
470  *
471  * Returns the number of bytes actually mixed into the device buffer. This
472  * will match fraglen unless the end of the secondary buffer is reached
473  * (and it is not looping).
474  *
475  * dsb  = the secondary buffer to mix from
476  * writepos = position (offset) in device buffer to write at
477  * fraglen = number of bytes to mix
478  */
479 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
480 {
481         INT len = fraglen;
482         BYTE *ibuf, *volbuf;
483         DWORD oldpos, mixbufpos;
484
485         TRACE("sec_mixpos=%d/%d\n", dsb->sec_mixpos, dsb->buflen);
486         TRACE("(%p,%d,%d)\n",dsb,writepos,fraglen);
487
488         if (len % dsb->device->pwfx->nBlockAlign) {
489                 INT nBlockAlign = dsb->device->pwfx->nBlockAlign;
490                 ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign);
491                 len -= len % nBlockAlign; /* data alignment */
492         }
493
494         /* Resample buffer to temporary buffer specifically allocated for this purpose, if needed */
495         oldpos = dsb->sec_mixpos;
496
497         DSOUND_MixToTemporary(dsb, len);
498         ibuf = dsb->device->tmp_buffer;
499
500         /* Apply volume if needed */
501         volbuf = DSOUND_MixerVol(dsb, len);
502         if (volbuf)
503                 ibuf = volbuf;
504
505         mixbufpos = DSOUND_bufpos_to_mixpos(dsb->device, writepos);
506         /* Now mix the temporary buffer into the devices main buffer */
507         if ((writepos + len) <= dsb->device->buflen)
508                 dsb->device->mixfunction(ibuf, dsb->device->mix_buffer + mixbufpos, len);
509         else
510         {
511                 DWORD todo = dsb->device->buflen - writepos;
512                 dsb->device->mixfunction(ibuf, dsb->device->mix_buffer + mixbufpos, todo);
513                 dsb->device->mixfunction(ibuf + todo, dsb->device->mix_buffer, len - todo);
514         }
515
516         /* check for notification positions */
517         if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&
518             dsb->state != STATE_STARTING) {
519                 INT ilen = DSOUND_BufPtrDiff(dsb->buflen, dsb->sec_mixpos, oldpos);
520                 DSOUND_CheckEvent(dsb, oldpos, ilen);
521         }
522
523         /* increase mix position */
524         dsb->primary_mixpos += len;
525         dsb->primary_mixpos %= dsb->device->buflen;
526
527         return len;
528 }
529
530 /**
531  * Mix some frames from the given secondary buffer "dsb" into the device
532  * primary buffer.
533  *
534  * dsb = the secondary buffer
535  * playpos = the current play position in the device buffer (primary buffer)
536  * writepos = the current safe-to-write position in the device buffer
537  * mixlen = the maximum number of bytes in the primary buffer to mix, from the
538  *          current writepos.
539  *
540  * Returns: the number of bytes beyond the writepos that were mixed.
541  */
542 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen)
543 {
544         /* The buffer's primary_mixpos may be before or after the device
545          * buffer's mixpos, but both must be ahead of writepos. */
546         DWORD primary_done;
547
548         TRACE("(%p,%d,%d)\n",dsb,writepos,mixlen);
549         TRACE("writepos=%d, primary_mixpos=%d, mixlen=%d\n", writepos, dsb->primary_mixpos, mixlen);
550         TRACE("looping=%d, leadin=%d\n", dsb->playflags, dsb->leadin);
551
552         /* If leading in, only mix about 20 ms, and 'skip' mixing the rest, for more fluid pointer advancement */
553         if (dsb->leadin && dsb->state == STATE_STARTING)
554         {
555                 if (mixlen > 2 * dsb->device->fraglen)
556                 {
557                         dsb->primary_mixpos += mixlen - 2 * dsb->device->fraglen;
558                         dsb->primary_mixpos %= dsb->device->buflen;
559                 }
560         }
561         dsb->leadin = FALSE;
562
563         /* calculate how much pre-buffering has already been done for this buffer */
564         primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos);
565
566         /* sanity */
567         if(mixlen < primary_done)
568         {
569                 /* Should *NEVER* happen */
570                 ERR("Fatal error. Under/Overflow? primary_done=%d, mixpos=%d/%d, primary_mixpos=%d, writepos=%d, mixlen=%d\n", primary_done,dsb->sec_mixpos, dsb->buflen, dsb->primary_mixpos, writepos, mixlen);
571                 dsb->primary_mixpos = writepos + mixlen;
572                 dsb->primary_mixpos %= dsb->device->buflen;
573                 return mixlen;
574         }
575
576         /* take into account already mixed data */
577         mixlen -= primary_done;
578
579         TRACE("primary_done=%d, mixlen (primary) = %i\n", primary_done, mixlen);
580
581         if (!mixlen)
582                 return primary_done;
583
584         /* First try to mix to the end of the buffer if possible
585          * Theoretically it would allow for better optimization
586         */
587         DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, mixlen);
588
589         /* re-calculate the primary done */
590         primary_done = DSOUND_BufPtrDiff(dsb->device->buflen, dsb->primary_mixpos, writepos);
591
592         TRACE("new primary_mixpos=%d, total mixed data=%d\n", dsb->primary_mixpos, primary_done);
593
594         /* Report back the total prebuffered amount for this buffer */
595         return primary_done;
596 }
597
598 /**
599  * For a DirectSoundDevice, go through all the currently playing buffers and
600  * mix them in to the device buffer.
601  *
602  * writepos = the current safe-to-write position in the primary buffer
603  * mixlen = the maximum amount to mix into the primary buffer
604  *          (beyond the current writepos)
605  * recover = true if the sound device may have been reset and the write
606  *           position in the device buffer changed
607  * all_stopped = reports back if all buffers have stopped
608  *
609  * Returns:  the length beyond the writepos that was mixed to.
610  */
611
612 static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, DWORD mixlen, BOOL recover, BOOL *all_stopped)
613 {
614         INT i;
615         IDirectSoundBufferImpl  *dsb;
616
617         /* unless we find a running buffer, all have stopped */
618         *all_stopped = TRUE;
619
620         TRACE("(%d,%d,%d)\n", writepos, mixlen, recover);
621         for (i = 0; i < device->nrofbuffers; i++) {
622                 dsb = device->buffers[i];
623
624                 TRACE("MixToPrimary for %p, state=%d\n", dsb, dsb->state);
625
626                 if (dsb->buflen && dsb->state) {
627                         TRACE("Checking %p, mixlen=%d\n", dsb, mixlen);
628                         RtlAcquireResourceShared(&dsb->lock, TRUE);
629                         /* if buffer is stopping it is stopped now */
630                         if (dsb->state == STATE_STOPPING) {
631                                 dsb->state = STATE_STOPPED;
632                                 DSOUND_CheckEvent(dsb, 0, 0);
633                         } else if (dsb->state != STATE_STOPPED) {
634
635                                 /* if recovering, reset the mix position */
636                                 if ((dsb->state == STATE_STARTING) || recover) {
637                                         dsb->primary_mixpos = writepos;
638                                 }
639
640                                 /* if the buffer was starting, it must be playing now */
641                                 if (dsb->state == STATE_STARTING)
642                                         dsb->state = STATE_PLAYING;
643
644                                 /* mix next buffer into the main buffer */
645                                 DSOUND_MixOne(dsb, writepos, mixlen);
646
647                                 *all_stopped = FALSE;
648                         }
649                         RtlReleaseResource(&dsb->lock);
650                 }
651         }
652 }
653
654 /**
655  * Add buffers to the emulated wave device system.
656  *
657  * device = The current dsound playback device
658  * force = If TRUE, the function will buffer up as many frags as possible,
659  *         even though and will ignore the actual state of the primary buffer.
660  *
661  * Returns:  None
662  */
663
664 static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force)
665 {
666         DWORD prebuf_frames, buf_offs_bytes, wave_fragpos;
667         int prebuf_frags;
668         BYTE *buffer;
669         HRESULT hr;
670
671         TRACE("(%p)\n", device);
672
673         /* calculate the current wave frag position */
674         wave_fragpos = (device->pwplay + device->pwqueue) % device->helfrags;
675
676         /* calculate the current wave write position */
677         buf_offs_bytes = wave_fragpos * device->fraglen;
678
679         TRACE("wave_fragpos = %i, buf_offs_bytes = %i, pwqueue = %i, prebuf = %i\n",
680                 wave_fragpos, buf_offs_bytes, device->pwqueue, device->prebuf);
681
682         if (!force)
683         {
684                 /* check remaining prebuffered frags */
685                 prebuf_frags = device->mixpos / device->fraglen;
686                 if (prebuf_frags == device->helfrags)
687                         --prebuf_frags;
688                 TRACE("wave_fragpos = %d, mixpos_frags = %d\n", wave_fragpos, prebuf_frags);
689                 if (prebuf_frags < wave_fragpos)
690                         prebuf_frags += device->helfrags;
691                 prebuf_frags -= wave_fragpos;
692                 TRACE("wanted prebuf_frags = %d\n", prebuf_frags);
693         }
694         else
695                 /* buffer the maximum amount of frags */
696                 prebuf_frags = device->prebuf;
697
698         /* limit to the queue we have left */
699         if ((prebuf_frags + device->pwqueue) > device->prebuf)
700                 prebuf_frags = device->prebuf - device->pwqueue;
701
702         TRACE("prebuf_frags = %i\n", prebuf_frags);
703
704         if(!prebuf_frags)
705                 return;
706
707         /* adjust queue */
708         device->pwqueue += prebuf_frags;
709
710         prebuf_frames = ((prebuf_frags + wave_fragpos > device->helfrags) ?
711                         (device->helfrags - wave_fragpos) :
712                         (prebuf_frags)) * device->fraglen / device->pwfx->nBlockAlign;
713
714         hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
715         if(FAILED(hr)){
716                 WARN("GetBuffer failed: %08x\n", hr);
717                 return;
718         }
719
720         memcpy(buffer, device->buffer + buf_offs_bytes,
721                         prebuf_frames * device->pwfx->nBlockAlign);
722
723         hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
724         if(FAILED(hr)){
725                 WARN("ReleaseBuffer failed: %08x\n", hr);
726                 return;
727         }
728
729         /* check if anything wrapped */
730         prebuf_frags = prebuf_frags + wave_fragpos - device->helfrags;
731         if(prebuf_frags > 0){
732                 prebuf_frames = prebuf_frags * device->fraglen / device->pwfx->nBlockAlign;
733
734                 hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
735                 if(FAILED(hr)){
736                         WARN("GetBuffer failed: %08x\n", hr);
737                         return;
738                 }
739
740                 memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign);
741
742                 hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
743                 if(FAILED(hr)){
744                         WARN("ReleaseBuffer failed: %08x\n", hr);
745                         return;
746                 }
747         }
748
749         TRACE("queue now = %i\n", device->pwqueue);
750 }
751
752 /**
753  * Perform mixing for a Direct Sound device. That is, go through all the
754  * secondary buffers (the sound bites currently playing) and mix them in
755  * to the primary buffer (the device buffer).
756  */
757 static void DSOUND_PerformMix(DirectSoundDevice *device)
758 {
759         UINT64 clock_pos, clock_freq, pos_bytes;
760         UINT delta_frags;
761         HRESULT hr;
762
763         TRACE("(%p)\n", device);
764
765         /* **** */
766         EnterCriticalSection(&device->mixlock);
767
768         hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
769         if(FAILED(hr)){
770                 WARN("GetFrequency failed: %08x\n", hr);
771         LeaveCriticalSection(&device->mixlock);
772                 return;
773         }
774
775         hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
776         if(FAILED(hr)){
777                 WARN("GetCurrentPadding failed: %08x\n", hr);
778         LeaveCriticalSection(&device->mixlock);
779                 return;
780         }
781
782         pos_bytes = (clock_pos * device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign) / clock_freq;
783
784         delta_frags = (pos_bytes - device->last_pos_bytes) / device->fraglen;
785         if(delta_frags > 0){
786                 device->pwplay += delta_frags;
787                 device->pwplay %= device->helfrags;
788                 device->pwqueue -= delta_frags;
789                 device->last_pos_bytes = pos_bytes - (pos_bytes % device->fraglen);
790         }
791
792         if (device->priolevel != DSSCL_WRITEPRIMARY) {
793                 BOOL recover = FALSE, all_stopped = FALSE;
794                 DWORD playpos, writepos, writelead, maxq, prebuff_max, prebuff_left, size1, size2, mixplaypos, mixplaypos2;
795                 LPVOID buf1, buf2;
796                 int nfiller;
797
798                 /* the sound of silence */
799                 nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
800
801                 /* get the position in the primary buffer */
802                 if (DSOUND_PrimaryGetPosition(device, &playpos, &writepos) != 0){
803                         LeaveCriticalSection(&(device->mixlock));
804                         return;
805                 }
806
807                 TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n",
808                         playpos,writepos,device->playpos,device->mixpos,device->buflen);
809                 assert(device->playpos < device->buflen);
810
811                 mixplaypos = DSOUND_bufpos_to_mixpos(device, device->playpos);
812                 mixplaypos2 = DSOUND_bufpos_to_mixpos(device, playpos);
813
814                 /* calc maximum prebuff */
815                 prebuff_max = (device->prebuf * device->fraglen);
816                 if (playpos + prebuff_max >= device->helfrags * device->fraglen)
817                         prebuff_max += device->buflen - device->helfrags * device->fraglen;
818
819                 /* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */
820                 prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
821                 writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos);
822
823                 /* check for underrun. underrun occurs when the write position passes the mix position
824                  * also wipe out just-played sound data */
825                 if((prebuff_left > prebuff_max) || (device->state == STATE_STOPPED) || (device->state == STATE_STARTING)){
826                         if (device->state == STATE_STOPPING || device->state == STATE_PLAYING)
827                                 WARN("Probable buffer underrun\n");
828                         else TRACE("Buffer starting or buffer underrun\n");
829
830                         /* recover mixing for all buffers */
831                         recover = TRUE;
832
833                         /* reset mix position to write position */
834                         device->mixpos = writepos;
835
836                         ZeroMemory(device->mix_buffer, device->mix_buffer_len);
837                         ZeroMemory(device->buffer, device->buflen);
838                 } else if (playpos < device->playpos) {
839                         buf1 = device->buffer + device->playpos;
840                         buf2 = device->buffer;
841                         size1 = device->buflen - device->playpos;
842                         size2 = playpos;
843                         FillMemory(device->mix_buffer + mixplaypos, device->mix_buffer_len - mixplaypos, 0);
844                         FillMemory(device->mix_buffer, mixplaypos2, 0);
845                         FillMemory(buf1, size1, nfiller);
846                         if (playpos && (!buf2 || !size2))
847                                 FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__, device->playpos, device->mixpos, playpos, writepos);
848                         FillMemory(buf2, size2, nfiller);
849                 } else {
850                         buf1 = device->buffer + device->playpos;
851                         buf2 = NULL;
852                         size1 = playpos - device->playpos;
853                         size2 = 0;
854                         FillMemory(device->mix_buffer + mixplaypos, mixplaypos2 - mixplaypos, 0);
855                         FillMemory(buf1, size1, nfiller);
856                 }
857                 device->playpos = playpos;
858
859                 /* find the maximum we can prebuffer from current write position */
860                 maxq = (writelead < prebuff_max) ? (prebuff_max - writelead) : 0;
861
862                 TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n",
863                         prebuff_left, device->prebuf, device->fraglen, prebuff_max, writelead);
864
865                 /* do the mixing */
866                 DSOUND_MixToPrimary(device, writepos, maxq, recover, &all_stopped);
867
868                 if (maxq + writepos > device->buflen)
869                 {
870                         DWORD todo = device->buflen - writepos;
871                         device->normfunction(device->mix_buffer + DSOUND_bufpos_to_mixpos(device, writepos), device->buffer + writepos, todo);
872                         device->normfunction(device->mix_buffer, device->buffer, maxq - todo);
873                 }
874                 else
875                         device->normfunction(device->mix_buffer + DSOUND_bufpos_to_mixpos(device, writepos), device->buffer + writepos, maxq);
876
877                 /* update the mix position, taking wrap-around into account */
878                 device->mixpos = writepos + maxq;
879                 device->mixpos %= device->buflen;
880
881                 /* update prebuff left */
882                 prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
883
884                 /* check if have a whole fragment */
885                 if (prebuff_left >= device->fraglen){
886
887                         /* update the wave queue */
888                         DSOUND_WaveQueue(device, FALSE);
889
890                         /* buffers are full. start playing if applicable */
891                         if(device->state == STATE_STARTING){
892                                 TRACE("started primary buffer\n");
893                                 if(DSOUND_PrimaryPlay(device) != DS_OK){
894                                         WARN("DSOUND_PrimaryPlay failed\n");
895                                 }
896                                 else{
897                                         /* we are playing now */
898                                         device->state = STATE_PLAYING;
899                                 }
900                         }
901
902                         /* buffers are full. start stopping if applicable */
903                         if(device->state == STATE_STOPPED){
904                                 TRACE("restarting primary buffer\n");
905                                 if(DSOUND_PrimaryPlay(device) != DS_OK){
906                                         WARN("DSOUND_PrimaryPlay failed\n");
907                                 }
908                                 else{
909                                         /* start stopping again. as soon as there is no more data, it will stop */
910                                         device->state = STATE_STOPPING;
911                                 }
912                         }
913                 }
914
915                 /* if device was stopping, its for sure stopped when all buffers have stopped */
916                 else if((all_stopped == TRUE) && (device->state == STATE_STOPPING)){
917                         TRACE("All buffers have stopped. Stopping primary buffer\n");
918                         device->state = STATE_STOPPED;
919
920                         /* stop the primary buffer now */
921                         DSOUND_PrimaryStop(device);
922                 }
923
924         } else {
925
926                 DSOUND_WaveQueue(device, TRUE);
927
928                 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
929                 if (device->state == STATE_STARTING) {
930                         if (DSOUND_PrimaryPlay(device) != DS_OK)
931                                 WARN("DSOUND_PrimaryPlay failed\n");
932                         else
933                                 device->state = STATE_PLAYING;
934                 }
935                 else if (device->state == STATE_STOPPING) {
936                         if (DSOUND_PrimaryStop(device) != DS_OK)
937                                 WARN("DSOUND_PrimaryStop failed\n");
938                         else
939                                 device->state = STATE_STOPPED;
940                 }
941         }
942
943         LeaveCriticalSection(&(device->mixlock));
944         /* **** */
945 }
946
947 void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
948                            DWORD_PTR dw1, DWORD_PTR dw2)
949 {
950         DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
951         DWORD start_time =  GetTickCount();
952         DWORD end_time;
953         TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
954         TRACE("entering at %d\n", start_time);
955
956         RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
957
958         if (device->ref)
959                 DSOUND_PerformMix(device);
960
961         RtlReleaseResource(&(device->buffer_list_lock));
962
963         end_time = GetTickCount();
964         TRACE("completed processing at %d, duration = %d\n", end_time, end_time - start_time);
965 }