msvcrt/tests: Don't test function directly when reporting errno.
[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  * TODO:
22  *      When PrimarySetFormat (via ReopenDevice or PrimaryOpen) fails,
23  *       it leaves dsound in unusable (not really open) state.
24  */
25
26 #include <stdarg.h>
27
28 #define COBJMACROS
29 #define NONAMELESSSTRUCT
30 #define NONAMELESSUNION
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winuser.h"
34 #include "mmsystem.h"
35 #include "winternl.h"
36 #include "mmddk.h"
37 #include "wine/debug.h"
38 #include "dsound.h"
39 #include "dsound_private.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
42
43 static DWORD DSOUND_fraglen(DirectSoundDevice *device)
44 {
45     REFERENCE_TIME period;
46     HRESULT hr;
47     DWORD ret;
48
49     hr = IAudioClient_GetDevicePeriod(device->client, &period, NULL);
50     if(FAILED(hr)){
51         /* just guess at 10ms */
52         WARN("GetDevicePeriod failed: %08x\n", hr);
53         ret = MulDiv(device->pwfx->nBlockAlign, device->pwfx->nSamplesPerSec, 100);
54     }else
55         ret = MulDiv(device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign, period, 10000000);
56
57     ret -= ret % device->pwfx->nBlockAlign;
58     return ret;
59 }
60
61 static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client,
62                                  BOOL forcewave, WAVEFORMATEX **wfx)
63 {
64     WAVEFORMATEXTENSIBLE *retwfe = NULL;
65     WAVEFORMATEX *w;
66     HRESULT hr;
67
68     if (!forcewave) {
69         WAVEFORMATEXTENSIBLE *mixwfe;
70         hr = IAudioClient_GetMixFormat(client, (WAVEFORMATEX**)&mixwfe);
71
72         if (FAILED(hr))
73             return hr;
74
75         if (mixwfe->Format.nChannels > 2) {
76             static int once;
77             if (!once++)
78                 FIXME("Limiting channels to 2 due to lack of multichannel support\n");
79
80             mixwfe->Format.nChannels = 2;
81             mixwfe->Format.nBlockAlign = mixwfe->Format.nChannels * mixwfe->Format.wBitsPerSample / 8;
82             mixwfe->Format.nAvgBytesPerSec = mixwfe->Format.nSamplesPerSec * mixwfe->Format.nBlockAlign;
83             mixwfe->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
84         }
85
86         if (!IsEqualGUID(&mixwfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
87             WAVEFORMATEXTENSIBLE testwfe = *mixwfe;
88
89             testwfe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
90             testwfe.Samples.wValidBitsPerSample = testwfe.Format.wBitsPerSample = 32;
91             testwfe.Format.nBlockAlign = testwfe.Format.nChannels * testwfe.Format.wBitsPerSample / 8;
92             testwfe.Format.nAvgBytesPerSec = testwfe.Format.nSamplesPerSec * testwfe.Format.nBlockAlign;
93
94             if (FAILED(IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &testwfe.Format, (WAVEFORMATEX**)&retwfe)))
95                 w = DSOUND_CopyFormat(&mixwfe->Format);
96             else if (retwfe)
97                 w = DSOUND_CopyFormat(&retwfe->Format);
98             else
99                 w = DSOUND_CopyFormat(&testwfe.Format);
100             CoTaskMemFree(retwfe);
101             retwfe = NULL;
102         } else
103             w = DSOUND_CopyFormat(&mixwfe->Format);
104         CoTaskMemFree(mixwfe);
105     } else if (device->primary_pwfx->wFormatTag == WAVE_FORMAT_PCM ||
106                device->primary_pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
107         WAVEFORMATEX *wi = device->primary_pwfx;
108         WAVEFORMATEXTENSIBLE *wfe;
109
110         /* Convert to WAVEFORMATEXTENSIBLE */
111         w = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
112         wfe = (WAVEFORMATEXTENSIBLE*)w;
113         if (!wfe)
114             return DSERR_OUTOFMEMORY;
115
116         wfe->Format = *wi;
117         w->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
118         w->cbSize = sizeof(*wfe) - sizeof(*w);
119         w->nBlockAlign = w->nChannels * w->wBitsPerSample / 8;
120         w->nAvgBytesPerSec = w->nSamplesPerSec * w->nBlockAlign;
121
122         wfe->dwChannelMask = 0;
123         if (wi->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
124             w->wBitsPerSample = 32;
125             wfe->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
126         } else
127             wfe->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
128         wfe->Samples.wValidBitsPerSample = w->wBitsPerSample;
129     } else
130         w = DSOUND_CopyFormat(device->primary_pwfx);
131
132     if (!w)
133         return DSERR_OUTOFMEMORY;
134
135     hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, w, (WAVEFORMATEX**)&retwfe);
136     if (retwfe) {
137         memcpy(w, retwfe, sizeof(WAVEFORMATEX) + retwfe->Format.cbSize);
138         CoTaskMemFree(retwfe);
139     }
140     if (FAILED(hr)) {
141         WARN("IsFormatSupported failed: %08x\n", hr);
142         HeapFree(GetProcessHeap(), 0, w);
143         return hr;
144     }
145     *wfx = w;
146     return S_OK;
147 }
148
149 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
150 {
151     UINT prebuf_frames;
152     REFERENCE_TIME prebuf_rt;
153     WAVEFORMATEX *wfx = NULL;
154     HRESULT hres;
155
156     TRACE("(%p, %d)\n", device, forcewave);
157
158     if(device->client){
159         IAudioClient_Release(device->client);
160         device->client = NULL;
161     }
162     if(device->render){
163         IAudioRenderClient_Release(device->render);
164         device->render = NULL;
165     }
166     if(device->clock){
167         IAudioClock_Release(device->clock);
168         device->clock = NULL;
169     }
170     if(device->volume){
171         IAudioStreamVolume_Release(device->volume);
172         device->volume = NULL;
173     }
174
175     hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
176             CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
177     if(FAILED(hres)) {
178         WARN("Activate failed: %08x\n", hres);
179         return hres;
180     }
181
182     hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx);
183     if (FAILED(hres)) {
184         IAudioClient_Release(device->client);
185         device->client = NULL;
186         return hres;
187     }
188     HeapFree(GetProcessHeap(), 0, device->pwfx);
189     device->pwfx = wfx;
190
191     prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign;
192     prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;
193
194     hres = IAudioClient_Initialize(device->client,
195             AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST,
196             prebuf_rt, 0, device->pwfx, NULL);
197     if(FAILED(hres)){
198         IAudioClient_Release(device->client);
199         device->client = NULL;
200         WARN("Initialize failed: %08x\n", hres);
201         return hres;
202     }
203
204     hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
205             (void**)&device->render);
206     if(FAILED(hres)){
207         IAudioClient_Release(device->client);
208         device->client = NULL;
209         WARN("GetService failed: %08x\n", hres);
210         return hres;
211     }
212
213     hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
214             (void**)&device->clock);
215     if(FAILED(hres)){
216         IAudioClient_Release(device->client);
217         IAudioRenderClient_Release(device->render);
218         device->client = NULL;
219         device->render = NULL;
220         WARN("GetService failed: %08x\n", hres);
221         return hres;
222     }
223
224     hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
225             (void**)&device->volume);
226     if(FAILED(hres)){
227         IAudioClient_Release(device->client);
228         IAudioRenderClient_Release(device->render);
229         IAudioClock_Release(device->clock);
230         device->client = NULL;
231         device->render = NULL;
232         device->clock = NULL;
233         WARN("GetService failed: %08x\n", hres);
234         return hres;
235     }
236
237     return S_OK;
238 }
239
240 HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
241 {
242         IDirectSoundBufferImpl** dsb = device->buffers;
243         LPBYTE newbuf;
244         DWORD i;
245
246         TRACE("(%p)\n", device);
247
248         device->fraglen = DSOUND_fraglen(device);
249
250         /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
251            on windows this size is always fixed (tested on win-xp) */
252         if (!device->buflen)
253                 device->buflen = ds_hel_buflen;
254         device->buflen -= device->buflen % device->pwfx->nBlockAlign;
255         while(device->buflen < device->fraglen * device->prebuf){
256                 device->buflen += ds_hel_buflen;
257                 device->buflen -= device->buflen % device->pwfx->nBlockAlign;
258         }
259
260         HeapFree(GetProcessHeap(), 0, device->mix_buffer);
261         device->mix_buffer_len = (device->buflen / (device->pwfx->wBitsPerSample / 8)) * sizeof(float);
262         device->mix_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, device->mix_buffer_len);
263         if (!device->mix_buffer)
264                 return DSERR_OUTOFMEMORY;
265
266         if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
267         else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
268
269     /* reallocate emulated primary buffer */
270     if (device->buffer)
271         newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, device->buflen);
272     else
273         newbuf = HeapAlloc(GetProcessHeap(),0, device->buflen);
274
275     if (!newbuf) {
276         ERR("failed to allocate primary buffer\n");
277         return DSERR_OUTOFMEMORY;
278         /* but the old buffer might still exist and must be re-prepared */
279     }
280
281     device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
282
283     device->buffer = newbuf;
284
285     TRACE("buflen: %u, fraglen: %u, mix_buffer_len: %u\n",
286             device->buflen, device->fraglen, device->mix_buffer_len);
287
288     if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
289             (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
290              IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat,
291                  &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
292         device->normfunction = normfunctions[4];
293     else
294         device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
295
296     FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
297     FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
298     device->playpos = 0;
299
300     if (device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
301          (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
302           IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
303         device->normfunction = normfunctions[4];
304     else
305         device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
306
307     for (i = 0; i < device->nrofbuffers; i++) {
308         RtlAcquireResourceExclusive(&dsb[i]->lock, TRUE);
309         DSOUND_RecalcFormat(dsb[i]);
310         RtlReleaseResource(&dsb[i]->lock);
311     }
312
313     return DS_OK;
314 }
315
316
317 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
318 {
319     HRESULT hr;
320
321     TRACE("(%p)\n", device);
322
323     if(device->client){
324         hr = IAudioClient_Stop(device->client);
325         if(FAILED(hr))
326             WARN("Stop failed: %08x\n", hr);
327     }
328
329     /* clear the queue */
330     device->in_mmdev_bytes = 0;
331 }
332
333 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
334 {
335         HRESULT err = DS_OK;
336         TRACE("(%p)\n", device);
337
338         device->buflen = ds_hel_buflen;
339         err = DSOUND_PrimaryOpen(device);
340
341         if (err != DS_OK) {
342                 WARN("DSOUND_PrimaryOpen failed\n");
343                 return err;
344         }
345
346         device->state = STATE_STOPPED;
347         return DS_OK;
348 }
349
350 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
351 {
352         TRACE("(%p)\n", device);
353
354         /* **** */
355         EnterCriticalSection(&(device->mixlock));
356
357         DSOUND_PrimaryClose(device);
358
359         if(device->primary && (device->primary->ref || device->primary->numIfaces))
360                 WARN("Destroying primary buffer while references held (%u %u)\n", device->primary->ref, device->primary->numIfaces);
361
362         HeapFree(GetProcessHeap(), 0, device->primary);
363         device->primary = NULL;
364
365         HeapFree(GetProcessHeap(),0,device->primary_pwfx);
366         HeapFree(GetProcessHeap(),0,device->pwfx);
367         device->pwfx=NULL;
368
369         LeaveCriticalSection(&(device->mixlock));
370         /* **** */
371
372         return DS_OK;
373 }
374
375 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
376 {
377     HRESULT hr;
378
379     TRACE("(%p)\n", device);
380
381     hr = IAudioClient_Start(device->client);
382     if(FAILED(hr)){
383         WARN("Start failed: %08x\n", hr);
384         return hr;
385     }
386
387     return DS_OK;
388 }
389
390 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
391 {
392     HRESULT hr;
393
394     TRACE("(%p)\n", device);
395
396     hr = IAudioClient_Stop(device->client);
397     if(FAILED(hr)){
398         WARN("Stop failed: %08x\n", hr);
399         return hr;
400     }
401
402     return DS_OK;
403 }
404
405 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
406 {
407         TRACE("(%p,%p,%p)\n", device, playpos, writepos);
408
409         /* check if playpos was requested */
410         if (playpos)
411                 *playpos = device->playing_offs_bytes;
412
413         /* check if writepos was requested */
414         if (writepos)
415                 /* the writepos is the first non-queued position */
416                 *writepos = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen;
417
418         TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
419         return DS_OK;
420 }
421
422 static DWORD DSOUND_GetFormatSize(LPCWAVEFORMATEX wfex)
423 {
424         if (wfex->wFormatTag == WAVE_FORMAT_PCM)
425                 return sizeof(WAVEFORMATEX);
426         else
427                 return sizeof(WAVEFORMATEX) + wfex->cbSize;
428 }
429
430 LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex)
431 {
432         DWORD size = DSOUND_GetFormatSize(wfex);
433         LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(),0,size);
434         if (pwfx == NULL) {
435                 WARN("out of memory\n");
436         } else if (wfex->wFormatTag != WAVE_FORMAT_PCM) {
437                 CopyMemory(pwfx, wfex, size);
438         } else {
439                 CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT));
440                 pwfx->cbSize=0;
441                 if (pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample/8) {
442                         WARN("Fixing bad nBlockAlign (%u)\n", pwfx->nBlockAlign);
443                         pwfx->nBlockAlign  = pwfx->nChannels * pwfx->wBitsPerSample/8;
444                 }
445                 if (pwfx->nAvgBytesPerSec != pwfx->nSamplesPerSec * pwfx->nBlockAlign) {
446                         WARN("Fixing bad nAvgBytesPerSec (%u)\n", pwfx->nAvgBytesPerSec);
447                         pwfx->nAvgBytesPerSec  = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
448                 }
449         }
450         return pwfx;
451 }
452
453 HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passed_fmt)
454 {
455         HRESULT err = S_OK;
456         WAVEFORMATEX *old_fmt;
457         WAVEFORMATEXTENSIBLE *fmtex, *passed_fmtex = (WAVEFORMATEXTENSIBLE*)passed_fmt;
458         BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY);
459
460         TRACE("(%p,%p)\n", device, passed_fmt);
461
462         if (device->priolevel == DSSCL_NORMAL) {
463                 WARN("failed priority check!\n");
464                 return DSERR_PRIOLEVELNEEDED;
465         }
466
467         /* Let's be pedantic! */
468         if (passed_fmt == NULL) {
469                 WARN("invalid parameter: passed_fmt==NULL!\n");
470                 return DSERR_INVALIDPARAM;
471         }
472         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
473                           "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
474                   passed_fmt->wFormatTag, passed_fmt->nChannels, passed_fmt->nSamplesPerSec,
475                   passed_fmt->nAvgBytesPerSec, passed_fmt->nBlockAlign,
476                   passed_fmt->wBitsPerSample, passed_fmt->cbSize);
477
478         if(passed_fmt->wBitsPerSample < 8 || passed_fmt->wBitsPerSample % 8 != 0 ||
479                         passed_fmt->nChannels == 0 || passed_fmt->nSamplesPerSec == 0 ||
480                         passed_fmt->nAvgBytesPerSec == 0 ||
481                         passed_fmt->nBlockAlign != passed_fmt->nChannels * passed_fmt->wBitsPerSample / 8)
482                 return DSERR_INVALIDPARAM;
483
484         if(passed_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
485                 if(passed_fmtex->Samples.wValidBitsPerSample > passed_fmtex->Format.wBitsPerSample)
486                         return DSERR_INVALIDPARAM;
487         }
488
489         /* **** */
490         RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
491         EnterCriticalSection(&(device->mixlock));
492
493         if (device->priolevel == DSSCL_WRITEPRIMARY) {
494                 old_fmt = device->primary_pwfx;
495                 device->primary_pwfx = DSOUND_CopyFormat(passed_fmt);
496                 fmtex = (WAVEFORMATEXTENSIBLE *)device->primary_pwfx;
497                 if (device->primary_pwfx == NULL) {
498                         err = DSERR_OUTOFMEMORY;
499                         goto out;
500                 }
501
502                 if (fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
503                     fmtex->Samples.wValidBitsPerSample == 0) {
504                         TRACE("Correcting 0 valid bits per sample\n");
505                         fmtex->Samples.wValidBitsPerSample = fmtex->Format.wBitsPerSample;
506                 }
507
508                 DSOUND_PrimaryClose(device);
509
510                 err = DSOUND_ReopenDevice(device, forced);
511                 if (FAILED(err)) {
512                         ERR("No formats could be opened\n");
513                         goto done;
514                 }
515
516                 err = DSOUND_PrimaryOpen(device);
517                 if (err != DS_OK) {
518                         ERR("DSOUND_PrimaryOpen failed\n");
519                         goto done;
520                 }
521
522 done:
523                 if (err != DS_OK)
524                         device->primary_pwfx = old_fmt;
525                 else
526                         HeapFree(GetProcessHeap(), 0, old_fmt);
527         } else if (passed_fmt->wFormatTag == WAVE_FORMAT_PCM ||
528                    passed_fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
529                 /* Fill in "real" values to primary_pwfx */
530                 WAVEFORMATEX *fmt = device->primary_pwfx;
531
532                 *fmt = *device->pwfx;
533                 fmtex = (void*)device->pwfx;
534
535                 if (IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
536                     passed_fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
537                         fmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
538                 } else {
539                         fmt->wFormatTag = WAVE_FORMAT_PCM;
540                         fmt->wBitsPerSample = 16;
541                 }
542                 fmt->nBlockAlign = fmt->nChannels * fmt->wBitsPerSample / 8;
543                 fmt->nAvgBytesPerSec = fmt->nBlockAlign * fmt->nSamplesPerSec;
544                 fmt->cbSize = 0;
545         } else {
546                 device->primary_pwfx = HeapReAlloc(GetProcessHeap(), 0, device->primary_pwfx, sizeof(*fmtex));
547                 memcpy(device->primary_pwfx, device->pwfx, sizeof(*fmtex));
548         }
549
550 out:
551         LeaveCriticalSection(&(device->mixlock));
552         RtlReleaseResource(&(device->buffer_list_lock));
553         /* **** */
554
555         return err;
556 }
557
558 /*******************************************************************************
559  *              PrimaryBuffer
560  */
561 static inline IDirectSoundBufferImpl *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
562 {
563     /* IDirectSoundBuffer and IDirectSoundBuffer8 use the same iface. */
564     return CONTAINING_RECORD(iface, IDirectSoundBufferImpl, IDirectSoundBuffer8_iface);
565 }
566
567 /* This sets this format for the primary buffer only */
568 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(IDirectSoundBuffer *iface,
569         const WAVEFORMATEX *wfex)
570 {
571     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
572     TRACE("(%p,%p)\n", iface, wfex);
573     return primarybuffer_SetFormat(This->device, wfex);
574 }
575
576 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(IDirectSoundBuffer *iface, LONG vol)
577 {
578         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
579         DirectSoundDevice *device = This->device;
580         HRESULT hr;
581         float lvol, rvol;
582
583         TRACE("(%p,%d)\n", iface, vol);
584
585         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
586                 WARN("control unavailable\n");
587                 return DSERR_CONTROLUNAVAIL;
588         }
589
590         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
591                 WARN("invalid parameter: vol = %d\n", vol);
592                 return DSERR_INVALIDPARAM;
593         }
594
595         /* **** */
596         EnterCriticalSection(&device->mixlock);
597
598         hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
599         if(FAILED(hr)){
600                 LeaveCriticalSection(&device->mixlock);
601                 WARN("GetChannelVolume failed: %08x\n", hr);
602                 return hr;
603         }
604
605         if(device->pwfx->nChannels > 1){
606                 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
607                 if(FAILED(hr)){
608                         LeaveCriticalSection(&device->mixlock);
609                         WARN("GetChannelVolume failed: %08x\n", hr);
610                         return hr;
611                 }
612         }else
613                 rvol = 1;
614
615         device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
616         device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
617
618         DSOUND_AmpFactorToVolPan(&device->volpan);
619         if (vol != device->volpan.lVolume) {
620                 device->volpan.lVolume=vol;
621                 DSOUND_RecalcVolPan(&device->volpan);
622                 lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
623                 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
624                 if(FAILED(hr)){
625                         LeaveCriticalSection(&device->mixlock);
626                         WARN("SetChannelVolume failed: %08x\n", hr);
627                         return hr;
628                 }
629
630                 if(device->pwfx->nChannels > 1){
631                         rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
632                         hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
633                         if(FAILED(hr)){
634                                 LeaveCriticalSection(&device->mixlock);
635                                 WARN("SetChannelVolume failed: %08x\n", hr);
636                                 return hr;
637                         }
638                 }
639         }
640
641         LeaveCriticalSection(&(device->mixlock));
642         /* **** */
643
644         return DS_OK;
645 }
646
647 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(IDirectSoundBuffer *iface, LONG *vol)
648 {
649         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
650         DirectSoundDevice *device = This->device;
651         float lvol, rvol;
652         HRESULT hr;
653         TRACE("(%p,%p)\n", iface, vol);
654
655         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
656                 WARN("control unavailable\n");
657                 return DSERR_CONTROLUNAVAIL;
658         }
659
660         if (vol == NULL) {
661                 WARN("invalid parameter: vol = NULL\n");
662                 return DSERR_INVALIDPARAM;
663         }
664
665         EnterCriticalSection(&device->mixlock);
666
667         hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
668         if(FAILED(hr)){
669                 LeaveCriticalSection(&device->mixlock);
670                 WARN("GetChannelVolume failed: %08x\n", hr);
671                 return hr;
672         }
673
674         if(device->pwfx->nChannels > 1){
675                 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
676                 if(FAILED(hr)){
677                         LeaveCriticalSection(&device->mixlock);
678                         WARN("GetChannelVolume failed: %08x\n", hr);
679                         return hr;
680                 }
681         }else
682                 rvol = 1;
683
684         device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
685         device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
686
687         DSOUND_AmpFactorToVolPan(&device->volpan);
688         *vol = device->volpan.lVolume;
689
690         LeaveCriticalSection(&device->mixlock);
691
692         return DS_OK;
693 }
694
695 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
696 {
697         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
698         TRACE("(%p,%d)\n",This,freq);
699
700         /* You cannot set the frequency of the primary buffer */
701         WARN("control unavailable\n");
702         return DSERR_CONTROLUNAVAIL;
703 }
704
705 static HRESULT WINAPI PrimaryBufferImpl_Play(IDirectSoundBuffer *iface, DWORD reserved1,
706         DWORD reserved2, DWORD flags)
707 {
708         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
709         DirectSoundDevice *device = This->device;
710         TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
711
712         if (!(flags & DSBPLAY_LOOPING)) {
713                 WARN("invalid parameter: flags = %08x\n", flags);
714                 return DSERR_INVALIDPARAM;
715         }
716
717         /* **** */
718         EnterCriticalSection(&(device->mixlock));
719
720         if (device->state == STATE_STOPPED)
721                 device->state = STATE_STARTING;
722         else if (device->state == STATE_STOPPING)
723                 device->state = STATE_PLAYING;
724
725         LeaveCriticalSection(&(device->mixlock));
726         /* **** */
727
728         return DS_OK;
729 }
730
731 static HRESULT WINAPI PrimaryBufferImpl_Stop(IDirectSoundBuffer *iface)
732 {
733         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
734         DirectSoundDevice *device = This->device;
735         TRACE("(%p)\n", iface);
736
737         /* **** */
738         EnterCriticalSection(&(device->mixlock));
739
740         if (device->state == STATE_PLAYING)
741                 device->state = STATE_STOPPING;
742         else if (device->state == STATE_STARTING)
743                 device->state = STATE_STOPPED;
744
745         LeaveCriticalSection(&(device->mixlock));
746         /* **** */
747
748         return DS_OK;
749 }
750
751 static ULONG WINAPI PrimaryBufferImpl_AddRef(IDirectSoundBuffer *iface)
752 {
753     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
754     ULONG ref = InterlockedIncrement(&(This->ref));
755     TRACE("(%p) ref was %d\n", This, ref - 1);
756     if(ref == 1)
757         InterlockedIncrement(&This->numIfaces);
758     return ref;
759 }
760
761 /* Decreases *out by 1 to no less than 0.
762  * Returns the new value of *out. */
763 LONG capped_refcount_dec(LONG *out)
764 {
765     LONG ref, oldref;
766     do {
767         ref = *out;
768         if(!ref)
769             return 0;
770         oldref = InterlockedCompareExchange(out, ref - 1, ref);
771     } while(oldref != ref);
772     return ref - 1;
773 }
774
775 static ULONG WINAPI PrimaryBufferImpl_Release(IDirectSoundBuffer *iface)
776 {
777     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
778     ULONG ref;
779
780     ref = capped_refcount_dec(&This->ref);
781     if(!ref)
782         capped_refcount_dec(&This->numIfaces);
783
784     TRACE("(%p) primary ref is now %d\n", This, ref);
785
786     return ref;
787 }
788
789 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *iface,
790         DWORD *playpos, DWORD *writepos)
791 {
792         HRESULT hres;
793         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
794         DirectSoundDevice *device = This->device;
795         TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
796
797         /* **** */
798         EnterCriticalSection(&(device->mixlock));
799
800         hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
801         if (hres != DS_OK) {
802                 WARN("DSOUND_PrimaryGetPosition failed\n");
803                 LeaveCriticalSection(&(device->mixlock));
804                 return hres;
805         }
806         if (writepos) {
807                 if (device->state != STATE_STOPPED)
808                         /* apply the documented 10ms lead to writepos */
809                         *writepos += device->writelead;
810                 while (*writepos >= device->buflen) *writepos -= device->buflen;
811         }
812
813         LeaveCriticalSection(&(device->mixlock));
814         /* **** */
815
816         TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
817         return DS_OK;
818 }
819
820 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
821 {
822         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
823         DirectSoundDevice *device = This->device;
824         TRACE("(%p,%p)\n", iface, status);
825
826         if (status == NULL) {
827                 WARN("invalid parameter: status == NULL\n");
828                 return DSERR_INVALIDPARAM;
829         }
830
831         *status = 0;
832         if ((device->state == STATE_STARTING) ||
833             (device->state == STATE_PLAYING))
834                 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
835
836         TRACE("status=%x\n", *status);
837         return DS_OK;
838 }
839
840
841 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *lpwf,
842         DWORD wfsize, DWORD *wfwritten)
843 {
844     DWORD size;
845     IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
846     DirectSoundDevice *device = This->device;
847     TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
848
849     size = sizeof(WAVEFORMATEX) + device->primary_pwfx->cbSize;
850
851     if (lpwf) { /* NULL is valid */
852         if (wfsize >= size) {
853             CopyMemory(lpwf,device->primary_pwfx,size);
854             if (wfwritten)
855                 *wfwritten = size;
856         } else {
857             WARN("invalid parameter: wfsize too small\n");
858             if (wfwritten)
859                 *wfwritten = 0;
860             return DSERR_INVALIDPARAM;
861         }
862     } else {
863         if (wfwritten)
864             *wfwritten = sizeof(WAVEFORMATEX) + device->primary_pwfx->cbSize;
865         else {
866             WARN("invalid parameter: wfwritten == NULL\n");
867             return DSERR_INVALIDPARAM;
868         }
869     }
870
871     return DS_OK;
872 }
873
874 static HRESULT WINAPI PrimaryBufferImpl_Lock(IDirectSoundBuffer *iface, DWORD writecursor,
875         DWORD writebytes, void **lplpaudioptr1, DWORD *audiobytes1, void **lplpaudioptr2,
876         DWORD *audiobytes2, DWORD flags)
877 {
878         HRESULT hres;
879         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
880         DirectSoundDevice *device = This->device;
881         TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
882                 iface,
883                 writecursor,
884                 writebytes,
885                 lplpaudioptr1,
886                 audiobytes1,
887                 lplpaudioptr2,
888                 audiobytes2,
889                 flags,
890                 GetTickCount()
891         );
892
893         if (!audiobytes1)
894             return DSERR_INVALIDPARAM;
895
896         if (device->priolevel != DSSCL_WRITEPRIMARY) {
897                 WARN("failed priority check!\n");
898                 return DSERR_PRIOLEVELNEEDED;
899         }
900
901         /* when this flag is set, writecursor is meaningless and must be calculated */
902         if (flags & DSBLOCK_FROMWRITECURSOR) {
903                 /* GetCurrentPosition does too much magic to duplicate here */
904                 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
905                 if (hres != DS_OK) {
906                         WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
907                         return hres;
908                 }
909         }
910
911         /* when this flag is set, writebytes is meaningless and must be set */
912         if (flags & DSBLOCK_ENTIREBUFFER)
913                 writebytes = device->buflen;
914
915         if (writecursor >= device->buflen) {
916                 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
917                      writecursor, device->buflen);
918                 return DSERR_INVALIDPARAM;
919         }
920
921         if (writebytes > device->buflen) {
922                 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
923                      writebytes, device->buflen);
924                 return DSERR_INVALIDPARAM;
925         }
926
927         if (writecursor+writebytes <= device->buflen) {
928                 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
929                 *audiobytes1 = writebytes;
930                 if (lplpaudioptr2)
931                         *(LPBYTE*)lplpaudioptr2 = NULL;
932                 if (audiobytes2)
933                         *audiobytes2 = 0;
934                 TRACE("->%d.0\n",writebytes);
935         } else {
936                 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
937                 *audiobytes1 = device->buflen-writecursor;
938                 if (lplpaudioptr2)
939                         *(LPBYTE*)lplpaudioptr2 = device->buffer;
940                 if (audiobytes2)
941                         *audiobytes2 = writebytes-(device->buflen-writecursor);
942                 TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
943         }
944         return DS_OK;
945 }
946
947 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD newpos)
948 {
949         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
950         TRACE("(%p,%d)\n",This,newpos);
951
952         /* You cannot set the position of the primary buffer */
953         WARN("invalid call\n");
954         return DSERR_INVALIDCALL;
955 }
956
957 static HRESULT WINAPI PrimaryBufferImpl_SetPan(IDirectSoundBuffer *iface, LONG pan)
958 {
959         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
960         DirectSoundDevice *device = This->device;
961         float lvol, rvol;
962         HRESULT hr;
963         TRACE("(%p,%d)\n", iface, pan);
964
965         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
966                 WARN("control unavailable\n");
967                 return DSERR_CONTROLUNAVAIL;
968         }
969
970         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
971                 WARN("invalid parameter: pan = %d\n", pan);
972                 return DSERR_INVALIDPARAM;
973         }
974
975         /* **** */
976         EnterCriticalSection(&device->mixlock);
977
978         hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
979         if(FAILED(hr)){
980                 LeaveCriticalSection(&device->mixlock);
981                 WARN("GetChannelVolume failed: %08x\n", hr);
982                 return hr;
983         }
984
985         if(device->pwfx->nChannels > 1){
986                 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
987                 if(FAILED(hr)){
988                         LeaveCriticalSection(&device->mixlock);
989                         WARN("GetChannelVolume failed: %08x\n", hr);
990                         return hr;
991                 }
992         }else
993                 rvol = 1;
994
995         device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
996         device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
997
998         DSOUND_AmpFactorToVolPan(&device->volpan);
999         if (pan != device->volpan.lPan) {
1000                 device->volpan.lPan=pan;
1001                 DSOUND_RecalcVolPan(&device->volpan);
1002
1003                 lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
1004                 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
1005                 if(FAILED(hr)){
1006                         LeaveCriticalSection(&device->mixlock);
1007                         WARN("SetChannelVolume failed: %08x\n", hr);
1008                         return hr;
1009                 }
1010
1011                 if(device->pwfx->nChannels > 1){
1012                         rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
1013                         hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
1014                         if(FAILED(hr)){
1015                                 LeaveCriticalSection(&device->mixlock);
1016                                 WARN("SetChannelVolume failed: %08x\n", hr);
1017                                 return hr;
1018                         }
1019                 }
1020         }
1021
1022         LeaveCriticalSection(&device->mixlock);
1023         /* **** */
1024
1025         return DS_OK;
1026 }
1027
1028 static HRESULT WINAPI PrimaryBufferImpl_GetPan(IDirectSoundBuffer *iface, LONG *pan)
1029 {
1030         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1031         DirectSoundDevice *device = This->device;
1032         float lvol, rvol;
1033         HRESULT hr;
1034         TRACE("(%p,%p)\n", iface, pan);
1035
1036         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
1037                 WARN("control unavailable\n");
1038                 return DSERR_CONTROLUNAVAIL;
1039         }
1040
1041         if (pan == NULL) {
1042                 WARN("invalid parameter: pan == NULL\n");
1043                 return DSERR_INVALIDPARAM;
1044         }
1045
1046         EnterCriticalSection(&device->mixlock);
1047
1048         hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
1049         if(FAILED(hr)){
1050                 LeaveCriticalSection(&device->mixlock);
1051                 WARN("GetChannelVolume failed: %08x\n", hr);
1052                 return hr;
1053         }
1054
1055         if(device->pwfx->nChannels > 1){
1056                 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
1057                 if(FAILED(hr)){
1058                         LeaveCriticalSection(&device->mixlock);
1059                         WARN("GetChannelVolume failed: %08x\n", hr);
1060                         return hr;
1061                 }
1062         }else
1063                 rvol = 1;
1064
1065         device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
1066         device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
1067
1068         DSOUND_AmpFactorToVolPan(&device->volpan);
1069         *pan = device->volpan.lPan;
1070
1071         LeaveCriticalSection(&device->mixlock);
1072
1073         return DS_OK;
1074 }
1075
1076 static HRESULT WINAPI PrimaryBufferImpl_Unlock(IDirectSoundBuffer *iface, void *p1, DWORD x1,
1077         void *p2, DWORD x2)
1078 {
1079         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1080         DirectSoundDevice *device = This->device;
1081         TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
1082
1083         if (device->priolevel != DSSCL_WRITEPRIMARY) {
1084                 WARN("failed priority check!\n");
1085                 return DSERR_PRIOLEVELNEEDED;
1086         }
1087
1088         if ((p1 && ((BYTE*)p1 < device->buffer || (BYTE*)p1 >= device->buffer + device->buflen)) ||
1089             (p2 && ((BYTE*)p2 < device->buffer || (BYTE*)p2 >= device->buffer + device->buflen)))
1090                 return DSERR_INVALIDPARAM;
1091
1092         return DS_OK;
1093 }
1094
1095 static HRESULT WINAPI PrimaryBufferImpl_Restore(IDirectSoundBuffer *iface)
1096 {
1097         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1098         FIXME("(%p):stub\n",This);
1099         return DS_OK;
1100 }
1101
1102 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
1103 {
1104         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1105         DirectSoundDevice *device = This->device;
1106         TRACE("(%p,%p)\n", iface, freq);
1107
1108         if (freq == NULL) {
1109                 WARN("invalid parameter: freq == NULL\n");
1110                 return DSERR_INVALIDPARAM;
1111         }
1112
1113         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1114                 WARN("control unavailable\n");
1115                 return DSERR_CONTROLUNAVAIL;
1116         }
1117
1118         *freq = device->pwfx->nSamplesPerSec;
1119         TRACE("-> %d\n", *freq);
1120
1121         return DS_OK;
1122 }
1123
1124 static HRESULT WINAPI PrimaryBufferImpl_Initialize(IDirectSoundBuffer *iface, IDirectSound *dsound,
1125         const DSBUFFERDESC *dbsd)
1126 {
1127         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1128         WARN("(%p) already initialized\n", This);
1129         return DSERR_ALREADYINITIALIZED;
1130 }
1131
1132 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
1133 {
1134         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1135         DirectSoundDevice *device = This->device;
1136         TRACE("(%p,%p)\n", iface, caps);
1137
1138         if (caps == NULL) {
1139                 WARN("invalid parameter: caps == NULL\n");
1140                 return DSERR_INVALIDPARAM;
1141         }
1142
1143         if (caps->dwSize < sizeof(*caps)) {
1144                 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1145                 return DSERR_INVALIDPARAM;
1146         }
1147
1148         caps->dwFlags = This->dsbd.dwFlags;
1149         caps->dwBufferBytes = device->buflen;
1150
1151         /* Windows reports these as zero */
1152         caps->dwUnlockTransferRate = 0;
1153         caps->dwPlayCpuOverhead = 0;
1154
1155         return DS_OK;
1156 }
1157
1158 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(IDirectSoundBuffer *iface, REFIID riid,
1159         void **ppobj)
1160 {
1161         IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1162
1163         TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1164
1165         if (ppobj == NULL) {
1166                 WARN("invalid parameter\n");
1167                 return E_INVALIDARG;
1168         }
1169
1170         *ppobj = NULL;  /* assume failure */
1171
1172         if ( IsEqualGUID(riid, &IID_IUnknown) ||
1173              IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1174                 IDirectSoundBuffer_AddRef(iface);
1175                 *ppobj = iface;
1176                 return S_OK;
1177         }
1178
1179         /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1180         /* a primary buffer can't have a DirectSoundBuffer8 interface */
1181         if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1182                 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1183                 return E_NOINTERFACE;
1184         }
1185
1186         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1187                 ERR("app requested IDirectSoundNotify on primary buffer\n");
1188                 /* FIXME: should we support this? */
1189                 return E_NOINTERFACE;
1190         }
1191
1192         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1193                 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1194                 return E_NOINTERFACE;
1195         }
1196
1197         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1198                 *ppobj = &This->IDirectSound3DListener_iface;
1199                 IDirectSound3DListener_AddRef(&This->IDirectSound3DListener_iface);
1200                 return S_OK;
1201         }
1202
1203         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1204                 *ppobj = &This->IKsPropertySet_iface;
1205                 IKsPropertySet_AddRef(&This->IKsPropertySet_iface);
1206                 return S_OK;
1207         }
1208
1209         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1210         return E_NOINTERFACE;
1211 }
1212
1213 static const IDirectSoundBufferVtbl dspbvt =
1214 {
1215         PrimaryBufferImpl_QueryInterface,
1216         PrimaryBufferImpl_AddRef,
1217         PrimaryBufferImpl_Release,
1218         PrimaryBufferImpl_GetCaps,
1219         PrimaryBufferImpl_GetCurrentPosition,
1220         PrimaryBufferImpl_GetFormat,
1221         PrimaryBufferImpl_GetVolume,
1222         PrimaryBufferImpl_GetPan,
1223         PrimaryBufferImpl_GetFrequency,
1224         PrimaryBufferImpl_GetStatus,
1225         PrimaryBufferImpl_Initialize,
1226         PrimaryBufferImpl_Lock,
1227         PrimaryBufferImpl_Play,
1228         PrimaryBufferImpl_SetCurrentPosition,
1229         PrimaryBufferImpl_SetFormat,
1230         PrimaryBufferImpl_SetVolume,
1231         PrimaryBufferImpl_SetPan,
1232         PrimaryBufferImpl_SetFrequency,
1233         PrimaryBufferImpl_Stop,
1234         PrimaryBufferImpl_Unlock,
1235         PrimaryBufferImpl_Restore
1236 };
1237
1238 HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb,
1239         const DSBUFFERDESC *dsbd)
1240 {
1241         IDirectSoundBufferImpl *dsb;
1242         TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1243
1244         if (dsbd->lpwfxFormat) {
1245                 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1246                 *ppdsb = NULL;
1247                 return DSERR_INVALIDPARAM;
1248         }
1249
1250         dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1251
1252         if (dsb == NULL) {
1253                 WARN("out of memory\n");
1254                 *ppdsb = NULL;
1255                 return DSERR_OUTOFMEMORY;
1256         }
1257
1258         dsb->ref = 0;
1259         dsb->ref3D = 0;
1260         dsb->refiks = 0;
1261         dsb->numIfaces = 0;
1262         dsb->device = device;
1263         dsb->IDirectSoundBuffer8_iface.lpVtbl = (IDirectSoundBuffer8Vtbl *)&dspbvt;
1264         dsb->IDirectSound3DListener_iface.lpVtbl = &ds3dlvt;
1265         dsb->IKsPropertySet_iface.lpVtbl = &iksbvt;
1266         dsb->dsbd = *dsbd;
1267
1268         /* IDirectSound3DListener */
1269         device->ds3dl.dwSize = sizeof(DS3DLISTENER);
1270         device->ds3dl.vPosition.x = 0.0;
1271         device->ds3dl.vPosition.y = 0.0;
1272         device->ds3dl.vPosition.z = 0.0;
1273         device->ds3dl.vVelocity.x = 0.0;
1274         device->ds3dl.vVelocity.y = 0.0;
1275         device->ds3dl.vVelocity.z = 0.0;
1276         device->ds3dl.vOrientFront.x = 0.0;
1277         device->ds3dl.vOrientFront.y = 0.0;
1278         device->ds3dl.vOrientFront.z = 1.0;
1279         device->ds3dl.vOrientTop.x = 0.0;
1280         device->ds3dl.vOrientTop.y = 1.0;
1281         device->ds3dl.vOrientTop.z = 0.0;
1282         device->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1283         device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1284         device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
1285         device->ds3dl_need_recalc = TRUE;
1286
1287         TRACE("Created primary buffer at %p\n", dsb);
1288         TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1289                 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1290                 device->pwfx->wFormatTag, device->pwfx->nChannels,
1291                 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1292                 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1293                 device->pwfx->cbSize);
1294
1295         IDirectSoundBuffer_AddRef(&dsb->IDirectSoundBuffer8_iface);
1296         *ppdsb = dsb;
1297         return S_OK;
1298 }