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