dsound: Check if hardware buffer is big enough before accepting it.
[wine/multimedia.git] / dlls / dsound / primary.c
blob359a5ac577793fa06973ed5921d3c3f01d64055a
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 static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
41 DWORD nBlockAlign;
42 DWORD fraglen;
43 TRACE("(%p)\n", device);
45 nBlockAlign = device->pwfx->nBlockAlign;
46 /* Alsa doesn't have continuous buffers, instead it has buffers with power of 2,
47 * If DS_TIME_DEL is about 10 ms, 512 * nBlockAlign is roughly correct */
48 fraglen = 512 * nBlockAlign;
50 /* Compensate for only being roughly accurate */
51 if (device->pwfx->nSamplesPerSec <= 26000)
52 fraglen /= 2;
54 if (device->pwfx->nSamplesPerSec <= 12000)
55 fraglen /= 2;
57 if (device->pwfx->nSamplesPerSec >= 80000)
58 fraglen *= 2;
60 device->fraglen = fraglen;
61 device->helfrags = device->buflen / fraglen;
62 TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
64 if (device->hwbuf && device->drvdesc.dwFlags & DSDDESC_DONTNEEDWRITELEAD)
65 device->writelead = 0;
66 else
67 /* calculate the 10ms write lead */
68 device->writelead = (device->pwfx->nSamplesPerSec / 100) * nBlockAlign;
71 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
73 HRESULT hres = DS_OK;
74 TRACE("(%p, %d)\n", device, forcewave);
76 if (device->driver)
78 IDsDriver_Close(device->driver);
79 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
80 waveOutClose(device->hwo);
81 IDsDriver_Release(device->driver);
82 device->driver = NULL;
83 device->buffer = NULL;
84 device->hwo = 0;
86 else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
87 waveOutClose(device->hwo);
89 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
90 if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
91 waveOutMessage((HWAVEOUT)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);
93 /* Get driver description */
94 if (device->driver) {
95 DWORD wod = device->drvdesc.dnDevNode;
96 hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
97 device->drvdesc.dnDevNode = wod;
98 if (FAILED(hres)) {
99 WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
100 IDsDriver_Release(device->driver);
101 device->driver = NULL;
105 /* if no DirectSound interface available, use WINMM API instead */
106 if (!device->driver)
107 device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
109 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
111 DWORD flags = CALLBACK_FUNCTION;
113 if (device->driver)
114 flags |= WAVE_DIRECTSOUND;
116 hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, flags));
117 if (FAILED(hres)) {
118 WARN("waveOutOpen failed\n");
119 if (device->driver)
121 IDsDriver_Release(device->driver);
122 device->driver = NULL;
124 return hres;
128 if (device->driver)
129 hres = IDsDriver_Open(device->driver);
131 return hres;
134 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
136 DWORD buflen;
137 HRESULT err = DS_OK;
138 TRACE("(%p)\n", device);
140 /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
141 on windows this size is always fixed (tested on win-xp) */
142 if (!device->buflen)
143 buflen = ds_hel_buflen;
144 else /* In case we move from hw accelerated to waveout */
145 buflen = device->buflen;
146 buflen -= buflen % device->pwfx->nBlockAlign;
147 device->buflen = buflen;
149 if (device->driver)
151 err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
152 DSBCAPS_PRIMARYBUFFER,0,
153 &(device->buflen),&(device->buffer),
154 (LPVOID*)&(device->hwbuf));
156 if (err != DS_OK) {
157 WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
158 err = DSOUND_ReopenDevice(device, TRUE);
159 if (FAILED(err))
161 WARN("Falling back to waveout failed too! Giving up\n");
162 return err;
165 DSOUND_RecalcPrimary(device);
166 device->prebuf = ds_snd_queue_max;
167 if (device->helfrags < ds_snd_queue_min)
169 WARN("Too little sound buffer to be effective (%d/%d) falling back to waveout\n", device->buflen, ds_snd_queue_min * device->fraglen);
170 device->buflen = buflen;
171 err = DSOUND_ReopenDevice(device, TRUE);
172 if (FAILED(err))
174 WARN("Falling back to waveout failed too! Giving up\n");
175 return err;
178 else if (device->helfrags < ds_snd_queue_max)
179 device->prebuf = device->helfrags;
182 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
183 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
185 /* are we using waveOut stuff? */
186 if (!device->driver) {
187 LPBYTE newbuf;
188 LPWAVEHDR headers = NULL;
189 DWORD overshot;
190 unsigned int c;
192 /* Start in pause mode, to allow buffers to get filled */
193 waveOutPause(device->hwo);
195 TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
197 /* reallocate emulated primary buffer */
198 if (device->buffer)
199 newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, buflen);
200 else
201 newbuf = HeapAlloc(GetProcessHeap(),0, buflen);
203 if (!newbuf) {
204 ERR("failed to allocate primary buffer\n");
205 return DSERR_OUTOFMEMORY;
206 /* but the old buffer might still exist and must be re-prepared */
209 DSOUND_RecalcPrimary(device);
210 if (device->pwave)
211 headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
212 else
213 headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));
215 if (!headers) {
216 ERR("failed to allocate wave headers\n");
217 HeapFree(GetProcessHeap(), 0, newbuf);
218 DSOUND_RecalcPrimary(device);
219 return DSERR_OUTOFMEMORY;
222 device->buffer = newbuf;
223 device->pwave = headers;
225 /* prepare fragment headers */
226 for (c=0; c<device->helfrags; c++) {
227 device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
228 device->pwave[c].dwBufferLength = device->fraglen;
229 device->pwave[c].dwUser = (DWORD)device;
230 device->pwave[c].dwFlags = 0;
231 device->pwave[c].dwLoops = 0;
232 err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
233 if (err != DS_OK) {
234 while (c--)
235 waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
236 break;
240 overshot = device->buflen % device->fraglen;
241 /* sanity */
242 if(overshot)
244 overshot -= overshot % device->pwfx->nBlockAlign;
245 device->pwave[device->helfrags - 1].dwBufferLength += overshot;
248 TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot);
250 FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
251 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
252 return err;
256 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
258 TRACE("(%p)\n", device);
260 /* are we using waveOut stuff? */
261 if (!device->hwbuf) {
262 unsigned c;
264 /* get out of CS when calling the wave system */
265 LeaveCriticalSection(&(device->mixlock));
266 /* **** */
267 device->pwqueue = (DWORD)-1; /* resetting queues */
268 waveOutReset(device->hwo);
269 for (c=0; c<device->helfrags; c++)
270 waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
271 /* **** */
272 EnterCriticalSection(&(device->mixlock));
274 /* clear the queue */
275 device->pwqueue = 0;
276 } else {
277 ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
278 if (!ref)
279 device->hwbuf = 0;
280 else
281 ERR("Still %d references on primary buffer, refcount leak?\n", ref);
285 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
287 HRESULT err = DS_OK;
288 TRACE("(%p)\n", device);
290 device->buflen = ds_hel_buflen;
291 err = DSOUND_PrimaryOpen(device);
293 if (err != DS_OK) {
294 WARN("DSOUND_PrimaryOpen failed\n");
295 return err;
298 device->state = STATE_STOPPED;
299 return DS_OK;
302 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
304 TRACE("(%p)\n", device);
306 /* **** */
307 EnterCriticalSection(&(device->mixlock));
309 DSOUND_PrimaryClose(device);
310 if (device->driver) {
311 if (device->hwbuf) {
312 if (IDsDriverBuffer_Release(device->hwbuf) == 0)
313 device->hwbuf = 0;
315 } else
316 HeapFree(GetProcessHeap(),0,device->pwave);
317 HeapFree(GetProcessHeap(),0,device->pwfx);
318 device->pwfx=NULL;
320 LeaveCriticalSection(&(device->mixlock));
321 /* **** */
323 return DS_OK;
326 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
328 HRESULT err = DS_OK;
329 TRACE("(%p)\n", device);
331 if (device->hwbuf) {
332 err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
333 if (err != DS_OK)
334 WARN("IDsDriverBuffer_Play failed\n");
335 } else {
336 err = mmErr(waveOutRestart(device->hwo));
337 if (err != DS_OK)
338 WARN("waveOutRestart failed\n");
341 return err;
344 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
346 HRESULT err = DS_OK;
347 TRACE("(%p)\n", device);
349 if (device->hwbuf) {
350 err = IDsDriverBuffer_Stop(device->hwbuf);
351 if (err == DSERR_BUFFERLOST) {
352 DSOUND_PrimaryClose(device);
353 err = DSOUND_ReopenDevice(device, FALSE);
354 if (FAILED(err))
355 ERR("DSOUND_ReopenDevice failed\n");
356 else
358 err = DSOUND_PrimaryOpen(device);
359 if (FAILED(err))
360 WARN("DSOUND_PrimaryOpen failed\n");
362 } else if (err != DS_OK) {
363 WARN("IDsDriverBuffer_Stop failed\n");
365 } else {
367 /* don't call the wave system with the lock set */
368 LeaveCriticalSection(&(device->mixlock));
369 /* **** */
371 err = mmErr(waveOutPause(device->hwo));
373 /* **** */
374 EnterCriticalSection(&(device->mixlock));
376 if (err != DS_OK)
377 WARN("waveOutPause failed\n");
380 return err;
383 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
385 TRACE("(%p,%p,%p)\n", device, playpos, writepos);
387 if (device->hwbuf) {
388 HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
389 if (err) {
390 WARN("IDsDriverBuffer_GetPosition failed\n");
391 return err;
393 } else {
394 TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
396 /* check if playpos was requested */
397 if (playpos)
398 /* use the cached play position */
399 *playpos = device->pwplay * device->fraglen;
401 /* check if writepos was requested */
402 if (writepos)
403 /* the writepos is the first non-queued position */
404 *writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
406 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
407 return DS_OK;
410 HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, BOOL forced)
412 HRESULT err = DSERR_BUFFERLOST;
413 int i, alloc_size, cp_size;
414 DWORD nSamplesPerSec, bpp, chans;
415 TRACE("(%p,%p)\n", device, wfex);
417 if (device->priolevel == DSSCL_NORMAL) {
418 WARN("failed priority check!\n");
419 return DSERR_PRIOLEVELNEEDED;
422 /* Let's be pedantic! */
423 if (wfex == NULL) {
424 WARN("invalid parameter: wfex==NULL!\n");
425 return DSERR_INVALIDPARAM;
427 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
428 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
429 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
430 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
431 wfex->wBitsPerSample, wfex->cbSize);
433 /* **** */
434 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
435 EnterCriticalSection(&(device->mixlock));
437 if (wfex->wFormatTag == WAVE_FORMAT_PCM) {
438 alloc_size = sizeof(WAVEFORMATEX);
439 cp_size = sizeof(PCMWAVEFORMAT);
440 } else
441 alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize;
443 device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size);
445 nSamplesPerSec = device->pwfx->nSamplesPerSec;
446 bpp = device->pwfx->wBitsPerSample;
447 chans = device->pwfx->nChannels;
449 CopyMemory(device->pwfx, wfex, cp_size);
451 if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
452 err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
454 /* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
455 if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
457 err = DSERR_BUFFERLOST;
458 CopyMemory(device->pwfx, wfex, cp_size);
461 if (err != DSERR_BUFFERLOST && FAILED(err)) {
462 WARN("IDsDriverBuffer_SetFormat failed\n");
463 if (!forced)
464 err = DS_OK;
465 goto done;
468 if (err == S_FALSE)
470 /* ALSA specific: S_FALSE tells that recreation was successful,
471 * but size and location may be changed, and buffer has to be restarted
472 * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
473 * and the entire re-initialization will occur anyway
475 IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
476 IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);
478 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
479 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
480 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
481 err = DS_OK;
483 DSOUND_RecalcPrimary(device);
486 if (err == DSERR_BUFFERLOST)
488 DSOUND_PrimaryClose(device);
490 err = DSOUND_ReopenDevice(device, FALSE);
491 if (FAILED(err))
493 WARN("DSOUND_ReopenDevice failed: %08x\n", err);
494 goto done;
496 err = DSOUND_PrimaryOpen(device);
497 if (err != DS_OK) {
498 WARN("DSOUND_PrimaryOpen failed\n");
499 goto done;
502 if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
504 DSOUND_PrimaryClose(device);
505 device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec;
506 err = DSOUND_ReopenDevice(device, TRUE);
507 if (FAILED(err))
508 WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
509 else if (FAILED((err = DSOUND_PrimaryOpen(device))))
510 WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
514 if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
515 IDirectSoundBufferImpl** dsb = device->buffers;
516 for (i = 0; i < device->nrofbuffers; i++, dsb++) {
517 /* **** */
518 RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
520 (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
521 DSOUND_RecalcFormat((*dsb));
522 DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen);
523 (*dsb)->primary_mixpos = 0;
525 RtlReleaseResource(&(*dsb)->lock);
526 /* **** */
530 done:
531 LeaveCriticalSection(&(device->mixlock));
532 RtlReleaseResource(&(device->buffer_list_lock));
533 /* **** */
535 return err;
538 /*******************************************************************************
539 * PrimaryBuffer
541 /* This sets this format for the <em>Primary Buffer Only</em> */
542 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
543 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
544 LPDIRECTSOUNDBUFFER iface,
545 LPCWAVEFORMATEX wfex)
547 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
548 TRACE("(%p,%p)\n", iface, wfex);
549 return DSOUND_PrimarySetFormat(device, wfex, device->priolevel == DSSCL_WRITEPRIMARY);
552 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
553 LPDIRECTSOUNDBUFFER iface,LONG vol
555 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
556 DWORD ampfactors;
557 DSVOLUMEPAN volpan;
558 HRESULT hres = DS_OK;
559 TRACE("(%p,%d)\n", iface, vol);
561 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
562 WARN("control unavailable\n");
563 return DSERR_CONTROLUNAVAIL;
566 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
567 WARN("invalid parameter: vol = %d\n", vol);
568 return DSERR_INVALIDPARAM;
571 /* **** */
572 EnterCriticalSection(&(device->mixlock));
574 waveOutGetVolume(device->hwo, &ampfactors);
575 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
576 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
577 DSOUND_AmpFactorToVolPan(&volpan);
578 if (vol != volpan.lVolume) {
579 volpan.lVolume=vol;
580 DSOUND_RecalcVolPan(&volpan);
581 if (device->hwbuf) {
582 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
583 if (hres != DS_OK)
584 WARN("IDsDriverBuffer_SetVolumePan failed\n");
585 } else {
586 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
587 waveOutSetVolume(device->hwo, ampfactors);
591 LeaveCriticalSection(&(device->mixlock));
592 /* **** */
594 return hres;
597 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
598 LPDIRECTSOUNDBUFFER iface,LPLONG vol
600 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
601 DWORD ampfactors;
602 DSVOLUMEPAN volpan;
603 TRACE("(%p,%p)\n", iface, vol);
605 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
606 WARN("control unavailable\n");
607 return DSERR_CONTROLUNAVAIL;
610 if (vol == NULL) {
611 WARN("invalid parameter: vol = NULL\n");
612 return DSERR_INVALIDPARAM;
615 waveOutGetVolume(device->hwo, &ampfactors);
616 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
617 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
618 DSOUND_AmpFactorToVolPan(&volpan);
619 *vol = volpan.lVolume;
620 return DS_OK;
623 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
624 LPDIRECTSOUNDBUFFER iface,DWORD freq
626 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
627 TRACE("(%p,%d)\n",This,freq);
629 /* You cannot set the frequency of the primary buffer */
630 WARN("control unavailable\n");
631 return DSERR_CONTROLUNAVAIL;
634 static HRESULT WINAPI PrimaryBufferImpl_Play(
635 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
637 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
638 TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
640 if (!(flags & DSBPLAY_LOOPING)) {
641 WARN("invalid parameter: flags = %08x\n", flags);
642 return DSERR_INVALIDPARAM;
645 /* **** */
646 EnterCriticalSection(&(device->mixlock));
648 if (device->state == STATE_STOPPED)
649 device->state = STATE_STARTING;
650 else if (device->state == STATE_STOPPING)
651 device->state = STATE_PLAYING;
653 LeaveCriticalSection(&(device->mixlock));
654 /* **** */
656 return DS_OK;
659 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
661 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
662 TRACE("(%p)\n", iface);
664 /* **** */
665 EnterCriticalSection(&(device->mixlock));
667 if (device->state == STATE_PLAYING)
668 device->state = STATE_STOPPING;
669 else if (device->state == STATE_STARTING)
670 device->state = STATE_STOPPED;
672 LeaveCriticalSection(&(device->mixlock));
673 /* **** */
675 return DS_OK;
678 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
680 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
681 ULONG ref = InterlockedIncrement(&(This->ref));
682 TRACE("(%p) ref was %d\n", This, ref - 1);
683 return ref;
686 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
688 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
689 DWORD ref = InterlockedDecrement(&(This->ref));
690 TRACE("(%p) ref was %d\n", This, ref + 1);
692 if (!ref) {
693 This->device->primary = NULL;
694 HeapFree(GetProcessHeap(), 0, This);
695 TRACE("(%p) released\n", This);
697 return ref;
700 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
701 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
703 HRESULT hres;
704 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
705 TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
707 /* **** */
708 EnterCriticalSection(&(device->mixlock));
710 hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
711 if (hres != DS_OK) {
712 WARN("DSOUND_PrimaryGetPosition failed\n");
713 LeaveCriticalSection(&(device->mixlock));
714 return hres;
716 if (writepos) {
717 if (device->state != STATE_STOPPED)
718 /* apply the documented 10ms lead to writepos */
719 *writepos += device->writelead;
720 while (*writepos >= device->buflen) *writepos -= device->buflen;
723 LeaveCriticalSection(&(device->mixlock));
724 /* **** */
726 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
727 return DS_OK;
730 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
731 LPDIRECTSOUNDBUFFER iface,LPDWORD status
733 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
734 TRACE("(%p,%p)\n", iface, status);
736 if (status == NULL) {
737 WARN("invalid parameter: status == NULL\n");
738 return DSERR_INVALIDPARAM;
741 *status = 0;
742 if ((device->state == STATE_STARTING) ||
743 (device->state == STATE_PLAYING))
744 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
746 TRACE("status=%x\n", *status);
747 return DS_OK;
751 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
752 LPDIRECTSOUNDBUFFER iface,
753 LPWAVEFORMATEX lpwf,
754 DWORD wfsize,
755 LPDWORD wfwritten)
757 DWORD size;
758 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
759 TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
761 size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
763 if (lpwf) { /* NULL is valid */
764 if (wfsize >= size) {
765 CopyMemory(lpwf,device->pwfx,size);
766 if (wfwritten)
767 *wfwritten = size;
768 } else {
769 WARN("invalid parameter: wfsize too small\n");
770 if (wfwritten)
771 *wfwritten = 0;
772 return DSERR_INVALIDPARAM;
774 } else {
775 if (wfwritten)
776 *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
777 else {
778 WARN("invalid parameter: wfwritten == NULL\n");
779 return DSERR_INVALIDPARAM;
783 return DS_OK;
786 static HRESULT WINAPI PrimaryBufferImpl_Lock(
787 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
789 HRESULT hres;
790 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
791 TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
792 iface,
793 writecursor,
794 writebytes,
795 lplpaudioptr1,
796 audiobytes1,
797 lplpaudioptr2,
798 audiobytes2,
799 flags,
800 GetTickCount()
803 if (device->priolevel != DSSCL_WRITEPRIMARY) {
804 WARN("failed priority check!\n");
805 return DSERR_PRIOLEVELNEEDED;
808 /* when this flag is set, writecursor is meaningless and must be calculated */
809 if (flags & DSBLOCK_FROMWRITECURSOR) {
810 /* GetCurrentPosition does too much magic to duplicate here */
811 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
812 if (hres != DS_OK) {
813 WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
814 return hres;
818 /* when this flag is set, writebytes is meaningless and must be set */
819 if (flags & DSBLOCK_ENTIREBUFFER)
820 writebytes = device->buflen;
822 if (writecursor >= device->buflen) {
823 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
824 writecursor, device->buflen);
825 return DSERR_INVALIDPARAM;
828 if (writebytes > device->buflen) {
829 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
830 writebytes, device->buflen);
831 return DSERR_INVALIDPARAM;
834 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
835 hres = IDsDriverBuffer_Lock(device->hwbuf,
836 lplpaudioptr1, audiobytes1,
837 lplpaudioptr2, audiobytes2,
838 writecursor, writebytes,
840 if (hres != DS_OK) {
841 WARN("IDsDriverBuffer_Lock failed\n");
842 return hres;
844 } else {
845 if (writecursor+writebytes <= device->buflen) {
846 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
847 *audiobytes1 = writebytes;
848 if (lplpaudioptr2)
849 *(LPBYTE*)lplpaudioptr2 = NULL;
850 if (audiobytes2)
851 *audiobytes2 = 0;
852 TRACE("->%d.0\n",writebytes);
853 } else {
854 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
855 *audiobytes1 = device->buflen-writecursor;
856 if (lplpaudioptr2)
857 *(LPBYTE*)lplpaudioptr2 = device->buffer;
858 if (audiobytes2)
859 *audiobytes2 = writebytes-(device->buflen-writecursor);
860 TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
863 return DS_OK;
866 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
867 LPDIRECTSOUNDBUFFER iface,DWORD newpos
869 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
870 TRACE("(%p,%d)\n",This,newpos);
872 /* You cannot set the position of the primary buffer */
873 WARN("invalid call\n");
874 return DSERR_INVALIDCALL;
877 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
878 LPDIRECTSOUNDBUFFER iface,LONG pan
880 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
881 DWORD ampfactors;
882 DSVOLUMEPAN volpan;
883 HRESULT hres = DS_OK;
884 TRACE("(%p,%d)\n", iface, pan);
886 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
887 WARN("control unavailable\n");
888 return DSERR_CONTROLUNAVAIL;
891 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
892 WARN("invalid parameter: pan = %d\n", pan);
893 return DSERR_INVALIDPARAM;
896 /* **** */
897 EnterCriticalSection(&(device->mixlock));
899 waveOutGetVolume(device->hwo, &ampfactors);
900 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
901 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
902 DSOUND_AmpFactorToVolPan(&volpan);
903 if (pan != volpan.lPan) {
904 volpan.lPan=pan;
905 DSOUND_RecalcVolPan(&volpan);
906 if (device->hwbuf) {
907 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
908 if (hres != DS_OK)
909 WARN("IDsDriverBuffer_SetVolumePan failed\n");
910 } else {
911 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
912 waveOutSetVolume(device->hwo, ampfactors);
916 LeaveCriticalSection(&(device->mixlock));
917 /* **** */
919 return hres;
922 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
923 LPDIRECTSOUNDBUFFER iface,LPLONG pan
925 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
926 DWORD ampfactors;
927 DSVOLUMEPAN volpan;
928 TRACE("(%p,%p)\n", iface, pan);
930 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
931 WARN("control unavailable\n");
932 return DSERR_CONTROLUNAVAIL;
935 if (pan == NULL) {
936 WARN("invalid parameter: pan == NULL\n");
937 return DSERR_INVALIDPARAM;
940 waveOutGetVolume(device->hwo, &ampfactors);
941 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
942 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
943 DSOUND_AmpFactorToVolPan(&volpan);
944 *pan = volpan.lPan;
945 return DS_OK;
948 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
949 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
951 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
952 TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
954 if (device->priolevel != DSSCL_WRITEPRIMARY) {
955 WARN("failed priority check!\n");
956 return DSERR_PRIOLEVELNEEDED;
959 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
960 HRESULT hres;
962 hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
963 if (hres != DS_OK) {
964 WARN("IDsDriverBuffer_Unlock failed\n");
965 return hres;
969 return DS_OK;
972 static HRESULT WINAPI PrimaryBufferImpl_Restore(
973 LPDIRECTSOUNDBUFFER iface
975 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
976 FIXME("(%p):stub\n",This);
977 return DS_OK;
980 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
981 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
983 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
984 TRACE("(%p,%p)\n", iface, freq);
986 if (freq == NULL) {
987 WARN("invalid parameter: freq == NULL\n");
988 return DSERR_INVALIDPARAM;
991 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
992 WARN("control unavailable\n");
993 return DSERR_CONTROLUNAVAIL;
996 *freq = device->pwfx->nSamplesPerSec;
997 TRACE("-> %d\n", *freq);
999 return DS_OK;
1002 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
1003 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
1005 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1006 WARN("(%p) already initialized\n", This);
1007 return DSERR_ALREADYINITIALIZED;
1010 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
1011 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1013 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
1014 TRACE("(%p,%p)\n", iface, caps);
1016 if (caps == NULL) {
1017 WARN("invalid parameter: caps == NULL\n");
1018 return DSERR_INVALIDPARAM;
1021 if (caps->dwSize < sizeof(*caps)) {
1022 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1023 return DSERR_INVALIDPARAM;
1026 caps->dwFlags = device->dsbd.dwFlags;
1027 caps->dwBufferBytes = device->buflen;
1029 /* Windows reports these as zero */
1030 caps->dwUnlockTransferRate = 0;
1031 caps->dwPlayCpuOverhead = 0;
1033 return DS_OK;
1036 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1037 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1039 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1040 DirectSoundDevice *device = This->device;
1041 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1043 if (ppobj == NULL) {
1044 WARN("invalid parameter\n");
1045 return E_INVALIDARG;
1048 *ppobj = NULL; /* assume failure */
1050 if ( IsEqualGUID(riid, &IID_IUnknown) ||
1051 IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1052 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1053 *ppobj = This;
1054 return S_OK;
1057 /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1058 /* a primary buffer can't have a DirectSoundBuffer8 interface */
1059 if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1060 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1061 return E_NOINTERFACE;
1064 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1065 ERR("app requested IDirectSoundNotify on primary buffer\n");
1066 /* FIXME: should we support this? */
1067 return E_NOINTERFACE;
1070 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1071 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1072 return E_NOINTERFACE;
1075 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1076 if (!device->listener)
1077 IDirectSound3DListenerImpl_Create(device, &device->listener);
1078 if (device->listener) {
1079 *ppobj = device->listener;
1080 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1081 return S_OK;
1084 WARN("IID_IDirectSound3DListener failed\n");
1085 return E_NOINTERFACE;
1088 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1089 FIXME("app requested IKsPropertySet on primary buffer\n");
1090 return E_NOINTERFACE;
1093 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1094 return E_NOINTERFACE;
1097 static const IDirectSoundBufferVtbl dspbvt =
1099 PrimaryBufferImpl_QueryInterface,
1100 PrimaryBufferImpl_AddRef,
1101 PrimaryBufferImpl_Release,
1102 PrimaryBufferImpl_GetCaps,
1103 PrimaryBufferImpl_GetCurrentPosition,
1104 PrimaryBufferImpl_GetFormat,
1105 PrimaryBufferImpl_GetVolume,
1106 PrimaryBufferImpl_GetPan,
1107 PrimaryBufferImpl_GetFrequency,
1108 PrimaryBufferImpl_GetStatus,
1109 PrimaryBufferImpl_Initialize,
1110 PrimaryBufferImpl_Lock,
1111 PrimaryBufferImpl_Play,
1112 PrimaryBufferImpl_SetCurrentPosition,
1113 PrimaryBufferImpl_SetFormat,
1114 PrimaryBufferImpl_SetVolume,
1115 PrimaryBufferImpl_SetPan,
1116 PrimaryBufferImpl_SetFrequency,
1117 PrimaryBufferImpl_Stop,
1118 PrimaryBufferImpl_Unlock,
1119 PrimaryBufferImpl_Restore
1122 HRESULT PrimaryBufferImpl_Create(
1123 DirectSoundDevice * device,
1124 PrimaryBufferImpl ** ppdsb,
1125 LPCDSBUFFERDESC dsbd)
1127 PrimaryBufferImpl *dsb;
1128 TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1130 if (dsbd->lpwfxFormat) {
1131 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1132 *ppdsb = NULL;
1133 return DSERR_INVALIDPARAM;
1136 dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1138 if (dsb == NULL) {
1139 WARN("out of memory\n");
1140 *ppdsb = NULL;
1141 return DSERR_OUTOFMEMORY;
1144 dsb->ref = 0;
1145 dsb->device = device;
1146 dsb->lpVtbl = &dspbvt;
1148 CopyMemory(&device->dsbd, dsbd, sizeof(*dsbd));
1150 TRACE("Created primary buffer at %p\n", dsb);
1151 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1152 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1153 device->pwfx->wFormatTag, device->pwfx->nChannels,
1154 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1155 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1156 device->pwfx->cbSize);
1158 *ppdsb = dsb;
1159 return S_OK;