dsound/tests: Fix a failing capture test on win2k.
[wine] / dlls / dsound / primary.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <stdarg.h>
23
24 #define NONAMELESSSTRUCT
25 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "mmsystem.h"
30 #include "winternl.h"
31 #include "mmddk.h"
32 #include "wine/debug.h"
33 #include "dsound.h"
34 #include "dsdriver.h"
35 #include "dsound_private.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
38
39 /** Calculate how long a fragment length of about 10 ms should be in frames
40  *
41  * nSamplesPerSec: Frequency rate in samples per second
42  * nBlockAlign: Size of a single blockalign
43  *
44  * Returns:
45  * Size in bytes of a single fragment
46  */
47 DWORD DSOUND_fraglen(DWORD nSamplesPerSec, DWORD nBlockAlign)
48 {
49     DWORD fraglen = 256 * nBlockAlign;
50
51     /* Compensate for only being roughly accurate */
52     if (nSamplesPerSec <= 26000)
53         fraglen /= 2;
54
55     if (nSamplesPerSec <= 10000)
56         fraglen /= 2;
57
58     if (nSamplesPerSec >= 80000)
59         fraglen *= 2;
60
61     return fraglen;
62 }
63
64 static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
65 {
66     TRACE("(%p)\n", device);
67
68     device->fraglen = DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign);
69     device->helfrags = device->buflen / device->fraglen;
70     TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
71
72     if (device->hwbuf && device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD)
73         device->writelead = 0;
74     else
75         /* calculate the 10ms write lead */
76         device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
77 }
78
79 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
80 {
81         HRESULT hres = DS_OK;
82         TRACE("(%p, %d)\n", device, forcewave);
83
84         if (device->driver)
85         {
86                 IDsDriver_Close(device->driver);
87                 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
88                         waveOutClose(device->hwo);
89                 IDsDriver_Release(device->driver);
90                 device->driver = NULL;
91                 device->buffer = NULL;
92                 device->hwo = 0;
93         }
94         else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
95                 waveOutClose(device->hwo);
96
97         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
98         if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
99                 waveOutMessage((HWAVEOUT)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);
100
101         /* Get driver description */
102         if (device->driver) {
103                 DWORD wod = device->drvdesc.dnDevNode;
104                 hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
105                 device->drvdesc.dnDevNode = wod;
106                 if (FAILED(hres)) {
107                         WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
108                         IDsDriver_Release(device->driver);
109                         device->driver = NULL;
110                 }
111         }
112
113         /* if no DirectSound interface available, use WINMM API instead */
114         if (!device->driver)
115                 device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
116
117         if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
118         {
119                 DWORD flags = CALLBACK_FUNCTION;
120
121                 if (device->driver)
122                         flags |= WAVE_DIRECTSOUND;
123
124                 hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, flags));
125                 if (FAILED(hres)) {
126                         WARN("waveOutOpen failed\n");
127                         if (device->driver)
128                         {
129                                 IDsDriver_Release(device->driver);
130                                 device->driver = NULL;
131                         }
132                         return hres;
133                 }
134         }
135
136         if (device->driver)
137                 hres = IDsDriver_Open(device->driver);
138
139         return hres;
140 }
141
142 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
143 {
144         DWORD buflen;
145         HRESULT err = DS_OK;
146         TRACE("(%p)\n", device);
147
148         /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
149            on windows this size is always fixed (tested on win-xp) */
150         if (!device->buflen)
151                 device->buflen = ds_hel_buflen;
152         buflen = device->buflen;
153         buflen -= buflen % device->pwfx->nBlockAlign;
154         device->buflen = buflen;
155
156         if (device->driver)
157         {
158                 err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
159                                                   DSBCAPS_PRIMARYBUFFER,0,
160                                                   &(device->buflen),&(device->buffer),
161                                                   (LPVOID*)&(device->hwbuf));
162
163                 if (err != DS_OK) {
164                         WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
165                         err = DSOUND_ReopenDevice(device, TRUE);
166                         if (FAILED(err))
167                         {
168                                 WARN("Falling back to waveout failed too! Giving up\n");
169                                 return err;
170                         }
171                 }
172                 if (device->hwbuf)
173                     IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
174
175                 DSOUND_RecalcPrimary(device);
176                 device->prebuf = ds_snd_queue_max;
177                 if (device->helfrags < ds_snd_queue_min)
178                 {
179                         WARN("Too little sound buffer to be effective (%d/%d) falling back to waveout\n", device->buflen, ds_snd_queue_min * device->fraglen);
180                         device->buflen = buflen;
181                         IDsDriverBuffer_Release(device->hwbuf);
182                         device->hwbuf = NULL;
183                         err = DSOUND_ReopenDevice(device, TRUE);
184                         if (FAILED(err))
185                         {
186                                 WARN("Falling back to waveout failed too! Giving up\n");
187                                 return err;
188                         }
189                 }
190                 else if (device->helfrags < ds_snd_queue_max)
191                         device->prebuf = device->helfrags;
192         }
193
194         device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
195         device->mix_buffer = HeapAlloc(GetProcessHeap(), 0, device->mix_buffer_len);
196         if (!device->mix_buffer)
197         {
198                 if (device->hwbuf)
199                         IDsDriverBuffer_Release(device->hwbuf);
200                 device->hwbuf = NULL;
201                 return DSERR_OUTOFMEMORY;
202         }
203
204         if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
205         else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
206
207         /* are we using waveOut stuff? */
208         if (!device->driver) {
209                 LPBYTE newbuf;
210                 LPWAVEHDR headers = NULL;
211                 DWORD overshot;
212                 unsigned int c;
213
214                 /* Start in pause mode, to allow buffers to get filled */
215                 waveOutPause(device->hwo);
216
217                 TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
218
219                 /* reallocate emulated primary buffer */
220                 if (device->buffer)
221                         newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, buflen);
222                 else
223                         newbuf = HeapAlloc(GetProcessHeap(),0, buflen);
224
225                 if (!newbuf) {
226                         ERR("failed to allocate primary buffer\n");
227                         return DSERR_OUTOFMEMORY;
228                         /* but the old buffer might still exist and must be re-prepared */
229                 }
230
231                 DSOUND_RecalcPrimary(device);
232                 if (device->pwave)
233                         headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
234                 else
235                         headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));
236
237                 if (!headers) {
238                         ERR("failed to allocate wave headers\n");
239                         HeapFree(GetProcessHeap(), 0, newbuf);
240                         DSOUND_RecalcPrimary(device);
241                         return DSERR_OUTOFMEMORY;
242                 }
243
244                 device->buffer = newbuf;
245                 device->pwave = headers;
246
247                 /* prepare fragment headers */
248                 for (c=0; c<device->helfrags; c++) {
249                         device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
250                         device->pwave[c].dwBufferLength = device->fraglen;
251                         device->pwave[c].dwUser = (DWORD)device;
252                         device->pwave[c].dwFlags = 0;
253                         device->pwave[c].dwLoops = 0;
254                         err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
255                         if (err != DS_OK) {
256                                 while (c--)
257                                         waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
258                                 break;
259                         }
260                 }
261
262                 overshot = device->buflen % device->fraglen;
263                 /* sanity */
264                 if(overshot)
265                 {
266                         overshot -= overshot % device->pwfx->nBlockAlign;
267                         device->pwave[device->helfrags - 1].dwBufferLength += overshot;
268                 }
269
270                 TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot);
271         }
272         device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
273         device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
274         FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
275         FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
276         device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
277         return err;
278 }
279
280
281 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
282 {
283         TRACE("(%p)\n", device);
284
285         /* are we using waveOut stuff? */
286         if (!device->hwbuf) {
287                 unsigned c;
288
289                 /* get out of CS when calling the wave system */
290                 LeaveCriticalSection(&(device->mixlock));
291                 /* **** */
292                 device->pwqueue = (DWORD)-1; /* resetting queues */
293                 waveOutReset(device->hwo);
294                 for (c=0; c<device->helfrags; c++)
295                         waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
296                 /* **** */
297                 EnterCriticalSection(&(device->mixlock));
298
299                 /* clear the queue */
300                 device->pwqueue = 0;
301         } else {
302                 ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
303                 if (!ref)
304                         device->hwbuf = 0;
305                 else
306                         ERR("Still %d references on primary buffer, refcount leak?\n", ref);
307         }
308 }
309
310 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
311 {
312         HRESULT err = DS_OK;
313         TRACE("(%p)\n", device);
314
315         device->buflen = ds_hel_buflen;
316         err = DSOUND_PrimaryOpen(device);
317
318         if (err != DS_OK) {
319                 WARN("DSOUND_PrimaryOpen failed\n");
320                 return err;
321         }
322
323         device->state = STATE_STOPPED;
324         return DS_OK;
325 }
326
327 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
328 {
329         TRACE("(%p)\n", device);
330
331         /* **** */
332         EnterCriticalSection(&(device->mixlock));
333
334         DSOUND_PrimaryClose(device);
335         if (device->driver) {
336                 if (device->hwbuf) {
337                         if (IDsDriverBuffer_Release(device->hwbuf) == 0)
338                                 device->hwbuf = 0;
339                 }
340         } else
341                 HeapFree(GetProcessHeap(),0,device->pwave);
342         HeapFree(GetProcessHeap(),0,device->pwfx);
343         device->pwfx=NULL;
344
345         LeaveCriticalSection(&(device->mixlock));
346         /* **** */
347
348         return DS_OK;
349 }
350
351 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
352 {
353         HRESULT err = DS_OK;
354         TRACE("(%p)\n", device);
355
356         if (device->hwbuf) {
357                 err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
358                 if (err != DS_OK)
359                         WARN("IDsDriverBuffer_Play failed\n");
360         } else {
361                 err = mmErr(waveOutRestart(device->hwo));
362                 if (err != DS_OK)
363                         WARN("waveOutRestart failed\n");
364         }
365
366         return err;
367 }
368
369 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
370 {
371         HRESULT err = DS_OK;
372         TRACE("(%p)\n", device);
373
374         if (device->hwbuf) {
375                 err = IDsDriverBuffer_Stop(device->hwbuf);
376                 if (err == DSERR_BUFFERLOST) {
377                         DSOUND_PrimaryClose(device);
378                         err = DSOUND_ReopenDevice(device, FALSE);
379                         if (FAILED(err))
380                                 ERR("DSOUND_ReopenDevice failed\n");
381                         else
382                         {
383                                 err = DSOUND_PrimaryOpen(device);
384                                 if (FAILED(err))
385                                         WARN("DSOUND_PrimaryOpen failed\n");
386                         }
387                 } else if (err != DS_OK) {
388                         WARN("IDsDriverBuffer_Stop failed\n");
389                 }
390         } else {
391
392                 /* don't call the wave system with the lock set */
393                 LeaveCriticalSection(&(device->mixlock));
394                 /* **** */
395
396                 err = mmErr(waveOutPause(device->hwo));
397
398                 /* **** */
399                 EnterCriticalSection(&(device->mixlock));
400
401                 if (err != DS_OK)
402                         WARN("waveOutPause failed\n");
403         }
404
405         return err;
406 }
407
408 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
409 {
410         TRACE("(%p,%p,%p)\n", device, playpos, writepos);
411
412         if (device->hwbuf) {
413                 HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
414                 if (err != S_OK) {
415                         WARN("IDsDriverBuffer_GetPosition failed\n");
416                         return err;
417                 }
418         } else {
419                 TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
420
421                 /* check if playpos was requested */
422                 if (playpos)
423                         /* use the cached play position */
424                         *playpos = device->pwplay * device->fraglen;
425
426                 /* check if writepos was requested */
427                 if (writepos)
428                         /* the writepos is the first non-queued position */
429                         *writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
430         }
431         TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
432         return DS_OK;
433 }
434
435 HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, BOOL forced)
436 {
437         HRESULT err = DSERR_BUFFERLOST;
438         int i, alloc_size, cp_size;
439         DWORD nSamplesPerSec, bpp, chans;
440         TRACE("(%p,%p)\n", device, wfex);
441
442         if (device->priolevel == DSSCL_NORMAL) {
443                 WARN("failed priority check!\n");
444                 return DSERR_PRIOLEVELNEEDED;
445         }
446
447         /* Let's be pedantic! */
448         if (wfex == NULL) {
449                 WARN("invalid parameter: wfex==NULL!\n");
450                 return DSERR_INVALIDPARAM;
451         }
452         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
453               "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
454               wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
455               wfex->nAvgBytesPerSec, wfex->nBlockAlign,
456               wfex->wBitsPerSample, wfex->cbSize);
457
458         /* **** */
459         RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
460         EnterCriticalSection(&(device->mixlock));
461
462         if (wfex->wFormatTag == WAVE_FORMAT_PCM) {
463             alloc_size = sizeof(WAVEFORMATEX);
464             cp_size = sizeof(PCMWAVEFORMAT);
465         } else
466             alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize;
467
468         device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size);
469
470         nSamplesPerSec = device->pwfx->nSamplesPerSec;
471         bpp = device->pwfx->wBitsPerSample;
472         chans = device->pwfx->nChannels;
473
474         CopyMemory(device->pwfx, wfex, cp_size);
475
476         if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
477                 err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
478
479                 /* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
480                 if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
481                 {
482                         err = DSERR_BUFFERLOST;
483                         CopyMemory(device->pwfx, wfex, cp_size);
484                 }
485
486                 if (err != DSERR_BUFFERLOST && FAILED(err)) {
487                         WARN("IDsDriverBuffer_SetFormat failed\n");
488                         if (!forced)
489                                 err = DS_OK;
490                         goto done;
491                 }
492
493                 if (err == S_FALSE)
494                 {
495                         /* ALSA specific: S_FALSE tells that recreation was successful,
496                          * but size and location may be changed, and buffer has to be restarted
497                          * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
498                          * and the entire re-initialization will occur anyway
499                          */
500                         IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
501                         IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);
502
503                         if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
504                         else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
505                         device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
506                         err = DS_OK;
507                 }
508                 DSOUND_RecalcPrimary(device);
509         }
510
511         if (err == DSERR_BUFFERLOST)
512         {
513                 DSOUND_PrimaryClose(device);
514
515                 err = DSOUND_ReopenDevice(device, FALSE);
516                 if (FAILED(err))
517                 {
518                         WARN("DSOUND_ReopenDevice failed: %08x\n", err);
519                         goto done;
520                 }
521                 err = DSOUND_PrimaryOpen(device);
522                 if (err != DS_OK) {
523                         WARN("DSOUND_PrimaryOpen failed\n");
524                         goto done;
525                 }
526
527                 if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
528                 {
529                         DSOUND_PrimaryClose(device);
530                         device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec;
531                         err = DSOUND_ReopenDevice(device, TRUE);
532                         if (FAILED(err))
533                                 WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
534                         else if (FAILED((err = DSOUND_PrimaryOpen(device))))
535                                 WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
536                 }
537         }
538
539         device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
540         device->mix_buffer = HeapReAlloc(GetProcessHeap(), 0, device->mix_buffer, device->mix_buffer_len);
541         FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
542         device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
543         device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
544
545         if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
546                 IDirectSoundBufferImpl** dsb = device->buffers;
547                 for (i = 0; i < device->nrofbuffers; i++, dsb++) {
548                         /* **** */
549                         RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
550
551                         (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
552                         DSOUND_RecalcFormat((*dsb));
553                         DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen, FALSE);
554                         (*dsb)->primary_mixpos = 0;
555
556                         RtlReleaseResource(&(*dsb)->lock);
557                         /* **** */
558                 }
559         }
560
561 done:
562         LeaveCriticalSection(&(device->mixlock));
563         RtlReleaseResource(&(device->buffer_list_lock));
564         /* **** */
565
566         return err;
567 }
568
569 /*******************************************************************************
570  *              PrimaryBuffer
571  */
572 /* This sets this format for the <em>Primary Buffer Only</em> */
573 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
574 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
575     LPDIRECTSOUNDBUFFER iface,
576     LPCWAVEFORMATEX wfex)
577 {
578     DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
579     TRACE("(%p,%p)\n", iface, wfex);
580     return DSOUND_PrimarySetFormat(device, wfex, device->priolevel == DSSCL_WRITEPRIMARY);
581 }
582
583 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
584         LPDIRECTSOUNDBUFFER iface,LONG vol
585 ) {
586         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
587         DWORD ampfactors;
588         HRESULT hres = DS_OK;
589         TRACE("(%p,%d)\n", iface, vol);
590
591         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
592                 WARN("control unavailable\n");
593                 return DSERR_CONTROLUNAVAIL;
594         }
595
596         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
597                 WARN("invalid parameter: vol = %d\n", vol);
598                 return DSERR_INVALIDPARAM;
599         }
600
601         /* **** */
602         EnterCriticalSection(&(device->mixlock));
603
604         waveOutGetVolume(device->hwo, &ampfactors);
605         device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
606         device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
607         DSOUND_AmpFactorToVolPan(&device->volpan);
608         if (vol != device->volpan.lVolume) {
609             device->volpan.lVolume=vol;
610             DSOUND_RecalcVolPan(&device->volpan);
611             if (device->hwbuf) {
612                 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
613                 if (hres != DS_OK)
614                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
615             } else {
616                 ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
617                 waveOutSetVolume(device->hwo, ampfactors);
618             }
619         }
620
621         LeaveCriticalSection(&(device->mixlock));
622         /* **** */
623
624         return hres;
625 }
626
627 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
628         LPDIRECTSOUNDBUFFER iface,LPLONG vol
629 ) {
630         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
631         DWORD ampfactors;
632         TRACE("(%p,%p)\n", iface, vol);
633
634         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
635                 WARN("control unavailable\n");
636                 return DSERR_CONTROLUNAVAIL;
637         }
638
639         if (vol == NULL) {
640                 WARN("invalid parameter: vol = NULL\n");
641                 return DSERR_INVALIDPARAM;
642         }
643
644         if (!device->hwbuf)
645         {
646             waveOutGetVolume(device->hwo, &ampfactors);
647             device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
648             device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
649             DSOUND_AmpFactorToVolPan(&device->volpan);
650         }
651         *vol = device->volpan.lVolume;
652         return DS_OK;
653 }
654
655 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
656         LPDIRECTSOUNDBUFFER iface,DWORD freq
657 ) {
658         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
659         TRACE("(%p,%d)\n",This,freq);
660
661         /* You cannot set the frequency of the primary buffer */
662         WARN("control unavailable\n");
663         return DSERR_CONTROLUNAVAIL;
664 }
665
666 static HRESULT WINAPI PrimaryBufferImpl_Play(
667         LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
668 ) {
669         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
670         TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
671
672         if (!(flags & DSBPLAY_LOOPING)) {
673                 WARN("invalid parameter: flags = %08x\n", flags);
674                 return DSERR_INVALIDPARAM;
675         }
676
677         /* **** */
678         EnterCriticalSection(&(device->mixlock));
679
680         if (device->state == STATE_STOPPED)
681                 device->state = STATE_STARTING;
682         else if (device->state == STATE_STOPPING)
683                 device->state = STATE_PLAYING;
684
685         LeaveCriticalSection(&(device->mixlock));
686         /* **** */
687
688         return DS_OK;
689 }
690
691 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
692 {
693         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
694         TRACE("(%p)\n", iface);
695
696         /* **** */
697         EnterCriticalSection(&(device->mixlock));
698
699         if (device->state == STATE_PLAYING)
700                 device->state = STATE_STOPPING;
701         else if (device->state == STATE_STARTING)
702                 device->state = STATE_STOPPED;
703
704         LeaveCriticalSection(&(device->mixlock));
705         /* **** */
706
707         return DS_OK;
708 }
709
710 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
711 {
712     PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
713     ULONG ref = InterlockedIncrement(&(This->ref));
714     TRACE("(%p) ref was %d\n", This, ref - 1);
715     return ref;
716 }
717
718 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
719 {
720     PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
721     DWORD ref = InterlockedDecrement(&(This->ref));
722     TRACE("(%p) ref was %d\n", This, ref + 1);
723
724     if (!ref) {
725         This->device->primary = NULL;
726         HeapFree(GetProcessHeap(), 0, This);
727         TRACE("(%p) released\n", This);
728     }
729     return ref;
730 }
731
732 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
733         LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
734 ) {
735         HRESULT hres;
736         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
737         TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
738
739         /* **** */
740         EnterCriticalSection(&(device->mixlock));
741
742         hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
743         if (hres != DS_OK) {
744                 WARN("DSOUND_PrimaryGetPosition failed\n");
745                 LeaveCriticalSection(&(device->mixlock));
746                 return hres;
747         }
748         if (writepos) {
749                 if (device->state != STATE_STOPPED)
750                         /* apply the documented 10ms lead to writepos */
751                         *writepos += device->writelead;
752                 while (*writepos >= device->buflen) *writepos -= device->buflen;
753         }
754
755         LeaveCriticalSection(&(device->mixlock));
756         /* **** */
757
758         TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
759         return DS_OK;
760 }
761
762 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
763         LPDIRECTSOUNDBUFFER iface,LPDWORD status
764 ) {
765         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
766         TRACE("(%p,%p)\n", iface, status);
767
768         if (status == NULL) {
769                 WARN("invalid parameter: status == NULL\n");
770                 return DSERR_INVALIDPARAM;
771         }
772
773         *status = 0;
774         if ((device->state == STATE_STARTING) ||
775             (device->state == STATE_PLAYING))
776                 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
777
778         TRACE("status=%x\n", *status);
779         return DS_OK;
780 }
781
782
783 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
784     LPDIRECTSOUNDBUFFER iface,
785     LPWAVEFORMATEX lpwf,
786     DWORD wfsize,
787     LPDWORD wfwritten)
788 {
789     DWORD size;
790     DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
791     TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
792
793     size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
794
795     if (lpwf) { /* NULL is valid */
796         if (wfsize >= size) {
797             CopyMemory(lpwf,device->pwfx,size);
798             if (wfwritten)
799                 *wfwritten = size;
800         } else {
801             WARN("invalid parameter: wfsize too small\n");
802             if (wfwritten)
803                 *wfwritten = 0;
804             return DSERR_INVALIDPARAM;
805         }
806     } else {
807         if (wfwritten)
808             *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
809         else {
810             WARN("invalid parameter: wfwritten == NULL\n");
811             return DSERR_INVALIDPARAM;
812         }
813     }
814
815     return DS_OK;
816 }
817
818 static HRESULT WINAPI PrimaryBufferImpl_Lock(
819         LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
820 ) {
821         HRESULT hres;
822         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
823         TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
824                 iface,
825                 writecursor,
826                 writebytes,
827                 lplpaudioptr1,
828                 audiobytes1,
829                 lplpaudioptr2,
830                 audiobytes2,
831                 flags,
832                 GetTickCount()
833         );
834
835         if (device->priolevel != DSSCL_WRITEPRIMARY) {
836                 WARN("failed priority check!\n");
837                 return DSERR_PRIOLEVELNEEDED;
838         }
839
840         /* when this flag is set, writecursor is meaningless and must be calculated */
841         if (flags & DSBLOCK_FROMWRITECURSOR) {
842                 /* GetCurrentPosition does too much magic to duplicate here */
843                 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
844                 if (hres != DS_OK) {
845                         WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
846                         return hres;
847                 }
848         }
849
850         /* when this flag is set, writebytes is meaningless and must be set */
851         if (flags & DSBLOCK_ENTIREBUFFER)
852                 writebytes = device->buflen;
853
854         if (writecursor >= device->buflen) {
855                 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
856                      writecursor, device->buflen);
857                 return DSERR_INVALIDPARAM;
858         }
859
860         if (writebytes > device->buflen) {
861                 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
862                      writebytes, device->buflen);
863                 return DSERR_INVALIDPARAM;
864         }
865
866         if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
867                 hres = IDsDriverBuffer_Lock(device->hwbuf,
868                                             lplpaudioptr1, audiobytes1,
869                                             lplpaudioptr2, audiobytes2,
870                                             writecursor, writebytes,
871                                             0);
872                 if (hres != DS_OK) {
873                         WARN("IDsDriverBuffer_Lock failed\n");
874                         return hres;
875                 }
876         } else {
877                 if (writecursor+writebytes <= device->buflen) {
878                         *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
879                         *audiobytes1 = writebytes;
880                         if (lplpaudioptr2)
881                                 *(LPBYTE*)lplpaudioptr2 = NULL;
882                         if (audiobytes2)
883                                 *audiobytes2 = 0;
884                         TRACE("->%d.0\n",writebytes);
885                 } else {
886                         *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
887                         *audiobytes1 = device->buflen-writecursor;
888                         if (lplpaudioptr2)
889                                 *(LPBYTE*)lplpaudioptr2 = device->buffer;
890                         if (audiobytes2)
891                                 *audiobytes2 = writebytes-(device->buflen-writecursor);
892                         TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
893                 }
894         }
895         return DS_OK;
896 }
897
898 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
899         LPDIRECTSOUNDBUFFER iface,DWORD newpos
900 ) {
901         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
902         TRACE("(%p,%d)\n",This,newpos);
903
904         /* You cannot set the position of the primary buffer */
905         WARN("invalid call\n");
906         return DSERR_INVALIDCALL;
907 }
908
909 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
910         LPDIRECTSOUNDBUFFER iface,LONG pan
911 ) {
912         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
913         DWORD ampfactors;
914         HRESULT hres = DS_OK;
915         TRACE("(%p,%d)\n", iface, pan);
916
917         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
918                 WARN("control unavailable\n");
919                 return DSERR_CONTROLUNAVAIL;
920         }
921
922         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
923                 WARN("invalid parameter: pan = %d\n", pan);
924                 return DSERR_INVALIDPARAM;
925         }
926
927         /* **** */
928         EnterCriticalSection(&(device->mixlock));
929
930         if (!device->hwbuf)
931         {
932             waveOutGetVolume(device->hwo, &ampfactors);
933             device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
934             device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
935             DSOUND_AmpFactorToVolPan(&device->volpan);
936         }
937         if (pan != device->volpan.lPan) {
938             device->volpan.lPan=pan;
939             DSOUND_RecalcVolPan(&device->volpan);
940             if (device->hwbuf) {
941                 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
942                 if (hres != DS_OK)
943                     WARN("IDsDriverBuffer_SetVolumePan failed\n");
944             } else {
945                 ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
946                 waveOutSetVolume(device->hwo, ampfactors);
947             }
948         }
949
950         LeaveCriticalSection(&(device->mixlock));
951         /* **** */
952
953         return hres;
954 }
955
956 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
957         LPDIRECTSOUNDBUFFER iface,LPLONG pan
958 ) {
959         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
960         DWORD ampfactors;
961         TRACE("(%p,%p)\n", iface, pan);
962
963         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
964                 WARN("control unavailable\n");
965                 return DSERR_CONTROLUNAVAIL;
966         }
967
968         if (pan == NULL) {
969                 WARN("invalid parameter: pan == NULL\n");
970                 return DSERR_INVALIDPARAM;
971         }
972
973         if (!device->hwbuf)
974         {
975             waveOutGetVolume(device->hwo, &ampfactors);
976             device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
977             device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
978             DSOUND_AmpFactorToVolPan(&device->volpan);
979         }
980         *pan = device->volpan.lPan;
981         return DS_OK;
982 }
983
984 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
985         LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
986 ) {
987         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
988         TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
989
990         if (device->priolevel != DSSCL_WRITEPRIMARY) {
991                 WARN("failed priority check!\n");
992                 return DSERR_PRIOLEVELNEEDED;
993         }
994
995         if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
996                 HRESULT hres;
997                 
998                 hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
999                 if (hres != DS_OK) {
1000                         WARN("IDsDriverBuffer_Unlock failed\n");
1001                         return hres;
1002                 }
1003         }
1004
1005         return DS_OK;
1006 }
1007
1008 static HRESULT WINAPI PrimaryBufferImpl_Restore(
1009         LPDIRECTSOUNDBUFFER iface
1010 ) {
1011         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1012         FIXME("(%p):stub\n",This);
1013         return DS_OK;
1014 }
1015
1016 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
1017         LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1018 ) {
1019         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1020         TRACE("(%p,%p)\n", iface, freq);
1021
1022         if (freq == NULL) {
1023                 WARN("invalid parameter: freq == NULL\n");
1024                 return DSERR_INVALIDPARAM;
1025         }
1026
1027         if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1028                 WARN("control unavailable\n");
1029                 return DSERR_CONTROLUNAVAIL;
1030         }
1031
1032         *freq = device->pwfx->nSamplesPerSec;
1033         TRACE("-> %d\n", *freq);
1034
1035         return DS_OK;
1036 }
1037
1038 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
1039         LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
1040 ) {
1041         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1042         WARN("(%p) already initialized\n", This);
1043         return DSERR_ALREADYINITIALIZED;
1044 }
1045
1046 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
1047         LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1048 ) {
1049         DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1050         TRACE("(%p,%p)\n", iface, caps);
1051
1052         if (caps == NULL) {
1053                 WARN("invalid parameter: caps == NULL\n");
1054                 return DSERR_INVALIDPARAM;
1055         }
1056
1057         if (caps->dwSize < sizeof(*caps)) {
1058                 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1059                 return DSERR_INVALIDPARAM;
1060         }
1061
1062         caps->dwFlags = device->dsbd.dwFlags;
1063         caps->dwBufferBytes = device->buflen;
1064
1065         /* Windows reports these as zero */
1066         caps->dwUnlockTransferRate = 0;
1067         caps->dwPlayCpuOverhead = 0;
1068
1069         return DS_OK;
1070 }
1071
1072 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1073         LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1074 ) {
1075         PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1076         DirectSoundDevice *device = This->device;
1077         TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1078
1079         if (ppobj == NULL) {
1080                 WARN("invalid parameter\n");
1081                 return E_INVALIDARG;
1082         }
1083
1084         *ppobj = NULL;  /* assume failure */
1085
1086         if ( IsEqualGUID(riid, &IID_IUnknown) ||
1087              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1088                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1089                 *ppobj = This;
1090                 return S_OK;
1091         }
1092
1093         /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1094         /* a primary buffer can't have a DirectSoundBuffer8 interface */
1095         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1096                 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1097                 return E_NOINTERFACE;
1098         }
1099
1100         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1101                 ERR("app requested IDirectSoundNotify on primary buffer\n");
1102                 /* FIXME: should we support this? */
1103                 return E_NOINTERFACE;
1104         }
1105
1106         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1107                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1108                 return E_NOINTERFACE;
1109         }
1110
1111         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1112                 if (!device->listener)
1113                         IDirectSound3DListenerImpl_Create(device, &device->listener);
1114                 if (device->listener) {
1115                         *ppobj = device->listener;
1116                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1117                         return S_OK;
1118                 }
1119
1120                 WARN("IID_IDirectSound3DListener failed\n");
1121                 return E_NOINTERFACE;
1122         }
1123
1124         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1125                 FIXME("app requested IKsPropertySet on primary buffer\n");
1126                 return E_NOINTERFACE;
1127         }
1128
1129         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1130         return E_NOINTERFACE;
1131 }
1132
1133 static const IDirectSoundBufferVtbl dspbvt =
1134 {
1135         PrimaryBufferImpl_QueryInterface,
1136         PrimaryBufferImpl_AddRef,
1137         PrimaryBufferImpl_Release,
1138         PrimaryBufferImpl_GetCaps,
1139         PrimaryBufferImpl_GetCurrentPosition,
1140         PrimaryBufferImpl_GetFormat,
1141         PrimaryBufferImpl_GetVolume,
1142         PrimaryBufferImpl_GetPan,
1143         PrimaryBufferImpl_GetFrequency,
1144         PrimaryBufferImpl_GetStatus,
1145         PrimaryBufferImpl_Initialize,
1146         PrimaryBufferImpl_Lock,
1147         PrimaryBufferImpl_Play,
1148         PrimaryBufferImpl_SetCurrentPosition,
1149         PrimaryBufferImpl_SetFormat,
1150         PrimaryBufferImpl_SetVolume,
1151         PrimaryBufferImpl_SetPan,
1152         PrimaryBufferImpl_SetFrequency,
1153         PrimaryBufferImpl_Stop,
1154         PrimaryBufferImpl_Unlock,
1155         PrimaryBufferImpl_Restore
1156 };
1157
1158 HRESULT PrimaryBufferImpl_Create(
1159         DirectSoundDevice * device,
1160         PrimaryBufferImpl ** ppdsb,
1161         LPCDSBUFFERDESC dsbd)
1162 {
1163         PrimaryBufferImpl *dsb;
1164         TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1165
1166         if (dsbd->lpwfxFormat) {
1167                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1168                 *ppdsb = NULL;
1169                 return DSERR_INVALIDPARAM;
1170         }
1171
1172         dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1173
1174         if (dsb == NULL) {
1175                 WARN("out of memory\n");
1176                 *ppdsb = NULL;
1177                 return DSERR_OUTOFMEMORY;
1178         }
1179
1180         dsb->ref = 0;
1181         dsb->device = device;
1182         dsb->lpVtbl = &dspbvt;
1183
1184         device->dsbd = *dsbd;
1185
1186         TRACE("Created primary buffer at %p\n", dsb);
1187         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1188                 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1189                 device->pwfx->wFormatTag, device->pwfx->nChannels,
1190                 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1191                 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1192                 device->pwfx->cbSize);
1193
1194         *ppdsb = dsb;
1195         return S_OK;
1196 }