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