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