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