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