dsound: Fix volume and panning for primary buffer.
[wine/wine64.git] / dlls / dsound / primary.c
blob661cd8ca0482377d3c0d1c2161f5b21121ae3c88
1 /* DirectSound
3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2002 TransGaming Technologies, Inc.
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.
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.
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
22 #include <stdarg.h>
24 #define NONAMELESSSTRUCT
25 #define NONAMELESSUNION
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "mmsystem.h"
30 #include "winternl.h"
31 #include "mmddk.h"
32 #include "wine/debug.h"
33 #include "dsound.h"
34 #include "dsdriver.h"
35 #include "dsound_private.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
39 /** Calculate how long a fragment length of about 10 ms should be in frames
41 * nSamplesPerSec: Frequency rate in samples per second
42 * nBlockAlign: Size of a single blockalign
44 * Returns:
45 * Size in bytes of a single fragment
47 DWORD DSOUND_fraglen(DWORD nSamplesPerSec, DWORD nBlockAlign)
49 DWORD fraglen = 256 * nBlockAlign;
51 /* Compensate for only being roughly accurate */
52 if (nSamplesPerSec <= 26000)
53 fraglen /= 2;
55 if (nSamplesPerSec <= 10000)
56 fraglen /= 2;
58 if (nSamplesPerSec >= 80000)
59 fraglen *= 2;
61 return fraglen;
64 static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
66 TRACE("(%p)\n", device);
68 device->fraglen = DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign);
69 device->helfrags = device->buflen / device->fraglen;
70 TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
72 if (device->hwbuf && device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD)
73 device->writelead = 0;
74 else
75 /* calculate the 10ms write lead */
76 device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
79 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
81 HRESULT hres = DS_OK;
82 TRACE("(%p, %d)\n", device, forcewave);
84 if (device->driver)
86 IDsDriver_Close(device->driver);
87 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
88 waveOutClose(device->hwo);
89 IDsDriver_Release(device->driver);
90 device->driver = NULL;
91 device->buffer = NULL;
92 device->hwo = 0;
94 else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
95 waveOutClose(device->hwo);
97 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
98 if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
99 waveOutMessage((HWAVEOUT)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);
101 /* Get driver description */
102 if (device->driver) {
103 DWORD wod = device->drvdesc.dnDevNode;
104 hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
105 device->drvdesc.dnDevNode = wod;
106 if (FAILED(hres)) {
107 WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
108 IDsDriver_Release(device->driver);
109 device->driver = NULL;
113 /* if no DirectSound interface available, use WINMM API instead */
114 if (!device->driver)
115 device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
117 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
119 DWORD flags = CALLBACK_FUNCTION;
121 if (device->driver)
122 flags |= WAVE_DIRECTSOUND;
124 hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, flags));
125 if (FAILED(hres)) {
126 WARN("waveOutOpen failed\n");
127 if (device->driver)
129 IDsDriver_Release(device->driver);
130 device->driver = NULL;
132 return hres;
136 if (device->driver)
137 hres = IDsDriver_Open(device->driver);
139 return hres;
142 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
144 DWORD buflen;
145 HRESULT err = DS_OK;
146 TRACE("(%p)\n", device);
148 /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
149 on windows this size is always fixed (tested on win-xp) */
150 if (!device->buflen)
151 device->buflen = ds_hel_buflen;
152 buflen = device->buflen;
153 buflen -= buflen % device->pwfx->nBlockAlign;
154 device->buflen = buflen;
156 if (device->driver)
158 err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
159 DSBCAPS_PRIMARYBUFFER,0,
160 &(device->buflen),&(device->buffer),
161 (LPVOID*)&(device->hwbuf));
163 if (err != DS_OK) {
164 WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
165 err = DSOUND_ReopenDevice(device, TRUE);
166 if (FAILED(err))
168 WARN("Falling back to waveout failed too! Giving up\n");
169 return err;
172 if (device->hwbuf)
173 IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
175 DSOUND_RecalcPrimary(device);
176 device->prebuf = ds_snd_queue_max;
177 if (device->helfrags < ds_snd_queue_min)
179 WARN("Too little sound buffer to be effective (%d/%d) falling back to waveout\n", device->buflen, ds_snd_queue_min * device->fraglen);
180 device->buflen = buflen;
181 IDsDriverBuffer_Release(device->hwbuf);
182 device->hwbuf = NULL;
183 err = DSOUND_ReopenDevice(device, TRUE);
184 if (FAILED(err))
186 WARN("Falling back to waveout failed too! Giving up\n");
187 return err;
190 else if (device->helfrags < ds_snd_queue_max)
191 device->prebuf = device->helfrags;
194 device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
195 device->mix_buffer = HeapAlloc(GetProcessHeap(), 0, device->mix_buffer_len);
196 if (!device->mix_buffer)
198 if (device->hwbuf)
199 IDsDriverBuffer_Release(device->hwbuf);
200 device->hwbuf = NULL;
201 return DSERR_OUTOFMEMORY;
204 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
205 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
207 /* are we using waveOut stuff? */
208 if (!device->driver) {
209 LPBYTE newbuf;
210 LPWAVEHDR headers = NULL;
211 DWORD overshot;
212 unsigned int c;
214 /* Start in pause mode, to allow buffers to get filled */
215 waveOutPause(device->hwo);
217 TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
219 /* reallocate emulated primary buffer */
220 if (device->buffer)
221 newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, buflen);
222 else
223 newbuf = HeapAlloc(GetProcessHeap(),0, buflen);
225 if (!newbuf) {
226 ERR("failed to allocate primary buffer\n");
227 return DSERR_OUTOFMEMORY;
228 /* but the old buffer might still exist and must be re-prepared */
231 DSOUND_RecalcPrimary(device);
232 if (device->pwave)
233 headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
234 else
235 headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));
237 if (!headers) {
238 ERR("failed to allocate wave headers\n");
239 HeapFree(GetProcessHeap(), 0, newbuf);
240 DSOUND_RecalcPrimary(device);
241 return DSERR_OUTOFMEMORY;
244 device->buffer = newbuf;
245 device->pwave = headers;
247 /* prepare fragment headers */
248 for (c=0; c<device->helfrags; c++) {
249 device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
250 device->pwave[c].dwBufferLength = device->fraglen;
251 device->pwave[c].dwUser = (DWORD)device;
252 device->pwave[c].dwFlags = 0;
253 device->pwave[c].dwLoops = 0;
254 err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
255 if (err != DS_OK) {
256 while (c--)
257 waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
258 break;
262 overshot = device->buflen % device->fraglen;
263 /* sanity */
264 if(overshot)
266 overshot -= overshot % device->pwfx->nBlockAlign;
267 device->pwave[device->helfrags - 1].dwBufferLength += overshot;
270 TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot);
272 device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
273 device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
274 FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
275 FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
276 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
277 return err;
281 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
283 TRACE("(%p)\n", device);
285 /* are we using waveOut stuff? */
286 if (!device->hwbuf) {
287 unsigned c;
289 /* get out of CS when calling the wave system */
290 LeaveCriticalSection(&(device->mixlock));
291 /* **** */
292 device->pwqueue = (DWORD)-1; /* resetting queues */
293 waveOutReset(device->hwo);
294 for (c=0; c<device->helfrags; c++)
295 waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
296 /* **** */
297 EnterCriticalSection(&(device->mixlock));
299 /* clear the queue */
300 device->pwqueue = 0;
301 } else {
302 ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
303 if (!ref)
304 device->hwbuf = 0;
305 else
306 ERR("Still %d references on primary buffer, refcount leak?\n", ref);
310 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
312 HRESULT err = DS_OK;
313 TRACE("(%p)\n", device);
315 device->buflen = ds_hel_buflen;
316 err = DSOUND_PrimaryOpen(device);
318 if (err != DS_OK) {
319 WARN("DSOUND_PrimaryOpen failed\n");
320 return err;
323 device->state = STATE_STOPPED;
324 return DS_OK;
327 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
329 TRACE("(%p)\n", device);
331 /* **** */
332 EnterCriticalSection(&(device->mixlock));
334 DSOUND_PrimaryClose(device);
335 if (device->driver) {
336 if (device->hwbuf) {
337 if (IDsDriverBuffer_Release(device->hwbuf) == 0)
338 device->hwbuf = 0;
340 } else
341 HeapFree(GetProcessHeap(),0,device->pwave);
342 HeapFree(GetProcessHeap(),0,device->pwfx);
343 device->pwfx=NULL;
345 LeaveCriticalSection(&(device->mixlock));
346 /* **** */
348 return DS_OK;
351 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
353 HRESULT err = DS_OK;
354 TRACE("(%p)\n", device);
356 if (device->hwbuf) {
357 err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
358 if (err != DS_OK)
359 WARN("IDsDriverBuffer_Play failed\n");
360 } else {
361 err = mmErr(waveOutRestart(device->hwo));
362 if (err != DS_OK)
363 WARN("waveOutRestart failed\n");
366 return err;
369 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
371 HRESULT err = DS_OK;
372 TRACE("(%p)\n", device);
374 if (device->hwbuf) {
375 err = IDsDriverBuffer_Stop(device->hwbuf);
376 if (err == DSERR_BUFFERLOST) {
377 DSOUND_PrimaryClose(device);
378 err = DSOUND_ReopenDevice(device, FALSE);
379 if (FAILED(err))
380 ERR("DSOUND_ReopenDevice failed\n");
381 else
383 err = DSOUND_PrimaryOpen(device);
384 if (FAILED(err))
385 WARN("DSOUND_PrimaryOpen failed\n");
387 } else if (err != DS_OK) {
388 WARN("IDsDriverBuffer_Stop failed\n");
390 } else {
392 /* don't call the wave system with the lock set */
393 LeaveCriticalSection(&(device->mixlock));
394 /* **** */
396 err = mmErr(waveOutPause(device->hwo));
398 /* **** */
399 EnterCriticalSection(&(device->mixlock));
401 if (err != DS_OK)
402 WARN("waveOutPause failed\n");
405 return err;
408 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
410 TRACE("(%p,%p,%p)\n", device, playpos, writepos);
412 if (device->hwbuf) {
413 HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
414 if (err != S_OK) {
415 WARN("IDsDriverBuffer_GetPosition failed\n");
416 return err;
418 } else {
419 TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
421 /* check if playpos was requested */
422 if (playpos)
423 /* use the cached play position */
424 *playpos = device->pwplay * device->fraglen;
426 /* check if writepos was requested */
427 if (writepos)
428 /* the writepos is the first non-queued position */
429 *writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
431 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
432 return DS_OK;
435 HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, BOOL forced)
437 HRESULT err = DSERR_BUFFERLOST;
438 int i, alloc_size, cp_size;
439 DWORD nSamplesPerSec, bpp, chans;
440 TRACE("(%p,%p)\n", device, wfex);
442 if (device->priolevel == DSSCL_NORMAL) {
443 WARN("failed priority check!\n");
444 return DSERR_PRIOLEVELNEEDED;
447 /* Let's be pedantic! */
448 if (wfex == NULL) {
449 WARN("invalid parameter: wfex==NULL!\n");
450 return DSERR_INVALIDPARAM;
452 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
453 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
454 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
455 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
456 wfex->wBitsPerSample, wfex->cbSize);
458 /* **** */
459 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
460 EnterCriticalSection(&(device->mixlock));
462 if (wfex->wFormatTag == WAVE_FORMAT_PCM) {
463 alloc_size = sizeof(WAVEFORMATEX);
464 cp_size = sizeof(PCMWAVEFORMAT);
465 } else
466 alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize;
468 device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size);
470 nSamplesPerSec = device->pwfx->nSamplesPerSec;
471 bpp = device->pwfx->wBitsPerSample;
472 chans = device->pwfx->nChannels;
474 CopyMemory(device->pwfx, wfex, cp_size);
476 if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
477 err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
479 /* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
480 if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
482 err = DSERR_BUFFERLOST;
483 CopyMemory(device->pwfx, wfex, cp_size);
486 if (err != DSERR_BUFFERLOST && FAILED(err)) {
487 WARN("IDsDriverBuffer_SetFormat failed\n");
488 if (!forced)
489 err = DS_OK;
490 goto done;
493 if (err == S_FALSE)
495 /* ALSA specific: S_FALSE tells that recreation was successful,
496 * but size and location may be changed, and buffer has to be restarted
497 * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
498 * and the entire re-initialization will occur anyway
500 IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
501 IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);
503 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
504 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
505 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
506 err = DS_OK;
508 DSOUND_RecalcPrimary(device);
511 if (err == DSERR_BUFFERLOST)
513 DSOUND_PrimaryClose(device);
515 err = DSOUND_ReopenDevice(device, FALSE);
516 if (FAILED(err))
518 WARN("DSOUND_ReopenDevice failed: %08x\n", err);
519 goto done;
521 err = DSOUND_PrimaryOpen(device);
522 if (err != DS_OK) {
523 WARN("DSOUND_PrimaryOpen failed\n");
524 goto done;
527 if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
529 DSOUND_PrimaryClose(device);
530 device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec;
531 err = DSOUND_ReopenDevice(device, TRUE);
532 if (FAILED(err))
533 WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
534 else if (FAILED((err = DSOUND_PrimaryOpen(device))))
535 WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
539 device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
540 device->mix_buffer = HeapReAlloc(GetProcessHeap(), 0, device->mix_buffer, device->mix_buffer_len);
541 FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
542 device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
543 device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
545 if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
546 IDirectSoundBufferImpl** dsb = device->buffers;
547 for (i = 0; i < device->nrofbuffers; i++, dsb++) {
548 /* **** */
549 RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
551 (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
552 DSOUND_RecalcFormat((*dsb));
553 DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen, FALSE);
554 (*dsb)->primary_mixpos = 0;
556 RtlReleaseResource(&(*dsb)->lock);
557 /* **** */
561 done:
562 LeaveCriticalSection(&(device->mixlock));
563 RtlReleaseResource(&(device->buffer_list_lock));
564 /* **** */
566 return err;
569 /*******************************************************************************
570 * PrimaryBuffer
572 /* This sets this format for the <em>Primary Buffer Only</em> */
573 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
574 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
575 LPDIRECTSOUNDBUFFER iface,
576 LPCWAVEFORMATEX wfex)
578 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
579 TRACE("(%p,%p)\n", iface, wfex);
580 return DSOUND_PrimarySetFormat(device, wfex, device->priolevel == DSSCL_WRITEPRIMARY);
583 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
584 LPDIRECTSOUNDBUFFER iface,LONG vol
586 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
587 DWORD ampfactors;
588 HRESULT hres = DS_OK;
589 TRACE("(%p,%d)\n", iface, vol);
591 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
592 WARN("control unavailable\n");
593 return DSERR_CONTROLUNAVAIL;
596 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
597 WARN("invalid parameter: vol = %d\n", vol);
598 return DSERR_INVALIDPARAM;
601 /* **** */
602 EnterCriticalSection(&(device->mixlock));
604 waveOutGetVolume(device->hwo, &ampfactors);
605 device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
606 device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
607 DSOUND_AmpFactorToVolPan(&device->volpan);
608 if (vol != device->volpan.lVolume) {
609 device->volpan.lVolume=vol;
610 DSOUND_RecalcVolPan(&device->volpan);
611 if (device->hwbuf) {
612 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
613 if (hres != DS_OK)
614 WARN("IDsDriverBuffer_SetVolumePan failed\n");
615 } else {
616 ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
617 waveOutSetVolume(device->hwo, ampfactors);
621 LeaveCriticalSection(&(device->mixlock));
622 /* **** */
624 return hres;
627 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
628 LPDIRECTSOUNDBUFFER iface,LPLONG vol
630 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
631 DWORD ampfactors;
632 TRACE("(%p,%p)\n", iface, vol);
634 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
635 WARN("control unavailable\n");
636 return DSERR_CONTROLUNAVAIL;
639 if (vol == NULL) {
640 WARN("invalid parameter: vol = NULL\n");
641 return DSERR_INVALIDPARAM;
644 if (!device->hwbuf)
646 waveOutGetVolume(device->hwo, &ampfactors);
647 device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
648 device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
649 DSOUND_AmpFactorToVolPan(&device->volpan);
651 *vol = device->volpan.lVolume;
652 return DS_OK;
655 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
656 LPDIRECTSOUNDBUFFER iface,DWORD freq
658 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
659 TRACE("(%p,%d)\n",This,freq);
661 /* You cannot set the frequency of the primary buffer */
662 WARN("control unavailable\n");
663 return DSERR_CONTROLUNAVAIL;
666 static HRESULT WINAPI PrimaryBufferImpl_Play(
667 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
669 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
670 TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
672 if (!(flags & DSBPLAY_LOOPING)) {
673 WARN("invalid parameter: flags = %08x\n", flags);
674 return DSERR_INVALIDPARAM;
677 /* **** */
678 EnterCriticalSection(&(device->mixlock));
680 if (device->state == STATE_STOPPED)
681 device->state = STATE_STARTING;
682 else if (device->state == STATE_STOPPING)
683 device->state = STATE_PLAYING;
685 LeaveCriticalSection(&(device->mixlock));
686 /* **** */
688 return DS_OK;
691 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
693 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
694 TRACE("(%p)\n", iface);
696 /* **** */
697 EnterCriticalSection(&(device->mixlock));
699 if (device->state == STATE_PLAYING)
700 device->state = STATE_STOPPING;
701 else if (device->state == STATE_STARTING)
702 device->state = STATE_STOPPED;
704 LeaveCriticalSection(&(device->mixlock));
705 /* **** */
707 return DS_OK;
710 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
712 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
713 ULONG ref = InterlockedIncrement(&(This->ref));
714 TRACE("(%p) ref was %d\n", This, ref - 1);
715 return ref;
718 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
720 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
721 DWORD ref = InterlockedDecrement(&(This->ref));
722 TRACE("(%p) ref was %d\n", This, ref + 1);
724 if (!ref) {
725 This->device->primary = NULL;
726 HeapFree(GetProcessHeap(), 0, This);
727 TRACE("(%p) released\n", This);
729 return ref;
732 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
733 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
735 HRESULT hres;
736 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
737 TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
739 /* **** */
740 EnterCriticalSection(&(device->mixlock));
742 hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
743 if (hres != DS_OK) {
744 WARN("DSOUND_PrimaryGetPosition failed\n");
745 LeaveCriticalSection(&(device->mixlock));
746 return hres;
748 if (writepos) {
749 if (device->state != STATE_STOPPED)
750 /* apply the documented 10ms lead to writepos */
751 *writepos += device->writelead;
752 while (*writepos >= device->buflen) *writepos -= device->buflen;
755 LeaveCriticalSection(&(device->mixlock));
756 /* **** */
758 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
759 return DS_OK;
762 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
763 LPDIRECTSOUNDBUFFER iface,LPDWORD status
765 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
766 TRACE("(%p,%p)\n", iface, status);
768 if (status == NULL) {
769 WARN("invalid parameter: status == NULL\n");
770 return DSERR_INVALIDPARAM;
773 *status = 0;
774 if ((device->state == STATE_STARTING) ||
775 (device->state == STATE_PLAYING))
776 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
778 TRACE("status=%x\n", *status);
779 return DS_OK;
783 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
784 LPDIRECTSOUNDBUFFER iface,
785 LPWAVEFORMATEX lpwf,
786 DWORD wfsize,
787 LPDWORD wfwritten)
789 DWORD size;
790 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
791 TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
793 size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
795 if (lpwf) { /* NULL is valid */
796 if (wfsize >= size) {
797 CopyMemory(lpwf,device->pwfx,size);
798 if (wfwritten)
799 *wfwritten = size;
800 } else {
801 WARN("invalid parameter: wfsize too small\n");
802 if (wfwritten)
803 *wfwritten = 0;
804 return DSERR_INVALIDPARAM;
806 } else {
807 if (wfwritten)
808 *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
809 else {
810 WARN("invalid parameter: wfwritten == NULL\n");
811 return DSERR_INVALIDPARAM;
815 return DS_OK;
818 static HRESULT WINAPI PrimaryBufferImpl_Lock(
819 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
821 HRESULT hres;
822 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
823 TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
824 iface,
825 writecursor,
826 writebytes,
827 lplpaudioptr1,
828 audiobytes1,
829 lplpaudioptr2,
830 audiobytes2,
831 flags,
832 GetTickCount()
835 if (device->priolevel != DSSCL_WRITEPRIMARY) {
836 WARN("failed priority check!\n");
837 return DSERR_PRIOLEVELNEEDED;
840 /* when this flag is set, writecursor is meaningless and must be calculated */
841 if (flags & DSBLOCK_FROMWRITECURSOR) {
842 /* GetCurrentPosition does too much magic to duplicate here */
843 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
844 if (hres != DS_OK) {
845 WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
846 return hres;
850 /* when this flag is set, writebytes is meaningless and must be set */
851 if (flags & DSBLOCK_ENTIREBUFFER)
852 writebytes = device->buflen;
854 if (writecursor >= device->buflen) {
855 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
856 writecursor, device->buflen);
857 return DSERR_INVALIDPARAM;
860 if (writebytes > device->buflen) {
861 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
862 writebytes, device->buflen);
863 return DSERR_INVALIDPARAM;
866 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
867 hres = IDsDriverBuffer_Lock(device->hwbuf,
868 lplpaudioptr1, audiobytes1,
869 lplpaudioptr2, audiobytes2,
870 writecursor, writebytes,
872 if (hres != DS_OK) {
873 WARN("IDsDriverBuffer_Lock failed\n");
874 return hres;
876 } else {
877 if (writecursor+writebytes <= device->buflen) {
878 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
879 *audiobytes1 = writebytes;
880 if (lplpaudioptr2)
881 *(LPBYTE*)lplpaudioptr2 = NULL;
882 if (audiobytes2)
883 *audiobytes2 = 0;
884 TRACE("->%d.0\n",writebytes);
885 } else {
886 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
887 *audiobytes1 = device->buflen-writecursor;
888 if (lplpaudioptr2)
889 *(LPBYTE*)lplpaudioptr2 = device->buffer;
890 if (audiobytes2)
891 *audiobytes2 = writebytes-(device->buflen-writecursor);
892 TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
895 return DS_OK;
898 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
899 LPDIRECTSOUNDBUFFER iface,DWORD newpos
901 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
902 TRACE("(%p,%d)\n",This,newpos);
904 /* You cannot set the position of the primary buffer */
905 WARN("invalid call\n");
906 return DSERR_INVALIDCALL;
909 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
910 LPDIRECTSOUNDBUFFER iface,LONG pan
912 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
913 DWORD ampfactors;
914 HRESULT hres = DS_OK;
915 TRACE("(%p,%d)\n", iface, pan);
917 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
918 WARN("control unavailable\n");
919 return DSERR_CONTROLUNAVAIL;
922 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
923 WARN("invalid parameter: pan = %d\n", pan);
924 return DSERR_INVALIDPARAM;
927 /* **** */
928 EnterCriticalSection(&(device->mixlock));
930 if (!device->hwbuf)
932 waveOutGetVolume(device->hwo, &ampfactors);
933 device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
934 device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
935 DSOUND_AmpFactorToVolPan(&device->volpan);
937 if (pan != device->volpan.lPan) {
938 device->volpan.lPan=pan;
939 DSOUND_RecalcVolPan(&device->volpan);
940 if (device->hwbuf) {
941 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &device->volpan);
942 if (hres != DS_OK)
943 WARN("IDsDriverBuffer_SetVolumePan failed\n");
944 } else {
945 ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16);
946 waveOutSetVolume(device->hwo, ampfactors);
950 LeaveCriticalSection(&(device->mixlock));
951 /* **** */
953 return hres;
956 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
957 LPDIRECTSOUNDBUFFER iface,LPLONG pan
959 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
960 DWORD ampfactors;
961 TRACE("(%p,%p)\n", iface, pan);
963 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
964 WARN("control unavailable\n");
965 return DSERR_CONTROLUNAVAIL;
968 if (pan == NULL) {
969 WARN("invalid parameter: pan == NULL\n");
970 return DSERR_INVALIDPARAM;
973 if (!device->hwbuf)
975 waveOutGetVolume(device->hwo, &ampfactors);
976 device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
977 device->volpan.dwTotalRightAmpFactor=ampfactors >> 16;
978 DSOUND_AmpFactorToVolPan(&device->volpan);
980 *pan = device->volpan.lPan;
981 return DS_OK;
984 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
985 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
987 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
988 TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
990 if (device->priolevel != DSSCL_WRITEPRIMARY) {
991 WARN("failed priority check!\n");
992 return DSERR_PRIOLEVELNEEDED;
995 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
996 HRESULT hres;
998 hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
999 if (hres != DS_OK) {
1000 WARN("IDsDriverBuffer_Unlock failed\n");
1001 return hres;
1005 return DS_OK;
1008 static HRESULT WINAPI PrimaryBufferImpl_Restore(
1009 LPDIRECTSOUNDBUFFER iface
1011 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1012 FIXME("(%p):stub\n",This);
1013 return DS_OK;
1016 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
1017 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1019 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1020 TRACE("(%p,%p)\n", iface, freq);
1022 if (freq == NULL) {
1023 WARN("invalid parameter: freq == NULL\n");
1024 return DSERR_INVALIDPARAM;
1027 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1028 WARN("control unavailable\n");
1029 return DSERR_CONTROLUNAVAIL;
1032 *freq = device->pwfx->nSamplesPerSec;
1033 TRACE("-> %d\n", *freq);
1035 return DS_OK;
1038 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
1039 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
1041 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1042 WARN("(%p) already initialized\n", This);
1043 return DSERR_ALREADYINITIALIZED;
1046 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
1047 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1049 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1050 TRACE("(%p,%p)\n", iface, caps);
1052 if (caps == NULL) {
1053 WARN("invalid parameter: caps == NULL\n");
1054 return DSERR_INVALIDPARAM;
1057 if (caps->dwSize < sizeof(*caps)) {
1058 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1059 return DSERR_INVALIDPARAM;
1062 caps->dwFlags = device->dsbd.dwFlags;
1063 caps->dwBufferBytes = device->buflen;
1065 /* Windows reports these as zero */
1066 caps->dwUnlockTransferRate = 0;
1067 caps->dwPlayCpuOverhead = 0;
1069 return DS_OK;
1072 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1073 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1075 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1076 DirectSoundDevice *device = This->device;
1077 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1079 if (ppobj == NULL) {
1080 WARN("invalid parameter\n");
1081 return E_INVALIDARG;
1084 *ppobj = NULL; /* assume failure */
1086 if ( IsEqualGUID(riid, &IID_IUnknown) ||
1087 IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1088 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1089 *ppobj = This;
1090 return S_OK;
1093 /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1094 /* a primary buffer can't have a DirectSoundBuffer8 interface */
1095 if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1096 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1097 return E_NOINTERFACE;
1100 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1101 ERR("app requested IDirectSoundNotify on primary buffer\n");
1102 /* FIXME: should we support this? */
1103 return E_NOINTERFACE;
1106 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1107 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1108 return E_NOINTERFACE;
1111 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1112 if (!device->listener)
1113 IDirectSound3DListenerImpl_Create(device, &device->listener);
1114 if (device->listener) {
1115 *ppobj = device->listener;
1116 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1117 return S_OK;
1120 WARN("IID_IDirectSound3DListener failed\n");
1121 return E_NOINTERFACE;
1124 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1125 FIXME("app requested IKsPropertySet on primary buffer\n");
1126 return E_NOINTERFACE;
1129 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1130 return E_NOINTERFACE;
1133 static const IDirectSoundBufferVtbl dspbvt =
1135 PrimaryBufferImpl_QueryInterface,
1136 PrimaryBufferImpl_AddRef,
1137 PrimaryBufferImpl_Release,
1138 PrimaryBufferImpl_GetCaps,
1139 PrimaryBufferImpl_GetCurrentPosition,
1140 PrimaryBufferImpl_GetFormat,
1141 PrimaryBufferImpl_GetVolume,
1142 PrimaryBufferImpl_GetPan,
1143 PrimaryBufferImpl_GetFrequency,
1144 PrimaryBufferImpl_GetStatus,
1145 PrimaryBufferImpl_Initialize,
1146 PrimaryBufferImpl_Lock,
1147 PrimaryBufferImpl_Play,
1148 PrimaryBufferImpl_SetCurrentPosition,
1149 PrimaryBufferImpl_SetFormat,
1150 PrimaryBufferImpl_SetVolume,
1151 PrimaryBufferImpl_SetPan,
1152 PrimaryBufferImpl_SetFrequency,
1153 PrimaryBufferImpl_Stop,
1154 PrimaryBufferImpl_Unlock,
1155 PrimaryBufferImpl_Restore
1158 HRESULT PrimaryBufferImpl_Create(
1159 DirectSoundDevice * device,
1160 PrimaryBufferImpl ** ppdsb,
1161 LPCDSBUFFERDESC dsbd)
1163 PrimaryBufferImpl *dsb;
1164 TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1166 if (dsbd->lpwfxFormat) {
1167 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1168 *ppdsb = NULL;
1169 return DSERR_INVALIDPARAM;
1172 dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1174 if (dsb == NULL) {
1175 WARN("out of memory\n");
1176 *ppdsb = NULL;
1177 return DSERR_OUTOFMEMORY;
1180 dsb->ref = 0;
1181 dsb->device = device;
1182 dsb->lpVtbl = &dspbvt;
1184 device->dsbd = *dsbd;
1186 TRACE("Created primary buffer at %p\n", dsb);
1187 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1188 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1189 device->pwfx->wFormatTag, device->pwfx->nChannels,
1190 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1191 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1192 device->pwfx->cbSize);
1194 *ppdsb = dsb;
1195 return S_OK;