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