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