dsound: Resample to float, not device format.
[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 /**
100  * Recalculate the size for temporary buffer, and new writelead
101  * Should be called when one of the following things occur:
102  * - Primary buffer format is changed
103  * - This buffer format (frequency) is changed
104  */
105 void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
106 {
107         DWORD ichannels = dsb->pwfx->nChannels;
108         DWORD ochannels = dsb->device->pwfx->nChannels;
109         WAVEFORMATEXTENSIBLE *pwfxe;
110         BOOL ieee = FALSE;
111
112         TRACE("(%p)\n",dsb);
113
114         pwfxe = (WAVEFORMATEXTENSIBLE *) dsb->pwfx;
115
116         if ((pwfxe->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) || ((pwfxe->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE)
117             && (IsEqualGUID(&pwfxe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))))
118                 ieee = TRUE;
119
120         /**
121          * Recalculate FIR step and gain.
122          *
123          * firstep says how many points of the FIR exist per one
124          * sample in the secondary buffer. firgain specifies what
125          * to multiply the FIR output by in order to attenuate it correctly.
126          */
127         if (dsb->freqAdjust > 1.0f) {
128                 /**
129                  * Yes, round it a bit to make sure that the
130                  * linear interpolation factor never changes.
131                  */
132                 dsb->firstep = ceil(fir_step / dsb->freqAdjust);
133         } else {
134                 dsb->firstep = fir_step;
135         }
136         dsb->firgain = (float)dsb->firstep / fir_step;
137
138         /* calculate the 10ms write lead */
139         dsb->writelead = (dsb->freq / 100) * dsb->pwfx->nBlockAlign;
140
141         dsb->freqAcc = 0;
142
143         dsb->get_aux = ieee ? getbpp[4] : getbpp[dsb->pwfx->wBitsPerSample/8 - 1];
144         dsb->put_aux = putieee32;
145
146         dsb->get = dsb->get_aux;
147         dsb->put = dsb->put_aux;
148
149         if (ichannels == ochannels)
150         {
151                 dsb->mix_channels = ichannels;
152                 if (ichannels > 32) {
153                         FIXME("Copying %u channels is unsupported, limiting to first 32\n", ichannels);
154                         dsb->mix_channels = 32;
155                 }
156         }
157         else if (ichannels == 1)
158         {
159                 dsb->mix_channels = 1;
160                 dsb->put = put_mono2stereo;
161         }
162         else if (ochannels == 1)
163         {
164                 dsb->mix_channels = 1;
165                 dsb->get = get_mono;
166         }
167         else
168         {
169                 if (ichannels > 2)
170                         FIXME("Conversion from %u to %u channels is not implemented, falling back to stereo\n", ichannels, ochannels);
171                 dsb->mix_channels = 2;
172         }
173 }
174
175 /**
176  * Check for application callback requests for when the play position
177  * reaches certain points.
178  *
179  * The offsets that will be triggered will be those between the recorded
180  * "last played" position for the buffer (i.e. dsb->playpos) and "len" bytes
181  * beyond that position.
182  */
183 void DSOUND_CheckEvent(const IDirectSoundBufferImpl *dsb, DWORD playpos, int len)
184 {
185         int                     i;
186         DWORD                   offset;
187         LPDSBPOSITIONNOTIFY     event;
188         TRACE("(%p,%d)\n",dsb,len);
189
190         if (dsb->nrofnotifies == 0)
191                 return;
192
193         TRACE("(%p) buflen = %d, playpos = %d, len = %d\n",
194                 dsb, dsb->buflen, playpos, len);
195         for (i = 0; i < dsb->nrofnotifies ; i++) {
196                 event = dsb->notifies + i;
197                 offset = event->dwOffset;
198                 TRACE("checking %d, position %d, event = %p\n",
199                         i, offset, event->hEventNotify);
200                 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
201                 /* OK. [Inside DirectX, p274] */
202                 /* Windows does not seem to enforce this, and some apps rely */
203                 /* on that, so we can't stop there. */
204                 /*  */
205                 /* This also means we can't sort the entries by offset, */
206                 /* because DSBPN_OFFSETSTOP == -1 */
207                 if (offset == DSBPN_OFFSETSTOP) {
208                         if (dsb->state == STATE_STOPPED) {
209                                 SetEvent(event->hEventNotify);
210                                 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
211                         }
212                         continue;
213                 }
214                 if ((playpos + len) >= dsb->buflen) {
215                         if ((offset < ((playpos + len) % dsb->buflen)) ||
216                             (offset >= playpos)) {
217                                 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
218                                 SetEvent(event->hEventNotify);
219                         }
220                 } else {
221                         if ((offset >= playpos) && (offset < (playpos + len))) {
222                                 TRACE("signalled event %p (%d)\n", event->hEventNotify, i);
223                                 SetEvent(event->hEventNotify);
224                         }
225                 }
226         }
227 }
228
229 static inline float get_current_sample(const IDirectSoundBufferImpl *dsb,
230         DWORD mixpos, DWORD channel)
231 {
232     if (mixpos >= dsb->buflen && !(dsb->playflags & DSBPLAY_LOOPING))
233         return 0.0f;
234     return dsb->get(dsb, mixpos % dsb->buflen, channel);
235 }
236
237 static UINT cp_fields_noresample(IDirectSoundBufferImpl *dsb, UINT count)
238 {
239     UINT istride = dsb->pwfx->nBlockAlign;
240     UINT ostride = dsb->device->pwfx->nChannels * sizeof(float);
241     DWORD channel, i;
242     for (i = 0; i < count; i++)
243         for (channel = 0; channel < dsb->mix_channels; channel++)
244             dsb->put(dsb, i * ostride, channel, get_current_sample(dsb,
245                     dsb->sec_mixpos + i * istride, channel));
246     return count;
247 }
248
249 static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, float *freqAcc)
250 {
251     UINT i, channel;
252     UINT istride = dsb->pwfx->nBlockAlign;
253     UINT ostride = dsb->device->pwfx->nChannels * sizeof(float);
254
255     float freqAdjust = dsb->freqAdjust;
256     float freqAcc_start = *freqAcc;
257     float freqAcc_end = freqAcc_start + count * freqAdjust;
258     UINT dsbfirstep = dsb->firstep;
259     UINT channels = dsb->mix_channels;
260     UINT max_ipos = freqAcc_start + count * freqAdjust;
261
262     UINT fir_cachesize = (fir_len + dsbfirstep - 2) / dsbfirstep;
263     UINT required_input = max_ipos + fir_cachesize;
264
265     float* intermediate = HeapAlloc(GetProcessHeap(), 0,
266             sizeof(float) * required_input * channels);
267
268     float* fir_copy = HeapAlloc(GetProcessHeap(), 0,
269             sizeof(float) * fir_cachesize);
270
271     /* Important: this buffer MUST be non-interleaved
272      * if you want -msse3 to have any effect.
273      * This is good for CPU cache effects, too.
274      */
275     float* itmp = intermediate;
276     for (channel = 0; channel < channels; channel++)
277         for (i = 0; i < required_input; i++)
278             *(itmp++) = get_current_sample(dsb,
279                     dsb->sec_mixpos + i * istride, channel);
280
281     for(i = 0; i < count; ++i) {
282         float total_fir_steps = (freqAcc_start + i * freqAdjust) * dsbfirstep;
283         UINT int_fir_steps = total_fir_steps;
284         UINT ipos = int_fir_steps / dsbfirstep;
285
286         UINT idx = (ipos + 1) * dsbfirstep - int_fir_steps - 1;
287         float rem = int_fir_steps + 1.0 - total_fir_steps;
288
289         int fir_used = 0;
290         while (idx < fir_len - 1) {
291             fir_copy[fir_used++] = fir[idx] * (1.0 - rem) + fir[idx + 1] * rem;
292             idx += dsb->firstep;
293         }
294
295         assert(fir_used <= fir_cachesize);
296         assert(ipos + fir_used <= required_input);
297
298         for (channel = 0; channel < dsb->mix_channels; channel++) {
299             int j;
300             float sum = 0.0;
301             float* cache = &intermediate[channel * required_input + ipos];
302             for (j = 0; j < fir_used; j++)
303                 sum += fir_copy[j] * cache[j];
304             dsb->put(dsb, i * ostride, channel, sum * dsb->firgain);
305         }
306     }
307
308     freqAcc_end -= (int)freqAcc_end;
309     *freqAcc = freqAcc_end;
310
311     HeapFree(GetProcessHeap(), 0, fir_copy);
312     HeapFree(GetProcessHeap(), 0, intermediate);
313
314     return max_ipos;
315 }
316
317 static void cp_fields(IDirectSoundBufferImpl *dsb, UINT count, float *freqAcc)
318 {
319     DWORD ipos, adv;
320
321     if (dsb->freqAdjust == 1.0)
322         adv = cp_fields_noresample(dsb, count); /* *freqAcc is unmodified */
323     else
324         adv = cp_fields_resample(dsb, count, freqAcc);
325
326     ipos = dsb->sec_mixpos + adv * dsb->pwfx->nBlockAlign;
327     if (ipos >= dsb->buflen) {
328         if (dsb->playflags & DSBPLAY_LOOPING)
329             ipos %= dsb->buflen;
330         else {
331             ipos = 0;
332             dsb->state = STATE_STOPPED;
333         }
334     }
335
336     dsb->sec_mixpos = ipos;
337 }
338
339 /**
340  * Calculate the distance between two buffer offsets, taking wraparound
341  * into account.
342  */
343 static inline DWORD DSOUND_BufPtrDiff(DWORD buflen, DWORD ptr1, DWORD ptr2)
344 {
345 /* If these asserts fail, the problem is not here, but in the underlying code */
346         assert(ptr1 < buflen);
347         assert(ptr2 < buflen);
348         if (ptr1 >= ptr2) {
349                 return ptr1 - ptr2;
350         } else {
351                 return buflen + ptr1 - ptr2;
352         }
353 }
354 /**
355  * Mix at most the given amount of data into the allocated temporary buffer
356  * of the given secondary buffer, starting from the dsb's first currently
357  * unsampled frame (writepos), translating frequency (pitch), stereo/mono
358  * and bits-per-sample so that it is ideal for the primary buffer.
359  * Doesn't perform any mixing - this is a straight copy/convert operation.
360  *
361  * dsb = the secondary buffer
362  * writepos = Starting position of changed buffer
363  * len = number of bytes to resample from writepos
364  *
365  * NOTE: writepos + len <= buflen. When called by mixer, MixOne makes sure of this.
366  */
367 static void DSOUND_MixToTemporary(IDirectSoundBufferImpl *dsb, DWORD frames)
368 {
369         UINT size_bytes = frames * sizeof(float) * dsb->device->pwfx->nChannels;
370
371         if (dsb->device->tmp_buffer_len < size_bytes || !dsb->device->tmp_buffer)
372         {
373                 dsb->device->tmp_buffer_len = size_bytes;
374                 if (dsb->device->tmp_buffer)
375                         dsb->device->tmp_buffer = HeapReAlloc(GetProcessHeap(), 0, dsb->device->tmp_buffer, size_bytes);
376                 else
377                         dsb->device->tmp_buffer = HeapAlloc(GetProcessHeap(), 0, size_bytes);
378         }
379
380         cp_fields(dsb, frames, &dsb->freqAcc);
381 }
382
383 static void DSOUND_MixerVol(const IDirectSoundBufferImpl *dsb, INT frames)
384 {
385         INT     i;
386         float vLeft, vRight;
387         UINT channels = dsb->device->pwfx->nChannels, chan;
388
389         TRACE("(%p,%d)\n",dsb,frames);
390         TRACE("left = %x, right = %x\n", dsb->volpan.dwTotalLeftAmpFactor,
391                 dsb->volpan.dwTotalRightAmpFactor);
392
393         if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
394             (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
395              !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
396                 return; /* Nothing to do */
397
398         if (channels != 1 && channels != 2)
399         {
400                 FIXME("There is no support for %u channels\n", channels);
401                 return;
402         }
403
404         vLeft = dsb->volpan.dwTotalLeftAmpFactor / ((float)0xFFFF);
405         vRight = dsb->volpan.dwTotalRightAmpFactor / ((float)0xFFFF);
406         for(i = 0; i < frames; ++i){
407                 for(chan = 0; chan < channels; ++chan){
408                         if(chan == 0)
409                                 dsb->device->tmp_buffer[i * channels + chan] *= vLeft;
410                         else
411                                 dsb->device->tmp_buffer[i * channels + chan] *= vRight;
412                 }
413         }
414 }
415
416 /**
417  * Mix (at most) the given number of bytes into the given position of the
418  * device buffer, from the secondary buffer "dsb" (starting at the current
419  * mix position for that buffer).
420  *
421  * Returns the number of bytes actually mixed into the device buffer. This
422  * will match fraglen unless the end of the secondary buffer is reached
423  * (and it is not looping).
424  *
425  * dsb  = the secondary buffer to mix from
426  * writepos = position (offset) in device buffer to write at
427  * fraglen = number of bytes to mix
428  */
429 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
430 {
431         INT len = fraglen;
432         float *ibuf;
433         DWORD oldpos;
434         UINT frames = fraglen / dsb->device->pwfx->nBlockAlign;
435
436         TRACE("sec_mixpos=%d/%d\n", dsb->sec_mixpos, dsb->buflen);
437         TRACE("(%p,%d,%d)\n",dsb,writepos,fraglen);
438
439         if (len % dsb->device->pwfx->nBlockAlign) {
440                 INT nBlockAlign = dsb->device->pwfx->nBlockAlign;
441                 ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign);
442                 len -= len % nBlockAlign; /* data alignment */
443         }
444
445         /* Resample buffer to temporary buffer specifically allocated for this purpose, if needed */
446         oldpos = dsb->sec_mixpos;
447
448         DSOUND_MixToTemporary(dsb, frames);
449         ibuf = dsb->device->tmp_buffer;
450
451         /* Apply volume if needed */
452         DSOUND_MixerVol(dsb, frames);
453
454         mixieee32(ibuf, dsb->device->mix_buffer, frames * dsb->device->pwfx->nChannels);
455
456         /* check for notification positions */
457         if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&
458             dsb->state != STATE_STARTING) {
459                 INT ilen = DSOUND_BufPtrDiff(dsb->buflen, dsb->sec_mixpos, oldpos);
460                 DSOUND_CheckEvent(dsb, oldpos, ilen);
461         }
462
463         return len;
464 }
465
466 /**
467  * Mix some frames from the given secondary buffer "dsb" into the device
468  * primary buffer.
469  *
470  * dsb = the secondary buffer
471  * playpos = the current play position in the device buffer (primary buffer)
472  * writepos = the current safe-to-write position in the device buffer
473  * mixlen = the maximum number of bytes in the primary buffer to mix, from the
474  *          current writepos.
475  *
476  * Returns: the number of bytes beyond the writepos that were mixed.
477  */
478 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD mixlen)
479 {
480         DWORD primary_done = 0;
481
482         TRACE("(%p,%d,%d)\n",dsb,writepos,mixlen);
483         TRACE("writepos=%d, mixlen=%d\n", writepos, mixlen);
484         TRACE("looping=%d, leadin=%d\n", dsb->playflags, dsb->leadin);
485
486         /* If leading in, only mix about 20 ms, and 'skip' mixing the rest, for more fluid pointer advancement */
487         /* FIXME: Is this needed? */
488         if (dsb->leadin && dsb->state == STATE_STARTING) {
489                 if (mixlen > 2 * dsb->device->fraglen) {
490                         primary_done = mixlen - 2 * dsb->device->fraglen;
491                         mixlen = 2 * dsb->device->fraglen;
492                         writepos += primary_done;
493                         dsb->sec_mixpos += (primary_done / dsb->device->pwfx->nBlockAlign) *
494                                 dsb->pwfx->nBlockAlign * dsb->freqAdjust;
495                 }
496         }
497
498         dsb->leadin = FALSE;
499
500         TRACE("mixlen (primary) = %i\n", mixlen);
501
502         /* First try to mix to the end of the buffer if possible
503          * Theoretically it would allow for better optimization
504         */
505         primary_done += DSOUND_MixInBuffer(dsb, writepos, mixlen);
506
507         TRACE("total mixed data=%d\n", primary_done);
508
509         /* Report back the total prebuffered amount for this buffer */
510         return primary_done;
511 }
512
513 /**
514  * For a DirectSoundDevice, go through all the currently playing buffers and
515  * mix them in to the device buffer.
516  *
517  * writepos = the current safe-to-write position in the primary buffer
518  * mixlen = the maximum amount to mix into the primary buffer
519  *          (beyond the current writepos)
520  * recover = true if the sound device may have been reset and the write
521  *           position in the device buffer changed
522  * all_stopped = reports back if all buffers have stopped
523  *
524  * Returns:  the length beyond the writepos that was mixed to.
525  */
526
527 static void DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos, DWORD mixlen, BOOL recover, BOOL *all_stopped)
528 {
529         INT i;
530         IDirectSoundBufferImpl  *dsb;
531
532         /* unless we find a running buffer, all have stopped */
533         *all_stopped = TRUE;
534
535         TRACE("(%d,%d,%d)\n", writepos, mixlen, recover);
536         for (i = 0; i < device->nrofbuffers; i++) {
537                 dsb = device->buffers[i];
538
539                 TRACE("MixToPrimary for %p, state=%d\n", dsb, dsb->state);
540
541                 if (dsb->buflen && dsb->state) {
542                         TRACE("Checking %p, mixlen=%d\n", dsb, mixlen);
543                         RtlAcquireResourceShared(&dsb->lock, TRUE);
544                         /* if buffer is stopping it is stopped now */
545                         if (dsb->state == STATE_STOPPING) {
546                                 dsb->state = STATE_STOPPED;
547                                 DSOUND_CheckEvent(dsb, 0, 0);
548                         } else if (dsb->state != STATE_STOPPED) {
549
550                                 /* if the buffer was starting, it must be playing now */
551                                 if (dsb->state == STATE_STARTING)
552                                         dsb->state = STATE_PLAYING;
553
554                                 /* mix next buffer into the main buffer */
555                                 DSOUND_MixOne(dsb, writepos, mixlen);
556
557                                 *all_stopped = FALSE;
558                         }
559                         RtlReleaseResource(&dsb->lock);
560                 }
561         }
562 }
563
564 /**
565  * Add buffers to the emulated wave device system.
566  *
567  * device = The current dsound playback device
568  * force = If TRUE, the function will buffer up as many frags as possible,
569  *         even though and will ignore the actual state of the primary buffer.
570  *
571  * Returns:  None
572  */
573
574 static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force)
575 {
576         DWORD prebuf_frames, prebuf_bytes, read_offs_bytes;
577         BYTE *buffer;
578         HRESULT hr;
579
580         TRACE("(%p)\n", device);
581
582         read_offs_bytes = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen;
583
584         TRACE("read_offs_bytes = %u, playing_offs_bytes = %u, in_mmdev_bytes: %u, prebuf = %u\n",
585                 read_offs_bytes, device->playing_offs_bytes, device->in_mmdev_bytes, device->prebuf);
586
587         if (!force)
588         {
589                 if(device->mixpos < device->playing_offs_bytes)
590                         prebuf_bytes = device->mixpos + device->buflen - device->playing_offs_bytes;
591                 else
592                         prebuf_bytes = device->mixpos - device->playing_offs_bytes;
593         }
594         else
595                 /* buffer the maximum amount of frags */
596                 prebuf_bytes = device->prebuf * device->fraglen;
597
598         /* limit to the queue we have left */
599         if(device->in_mmdev_bytes + prebuf_bytes > device->prebuf * device->fraglen)
600                 prebuf_bytes = device->prebuf * device->fraglen - device->in_mmdev_bytes;
601
602         TRACE("prebuf_bytes = %u\n", prebuf_bytes);
603
604         if(!prebuf_bytes)
605                 return;
606
607         device->in_mmdev_bytes += prebuf_bytes;
608
609         if(prebuf_bytes + read_offs_bytes > device->buflen){
610                 DWORD chunk_bytes = device->buflen - read_offs_bytes;
611                 prebuf_frames = chunk_bytes / device->pwfx->nBlockAlign;
612                 prebuf_bytes -= chunk_bytes;
613         }else{
614                 prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign;
615                 prebuf_bytes = 0;
616         }
617
618         hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
619         if(FAILED(hr)){
620                 WARN("GetBuffer failed: %08x\n", hr);
621                 return;
622         }
623
624         memcpy(buffer, device->buffer + read_offs_bytes,
625                         prebuf_frames * device->pwfx->nBlockAlign);
626
627         hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
628         if(FAILED(hr)){
629                 WARN("ReleaseBuffer failed: %08x\n", hr);
630                 return;
631         }
632
633         /* check if anything wrapped */
634         if(prebuf_bytes > 0){
635                 prebuf_frames = prebuf_bytes / device->pwfx->nBlockAlign;
636
637                 hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer);
638                 if(FAILED(hr)){
639                         WARN("GetBuffer failed: %08x\n", hr);
640                         return;
641                 }
642
643                 memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign);
644
645                 hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0);
646                 if(FAILED(hr)){
647                         WARN("ReleaseBuffer failed: %08x\n", hr);
648                         return;
649                 }
650         }
651
652         TRACE("in_mmdev_bytes now = %i\n", device->in_mmdev_bytes);
653 }
654
655 /**
656  * Perform mixing for a Direct Sound device. That is, go through all the
657  * secondary buffers (the sound bites currently playing) and mix them in
658  * to the primary buffer (the device buffer).
659  *
660  * The mixing procedure goes:
661  *
662  * secondary->buffer (secondary format)
663  *   =[Resample]=> device->tmp_buffer (float format)
664  *   =[Volume]=> device->tmp_buffer (float format)
665  *   =[Mix]=> device->mix_buffer (float format)
666  *   =[Reformat]=> device->buffer (device format)
667  */
668 static void DSOUND_PerformMix(DirectSoundDevice *device)
669 {
670         UINT64 clock_pos, clock_freq, pos_bytes;
671         UINT delta_frags;
672         HRESULT hr;
673
674         TRACE("(%p)\n", device);
675
676         /* **** */
677         EnterCriticalSection(&device->mixlock);
678
679         hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
680         if(FAILED(hr)){
681                 WARN("GetFrequency failed: %08x\n", hr);
682         LeaveCriticalSection(&device->mixlock);
683                 return;
684         }
685
686         hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
687         if(FAILED(hr)){
688                 WARN("GetCurrentPadding failed: %08x\n", hr);
689         LeaveCriticalSection(&device->mixlock);
690                 return;
691         }
692
693         pos_bytes = (clock_pos * device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign) / clock_freq;
694
695         delta_frags = (pos_bytes - device->last_pos_bytes) / device->fraglen;
696         if(delta_frags > 0){
697                 device->playing_offs_bytes += delta_frags * device->fraglen;
698                 device->playing_offs_bytes %= device->buflen;
699                 device->in_mmdev_bytes -= delta_frags * device->fraglen;
700                 device->last_pos_bytes = pos_bytes - (pos_bytes % device->fraglen);
701         }
702
703         if (device->priolevel != DSSCL_WRITEPRIMARY) {
704                 BOOL recover = FALSE, all_stopped = FALSE;
705                 DWORD playpos, writepos, writelead, maxq, prebuff_max, prebuff_left, size1, size2;
706                 LPVOID buf1, buf2;
707                 int nfiller;
708
709                 /* the sound of silence */
710                 nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;
711
712                 /* get the position in the primary buffer */
713                 if (DSOUND_PrimaryGetPosition(device, &playpos, &writepos) != 0){
714                         LeaveCriticalSection(&(device->mixlock));
715                         return;
716                 }
717
718                 TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n",
719                         playpos,writepos,device->playpos,device->mixpos,device->buflen);
720                 assert(device->playpos < device->buflen);
721
722                 /* calc maximum prebuff */
723                 prebuff_max = (device->prebuf * device->fraglen);
724
725                 /* check how close we are to an underrun. It occurs when the writepos overtakes the mixpos */
726                 prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
727                 writelead = DSOUND_BufPtrDiff(device->buflen, writepos, playpos);
728
729                 /* check for underrun. underrun occurs when the write position passes the mix position
730                  * also wipe out just-played sound data */
731                 if((prebuff_left > prebuff_max) || (device->state == STATE_STOPPED) || (device->state == STATE_STARTING)){
732                         if (device->state == STATE_STOPPING || device->state == STATE_PLAYING)
733                                 WARN("Probable buffer underrun\n");
734                         else TRACE("Buffer starting or buffer underrun\n");
735
736                         /* recover mixing for all buffers */
737                         recover = TRUE;
738
739                         /* reset mix position to write position */
740                         device->mixpos = writepos;
741
742                         ZeroMemory(device->buffer, device->buflen);
743                 } else if (playpos < device->playpos) {
744                         buf1 = device->buffer + device->playpos;
745                         buf2 = device->buffer;
746                         size1 = device->buflen - device->playpos;
747                         size2 = playpos;
748                         FillMemory(buf1, size1, nfiller);
749                         if (playpos && (!buf2 || !size2))
750                                 FIXME("%d: (%d, %d)=>(%d, %d) There should be an additional buffer here!!\n", __LINE__, device->playpos, device->mixpos, playpos, writepos);
751                         FillMemory(buf2, size2, nfiller);
752                 } else {
753                         buf1 = device->buffer + device->playpos;
754                         buf2 = NULL;
755                         size1 = playpos - device->playpos;
756                         size2 = 0;
757                         FillMemory(buf1, size1, nfiller);
758                 }
759                 device->playpos = playpos;
760
761                 /* find the maximum we can prebuffer from current write position */
762                 maxq = (writelead < prebuff_max) ? (prebuff_max - writelead) : 0;
763
764                 TRACE("prebuff_left = %d, prebuff_max = %dx%d=%d, writelead=%d\n",
765                         prebuff_left, device->prebuf, device->fraglen, prebuff_max, writelead);
766
767                 ZeroMemory(device->mix_buffer, device->mix_buffer_len);
768
769                 /* do the mixing */
770                 DSOUND_MixToPrimary(device, writepos, maxq, recover, &all_stopped);
771
772                 if (maxq + writepos > device->buflen)
773                 {
774                         DWORD todo = device->buflen - writepos;
775                         DWORD offs_float = (todo / device->pwfx->nBlockAlign) * device->pwfx->nChannels;
776                         device->normfunction(device->mix_buffer, device->buffer + writepos, todo);
777                         device->normfunction(device->mix_buffer + offs_float, device->buffer, maxq - todo);
778                 }
779                 else
780                         device->normfunction(device->mix_buffer, device->buffer + writepos, maxq);
781
782                 /* update the mix position, taking wrap-around into account */
783                 device->mixpos = writepos + maxq;
784                 device->mixpos %= device->buflen;
785
786                 /* update prebuff left */
787                 prebuff_left = DSOUND_BufPtrDiff(device->buflen, device->mixpos, playpos);
788
789                 /* check if have a whole fragment */
790                 if (prebuff_left >= device->fraglen){
791
792                         /* update the wave queue */
793                         DSOUND_WaveQueue(device, FALSE);
794
795                         /* buffers are full. start playing if applicable */
796                         if(device->state == STATE_STARTING){
797                                 TRACE("started primary buffer\n");
798                                 if(DSOUND_PrimaryPlay(device) != DS_OK){
799                                         WARN("DSOUND_PrimaryPlay failed\n");
800                                 }
801                                 else{
802                                         /* we are playing now */
803                                         device->state = STATE_PLAYING;
804                                 }
805                         }
806
807                         /* buffers are full. start stopping if applicable */
808                         if(device->state == STATE_STOPPED){
809                                 TRACE("restarting primary buffer\n");
810                                 if(DSOUND_PrimaryPlay(device) != DS_OK){
811                                         WARN("DSOUND_PrimaryPlay failed\n");
812                                 }
813                                 else{
814                                         /* start stopping again. as soon as there is no more data, it will stop */
815                                         device->state = STATE_STOPPING;
816                                 }
817                         }
818                 }
819
820                 /* if device was stopping, its for sure stopped when all buffers have stopped */
821                 else if((all_stopped == TRUE) && (device->state == STATE_STOPPING)){
822                         TRACE("All buffers have stopped. Stopping primary buffer\n");
823                         device->state = STATE_STOPPED;
824
825                         /* stop the primary buffer now */
826                         DSOUND_PrimaryStop(device);
827                 }
828
829         } else {
830
831                 DSOUND_WaveQueue(device, TRUE);
832
833                 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
834                 if (device->state == STATE_STARTING) {
835                         if (DSOUND_PrimaryPlay(device) != DS_OK)
836                                 WARN("DSOUND_PrimaryPlay failed\n");
837                         else
838                                 device->state = STATE_PLAYING;
839                 }
840                 else if (device->state == STATE_STOPPING) {
841                         if (DSOUND_PrimaryStop(device) != DS_OK)
842                                 WARN("DSOUND_PrimaryStop failed\n");
843                         else
844                                 device->state = STATE_STOPPED;
845                 }
846         }
847
848         LeaveCriticalSection(&(device->mixlock));
849         /* **** */
850 }
851
852 void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
853                            DWORD_PTR dw1, DWORD_PTR dw2)
854 {
855         DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
856         DWORD start_time =  GetTickCount();
857         DWORD end_time;
858         TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
859         TRACE("entering at %d\n", start_time);
860
861         RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
862
863         if (device->ref)
864                 DSOUND_PerformMix(device);
865
866         RtlReleaseResource(&(device->buffer_list_lock));
867
868         end_time = GetTickCount();
869         TRACE("completed processing at %d, duration = %d\n", end_time, end_time - start_time);
870 }