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