push 83f6eeab4f78cf34cba36fe6c2150f9c23ec0aba
[wine/hacks.git] / dlls / dsound / primary.c
blob4c9e12500a5bf6ab3c05a879999a176319d70658
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 if (device->driver)
76 IDsDriver_Close(device->driver);
77 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
78 waveOutClose(device->hwo);
79 IDsDriver_Release(device->driver);
80 device->driver = NULL;
81 device->buffer = NULL;
82 device->hwo = 0;
84 else if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
85 waveOutClose(device->hwo);
87 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
88 if (ds_hw_accel != DS_HW_ACCEL_EMULATION && !forcewave)
89 waveOutMessage((HWAVEOUT)device->drvdesc.dnDevNode, DRV_QUERYDSOUNDIFACE, (DWORD_PTR)&device->driver, 0);
91 /* Get driver description */
92 if (device->driver) {
93 DWORD wod = device->drvdesc.dnDevNode;
94 hres = IDsDriver_GetDriverDesc(device->driver,&(device->drvdesc));
95 device->drvdesc.dnDevNode = wod;
96 if (FAILED(hres)) {
97 WARN("IDsDriver_GetDriverDesc failed: %08x\n", hres);
98 IDsDriver_Release(device->driver);
99 device->driver = NULL;
103 /* if no DirectSound interface available, use WINMM API instead */
104 if (!device->driver)
105 device->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
107 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
109 DWORD flags = CALLBACK_FUNCTION;
111 if (device->driver)
112 flags |= WAVE_DIRECTSOUND;
114 hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD)device, flags));
115 if (FAILED(hres)) {
116 WARN("waveOutOpen failed\n");
117 if (device->driver)
119 IDsDriver_Release(device->driver);
120 device->driver = NULL;
122 return hres;
126 if (device->driver)
127 hres = IDsDriver_Open(device->driver);
129 return hres;
132 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
134 HRESULT err = DS_OK;
135 TRACE("(%p)\n", device);
137 if (device->driver)
139 err = IDsDriver_CreateSoundBuffer(device->driver,device->pwfx,
140 DSBCAPS_PRIMARYBUFFER,0,
141 &(device->buflen),&(device->buffer),
142 (LPVOID*)&(device->hwbuf));
144 if (err != DS_OK) {
145 WARN("IDsDriver_CreateSoundBuffer failed (%08x), falling back to waveout\n", err);
146 err = DSOUND_ReopenDevice(device, TRUE);
147 if (FAILED(err))
149 WARN("Falling back to waveout failed too! Giving up\n");
150 return err;
155 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
156 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
158 /* are we using waveOut stuff? */
159 if (!device->driver) {
160 LPBYTE newbuf;
161 LPWAVEHDR headers = NULL;
162 DWORD buflen, overshot, oldbuflen;
163 unsigned int c;
165 /* Start in pause mode, to allow buffers to get filled */
166 waveOutPause(device->hwo);
168 /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
169 on windows this size is always fixed (tested on win-xp) */
170 if (!device->buflen)
171 buflen = ds_hel_buflen;
172 else /* In case we move from hw accelerated to waveout */
173 buflen = device->buflen;
174 buflen -= ds_hel_buflen % device->pwfx->nBlockAlign;
176 TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
178 /* reallocate emulated primary buffer */
179 if (device->buffer)
180 newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer,buflen);
181 else
182 newbuf = HeapAlloc(GetProcessHeap(),0,buflen);
184 if (!newbuf) {
185 ERR("failed to allocate primary buffer\n");
186 return DSERR_OUTOFMEMORY;
187 /* but the old buffer might still exist and must be re-prepared */
190 oldbuflen = device->buflen;
191 device->buflen = buflen;
192 DSOUND_RecalcPrimary(device);
193 if (device->pwave)
194 headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR));
195 else
196 headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR));
198 if (!headers) {
199 ERR("failed to allocate wave headers\n");
200 HeapFree(GetProcessHeap(), 0, newbuf);
201 device->buflen = oldbuflen;
202 DSOUND_RecalcPrimary(device);
203 return DSERR_OUTOFMEMORY;
206 device->buffer = newbuf;
207 device->pwave = headers;
209 /* prepare fragment headers */
210 for (c=0; c<device->helfrags; c++) {
211 device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen;
212 device->pwave[c].dwBufferLength = device->fraglen;
213 device->pwave[c].dwUser = (DWORD)device;
214 device->pwave[c].dwFlags = 0;
215 device->pwave[c].dwLoops = 0;
216 err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)));
217 if (err != DS_OK) {
218 while (c--)
219 waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR));
220 break;
224 overshot = device->buflen % device->helfrags;
225 /* sanity */
226 if(overshot)
228 WARN("helfrags (%d x %d) doesn't fit entirely in buflen (%d) overshot: %d\n", device->helfrags, device->fraglen, device->buflen, overshot);
229 device->pwave[device->helfrags - 1].dwBufferLength += overshot;
232 TRACE("fraglen=%d\n", device->fraglen);
234 FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
235 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
236 DSOUND_RecalcPrimary(device);
238 return err;
242 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
244 TRACE("(%p)\n", device);
246 /* are we using waveOut stuff? */
247 if (!device->hwbuf) {
248 unsigned c;
250 /* get out of CS when calling the wave system */
251 LeaveCriticalSection(&(device->mixlock));
252 /* **** */
253 device->pwqueue = (DWORD)-1; /* resetting queues */
254 waveOutReset(device->hwo);
255 for (c=0; c<device->helfrags; c++)
256 waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR));
257 /* **** */
258 EnterCriticalSection(&(device->mixlock));
260 /* clear the queue */
261 device->pwqueue = 0;
262 } else {
263 ULONG ref = IDsDriverBuffer_Release(device->hwbuf);
264 if (!ref)
265 device->hwbuf = 0;
266 else
267 ERR("Still %d references on primary buffer, refcount leak?\n", ref);
271 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
273 HRESULT err = DS_OK;
274 TRACE("(%p)\n", device);
276 device->buflen = ds_hel_buflen;
277 err = DSOUND_PrimaryOpen(device);
279 if (err != DS_OK) {
280 WARN("DSOUND_PrimaryOpen failed\n");
281 return err;
284 device->state = STATE_STOPPED;
285 return DS_OK;
288 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
290 TRACE("(%p)\n", device);
292 /* **** */
293 EnterCriticalSection(&(device->mixlock));
295 DSOUND_PrimaryClose(device);
296 if (device->driver) {
297 if (device->hwbuf) {
298 if (IDsDriverBuffer_Release(device->hwbuf) == 0)
299 device->hwbuf = 0;
301 } else
302 HeapFree(GetProcessHeap(),0,device->pwave);
303 HeapFree(GetProcessHeap(),0,device->pwfx);
304 device->pwfx=NULL;
306 LeaveCriticalSection(&(device->mixlock));
307 /* **** */
309 return DS_OK;
312 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
314 HRESULT err = DS_OK;
315 TRACE("(%p)\n", device);
317 if (device->hwbuf) {
318 err = IDsDriverBuffer_Play(device->hwbuf, 0, 0, DSBPLAY_LOOPING);
319 if (err != DS_OK)
320 WARN("IDsDriverBuffer_Play failed\n");
321 } else {
322 err = mmErr(waveOutRestart(device->hwo));
323 if (err != DS_OK)
324 WARN("waveOutRestart failed\n");
327 return err;
330 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
332 HRESULT err = DS_OK;
333 TRACE("(%p)\n", device);
335 if (device->hwbuf) {
336 err = IDsDriverBuffer_Stop(device->hwbuf);
337 if (err == DSERR_BUFFERLOST) {
338 DSOUND_PrimaryClose(device);
339 err = DSOUND_ReopenDevice(device, !device->driver);
340 if (FAILED(err))
341 ERR("DSOUND_ReopenDevice failed\n");
342 else
344 err = DSOUND_PrimaryOpen(device);
345 if (FAILED(err))
346 WARN("DSOUND_PrimaryOpen failed\n");
348 } else if (err != DS_OK) {
349 WARN("IDsDriverBuffer_Stop failed\n");
351 } else {
353 /* don't call the wave system with the lock set */
354 LeaveCriticalSection(&(device->mixlock));
355 /* **** */
357 err = mmErr(waveOutPause(device->hwo));
359 /* **** */
360 EnterCriticalSection(&(device->mixlock));
362 if (err != DS_OK)
363 WARN("waveOutPause failed\n");
366 return err;
369 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
371 TRACE("(%p,%p,%p)\n", device, playpos, writepos);
373 if (device->hwbuf) {
374 HRESULT err=IDsDriverBuffer_GetPosition(device->hwbuf,playpos,writepos);
375 if (err) {
376 WARN("IDsDriverBuffer_GetPosition failed\n");
377 return err;
379 } else {
381 /* check if playpos was requested */
382 if (playpos) {
383 /* use the cached play position */
384 *playpos = device->pwplay * device->fraglen;
387 /* check if writepos was requested */
388 if (writepos) {
389 TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
391 /* the writepos is the first non-queued position */
392 *writepos = (device->pwplay + device->pwqueue) * device->fraglen;
393 *writepos %= device->buflen;
396 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
397 return DS_OK;
400 HRESULT DSOUND_PrimarySetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex, BOOL forced)
402 HRESULT err = DSERR_BUFFERLOST;
403 int i, alloc_size, cp_size;
404 DWORD nSamplesPerSec, bpp, chans;
405 TRACE("(%p,%p)\n", device, wfex);
407 if (device->priolevel == DSSCL_NORMAL) {
408 WARN("failed priority check!\n");
409 return DSERR_PRIOLEVELNEEDED;
412 /* Let's be pedantic! */
413 if (wfex == NULL) {
414 WARN("invalid parameter: wfex==NULL!\n");
415 return DSERR_INVALIDPARAM;
417 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
418 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
419 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
420 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
421 wfex->wBitsPerSample, wfex->cbSize);
423 /* **** */
424 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
425 EnterCriticalSection(&(device->mixlock));
427 if (wfex->wFormatTag == WAVE_FORMAT_PCM) {
428 alloc_size = sizeof(WAVEFORMATEX);
429 cp_size = sizeof(PCMWAVEFORMAT);
430 } else
431 alloc_size = cp_size = sizeof(WAVEFORMATEX) + wfex->cbSize;
433 device->pwfx = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,device->pwfx,alloc_size);
435 nSamplesPerSec = device->pwfx->nSamplesPerSec;
436 bpp = device->pwfx->wBitsPerSample;
437 chans = device->pwfx->nChannels;
439 CopyMemory(device->pwfx, wfex, cp_size);
441 if (!(device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) && device->hwbuf) {
442 err = IDsDriverBuffer_SetFormat(device->hwbuf, device->pwfx);
444 /* On bad format, try to re-create, big chance it will work then, only do this if we <HAVE> to */
445 if (forced && (device->pwfx->nSamplesPerSec/100 != wfex->nSamplesPerSec/100 || err == DSERR_BADFORMAT))
447 err = DSERR_BUFFERLOST;
448 CopyMemory(device->pwfx, wfex, cp_size);
451 if (err != DSERR_BUFFERLOST && FAILED(err)) {
452 WARN("IDsDriverBuffer_SetFormat failed\n");
453 if (!forced)
454 err = DS_OK;
455 goto done;
457 if (err == S_FALSE)
459 /* ALSA specific: S_FALSE tells that recreation was successful,
460 * but size and location may be changed, and buffer has to be restarted
461 * I put it here, so if frequency doesn't match the error will be changed to DSERR_BUFFERLOST
462 * and the entire re-initialization will occur anyway
464 IDsDriverBuffer_Lock(device->hwbuf, (LPVOID *)&device->buffer, &device->buflen, NULL, NULL, 0, 0, DSBLOCK_ENTIREBUFFER);
465 IDsDriverBuffer_Unlock(device->hwbuf, device->buffer, 0, NULL, 0);
467 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
468 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
469 device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
470 err = DS_OK;
472 DSOUND_RecalcPrimary(device);
475 if (err == DSERR_BUFFERLOST)
477 DSOUND_PrimaryClose(device);
479 if (device->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT)
481 err = DSOUND_ReopenDevice(device, FALSE);
482 if (FAILED(err))
484 WARN("DSOUND_ReopenDevice failed: %08x\n", err);
485 goto done;
488 err = DSOUND_PrimaryOpen(device);
489 if (err != DS_OK) {
490 WARN("DSOUND_PrimaryOpen failed\n");
491 goto done;
495 if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) {
496 IDirectSoundBufferImpl** dsb = device->buffers;
497 for (i = 0; i < device->nrofbuffers; i++, dsb++) {
498 /* **** */
499 RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
501 (*dsb)->freqAdjust = ((DWORD64)(*dsb)->freq << DSOUND_FREQSHIFT) / device->pwfx->nSamplesPerSec;
502 DSOUND_RecalcFormat((*dsb));
503 DSOUND_MixToTemporary((*dsb), 0, (*dsb)->buflen);
504 (*dsb)->primary_mixpos = 0;
506 RtlReleaseResource(&(*dsb)->lock);
507 /* **** */
511 done:
512 LeaveCriticalSection(&(device->mixlock));
513 RtlReleaseResource(&(device->buffer_list_lock));
514 /* **** */
516 return err;
519 /*******************************************************************************
520 * PrimaryBuffer
522 /* This sets this format for the <em>Primary Buffer Only</em> */
523 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
524 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(
525 LPDIRECTSOUNDBUFFER iface,
526 LPCWAVEFORMATEX wfex)
528 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
529 TRACE("(%p,%p)\n", iface, wfex);
530 return DSOUND_PrimarySetFormat(device, wfex, device->priolevel == DSSCL_WRITEPRIMARY);
533 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(
534 LPDIRECTSOUNDBUFFER iface,LONG vol
536 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
537 DWORD ampfactors;
538 DSVOLUMEPAN volpan;
539 HRESULT hres = DS_OK;
540 TRACE("(%p,%d)\n", iface, vol);
542 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
543 WARN("control unavailable\n");
544 return DSERR_CONTROLUNAVAIL;
547 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
548 WARN("invalid parameter: vol = %d\n", vol);
549 return DSERR_INVALIDPARAM;
552 /* **** */
553 EnterCriticalSection(&(device->mixlock));
555 waveOutGetVolume(device->hwo, &ampfactors);
556 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
557 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
558 DSOUND_AmpFactorToVolPan(&volpan);
559 if (vol != volpan.lVolume) {
560 volpan.lVolume=vol;
561 DSOUND_RecalcVolPan(&volpan);
562 if (device->hwbuf) {
563 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
564 if (hres != DS_OK)
565 WARN("IDsDriverBuffer_SetVolumePan failed\n");
566 } else {
567 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
568 waveOutSetVolume(device->hwo, ampfactors);
572 LeaveCriticalSection(&(device->mixlock));
573 /* **** */
575 return hres;
578 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(
579 LPDIRECTSOUNDBUFFER iface,LPLONG vol
581 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
582 DWORD ampfactors;
583 DSVOLUMEPAN volpan;
584 TRACE("(%p,%p)\n", iface, vol);
586 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
587 WARN("control unavailable\n");
588 return DSERR_CONTROLUNAVAIL;
591 if (vol == NULL) {
592 WARN("invalid parameter: vol = NULL\n");
593 return DSERR_INVALIDPARAM;
596 waveOutGetVolume(device->hwo, &ampfactors);
597 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
598 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
599 DSOUND_AmpFactorToVolPan(&volpan);
600 *vol = volpan.lVolume;
601 return DS_OK;
604 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(
605 LPDIRECTSOUNDBUFFER iface,DWORD freq
607 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
608 TRACE("(%p,%d)\n",This,freq);
610 /* You cannot set the frequency of the primary buffer */
611 WARN("control unavailable\n");
612 return DSERR_CONTROLUNAVAIL;
615 static HRESULT WINAPI PrimaryBufferImpl_Play(
616 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
618 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
619 TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
621 if (!(flags & DSBPLAY_LOOPING)) {
622 WARN("invalid parameter: flags = %08x\n", flags);
623 return DSERR_INVALIDPARAM;
626 /* **** */
627 EnterCriticalSection(&(device->mixlock));
629 if (device->state == STATE_STOPPED)
630 device->state = STATE_STARTING;
631 else if (device->state == STATE_STOPPING)
632 device->state = STATE_PLAYING;
634 LeaveCriticalSection(&(device->mixlock));
635 /* **** */
637 return DS_OK;
640 static HRESULT WINAPI PrimaryBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
642 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
643 TRACE("(%p)\n", iface);
645 /* **** */
646 EnterCriticalSection(&(device->mixlock));
648 if (device->state == STATE_PLAYING)
649 device->state = STATE_STOPPING;
650 else if (device->state == STATE_STARTING)
651 device->state = STATE_STOPPED;
653 LeaveCriticalSection(&(device->mixlock));
654 /* **** */
656 return DS_OK;
659 static ULONG WINAPI PrimaryBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface)
661 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
662 ULONG ref = InterlockedIncrement(&(This->ref));
663 TRACE("(%p) ref was %d\n", This, ref - 1);
664 return ref;
667 static ULONG WINAPI PrimaryBufferImpl_Release(LPDIRECTSOUNDBUFFER iface)
669 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
670 DWORD ref = InterlockedDecrement(&(This->ref));
671 TRACE("(%p) ref was %d\n", This, ref + 1);
673 if (!ref) {
674 This->device->primary = NULL;
675 HeapFree(GetProcessHeap(), 0, This);
676 TRACE("(%p) released\n", This);
678 return ref;
681 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
682 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
684 HRESULT hres;
685 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
686 TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
688 /* **** */
689 EnterCriticalSection(&(device->mixlock));
691 hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
692 if (hres != DS_OK) {
693 WARN("DSOUND_PrimaryGetPosition failed\n");
694 LeaveCriticalSection(&(device->mixlock));
695 return hres;
697 if (writepos) {
698 if (device->state != STATE_STOPPED)
699 /* apply the documented 10ms lead to writepos */
700 *writepos += device->writelead;
701 while (*writepos >= device->buflen) *writepos -= device->buflen;
704 LeaveCriticalSection(&(device->mixlock));
705 /* **** */
707 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
708 return DS_OK;
711 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(
712 LPDIRECTSOUNDBUFFER iface,LPDWORD status
714 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
715 TRACE("(%p,%p)\n", iface, status);
717 if (status == NULL) {
718 WARN("invalid parameter: status == NULL\n");
719 return DSERR_INVALIDPARAM;
722 *status = 0;
723 if ((device->state == STATE_STARTING) ||
724 (device->state == STATE_PLAYING))
725 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
727 TRACE("status=%x\n", *status);
728 return DS_OK;
732 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(
733 LPDIRECTSOUNDBUFFER iface,
734 LPWAVEFORMATEX lpwf,
735 DWORD wfsize,
736 LPDWORD wfwritten)
738 DWORD size;
739 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
740 TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
742 size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
744 if (lpwf) { /* NULL is valid */
745 if (wfsize >= size) {
746 CopyMemory(lpwf,device->pwfx,size);
747 if (wfwritten)
748 *wfwritten = size;
749 } else {
750 WARN("invalid parameter: wfsize too small\n");
751 if (wfwritten)
752 *wfwritten = 0;
753 return DSERR_INVALIDPARAM;
755 } else {
756 if (wfwritten)
757 *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
758 else {
759 WARN("invalid parameter: wfwritten == NULL\n");
760 return DSERR_INVALIDPARAM;
764 return DS_OK;
767 static HRESULT WINAPI PrimaryBufferImpl_Lock(
768 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID *lplpaudioptr1,LPDWORD audiobytes1,LPVOID *lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
770 HRESULT hres;
771 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
772 TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
773 iface,
774 writecursor,
775 writebytes,
776 lplpaudioptr1,
777 audiobytes1,
778 lplpaudioptr2,
779 audiobytes2,
780 flags,
781 GetTickCount()
784 if (device->priolevel != DSSCL_WRITEPRIMARY) {
785 WARN("failed priority check!\n");
786 return DSERR_PRIOLEVELNEEDED;
789 /* when this flag is set, writecursor is meaningless and must be calculated */
790 if (flags & DSBLOCK_FROMWRITECURSOR) {
791 /* GetCurrentPosition does too much magic to duplicate here */
792 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
793 if (hres != DS_OK) {
794 WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
795 return hres;
799 /* when this flag is set, writebytes is meaningless and must be set */
800 if (flags & DSBLOCK_ENTIREBUFFER)
801 writebytes = device->buflen;
803 if (writecursor >= device->buflen) {
804 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
805 writecursor, device->buflen);
806 return DSERR_INVALIDPARAM;
809 if (writebytes > device->buflen) {
810 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
811 writebytes, device->buflen);
812 return DSERR_INVALIDPARAM;
815 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
816 hres = IDsDriverBuffer_Lock(device->hwbuf,
817 lplpaudioptr1, audiobytes1,
818 lplpaudioptr2, audiobytes2,
819 writecursor, writebytes,
821 if (hres != DS_OK) {
822 WARN("IDsDriverBuffer_Lock failed\n");
823 return hres;
825 } else {
826 if (writecursor+writebytes <= device->buflen) {
827 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
828 *audiobytes1 = writebytes;
829 if (lplpaudioptr2)
830 *(LPBYTE*)lplpaudioptr2 = NULL;
831 if (audiobytes2)
832 *audiobytes2 = 0;
833 TRACE("->%d.0\n",writebytes);
834 } else {
835 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
836 *audiobytes1 = device->buflen-writecursor;
837 if (lplpaudioptr2)
838 *(LPBYTE*)lplpaudioptr2 = device->buffer;
839 if (audiobytes2)
840 *audiobytes2 = writebytes-(device->buflen-writecursor);
841 TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
844 return DS_OK;
847 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(
848 LPDIRECTSOUNDBUFFER iface,DWORD newpos
850 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
851 TRACE("(%p,%d)\n",This,newpos);
853 /* You cannot set the position of the primary buffer */
854 WARN("invalid call\n");
855 return DSERR_INVALIDCALL;
858 static HRESULT WINAPI PrimaryBufferImpl_SetPan(
859 LPDIRECTSOUNDBUFFER iface,LONG pan
861 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
862 DWORD ampfactors;
863 DSVOLUMEPAN volpan;
864 HRESULT hres = DS_OK;
865 TRACE("(%p,%d)\n", iface, pan);
867 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
868 WARN("control unavailable\n");
869 return DSERR_CONTROLUNAVAIL;
872 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
873 WARN("invalid parameter: pan = %d\n", pan);
874 return DSERR_INVALIDPARAM;
877 /* **** */
878 EnterCriticalSection(&(device->mixlock));
880 waveOutGetVolume(device->hwo, &ampfactors);
881 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
882 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
883 DSOUND_AmpFactorToVolPan(&volpan);
884 if (pan != volpan.lPan) {
885 volpan.lPan=pan;
886 DSOUND_RecalcVolPan(&volpan);
887 if (device->hwbuf) {
888 hres = IDsDriverBuffer_SetVolumePan(device->hwbuf, &volpan);
889 if (hres != DS_OK)
890 WARN("IDsDriverBuffer_SetVolumePan failed\n");
891 } else {
892 ampfactors = (volpan.dwTotalLeftAmpFactor & 0xffff) | (volpan.dwTotalRightAmpFactor << 16);
893 waveOutSetVolume(device->hwo, ampfactors);
897 LeaveCriticalSection(&(device->mixlock));
898 /* **** */
900 return hres;
903 static HRESULT WINAPI PrimaryBufferImpl_GetPan(
904 LPDIRECTSOUNDBUFFER iface,LPLONG pan
906 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
907 DWORD ampfactors;
908 DSVOLUMEPAN volpan;
909 TRACE("(%p,%p)\n", iface, pan);
911 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
912 WARN("control unavailable\n");
913 return DSERR_CONTROLUNAVAIL;
916 if (pan == NULL) {
917 WARN("invalid parameter: pan == NULL\n");
918 return DSERR_INVALIDPARAM;
921 waveOutGetVolume(device->hwo, &ampfactors);
922 volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff;
923 volpan.dwTotalRightAmpFactor=ampfactors >> 16;
924 DSOUND_AmpFactorToVolPan(&volpan);
925 *pan = volpan.lPan;
926 return DS_OK;
929 static HRESULT WINAPI PrimaryBufferImpl_Unlock(
930 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
932 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
933 TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
935 if (device->priolevel != DSSCL_WRITEPRIMARY) {
936 WARN("failed priority check!\n");
937 return DSERR_PRIOLEVELNEEDED;
940 if (!(device->drvdesc.dwFlags & DSDDESC_DONTNEEDPRIMARYLOCK) && device->hwbuf) {
941 HRESULT hres;
943 hres = IDsDriverBuffer_Unlock(device->hwbuf, p1, x1, p2, x2);
944 if (hres != DS_OK) {
945 WARN("IDsDriverBuffer_Unlock failed\n");
946 return hres;
950 return DS_OK;
953 static HRESULT WINAPI PrimaryBufferImpl_Restore(
954 LPDIRECTSOUNDBUFFER iface
956 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
957 FIXME("(%p):stub\n",This);
958 return DS_OK;
961 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(
962 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
964 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
965 TRACE("(%p,%p)\n", iface, freq);
967 if (freq == NULL) {
968 WARN("invalid parameter: freq == NULL\n");
969 return DSERR_INVALIDPARAM;
972 if (!(device->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
973 WARN("control unavailable\n");
974 return DSERR_CONTROLUNAVAIL;
977 *freq = device->pwfx->nSamplesPerSec;
978 TRACE("-> %d\n", *freq);
980 return DS_OK;
983 static HRESULT WINAPI PrimaryBufferImpl_Initialize(
984 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPCDSBUFFERDESC dbsd
986 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
987 WARN("(%p) already initialized\n", This);
988 return DSERR_ALREADYINITIALIZED;
991 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(
992 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
994 DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
995 TRACE("(%p,%p)\n", iface, caps);
997 if (caps == NULL) {
998 WARN("invalid parameter: caps == NULL\n");
999 return DSERR_INVALIDPARAM;
1002 if (caps->dwSize < sizeof(*caps)) {
1003 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1004 return DSERR_INVALIDPARAM;
1007 caps->dwFlags = device->dsbd.dwFlags;
1008 caps->dwBufferBytes = device->buflen;
1010 /* Windows reports these as zero */
1011 caps->dwUnlockTransferRate = 0;
1012 caps->dwPlayCpuOverhead = 0;
1014 return DS_OK;
1017 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(
1018 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1020 PrimaryBufferImpl *This = (PrimaryBufferImpl *)iface;
1021 DirectSoundDevice *device = This->device;
1022 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1024 if (ppobj == NULL) {
1025 WARN("invalid parameter\n");
1026 return E_INVALIDARG;
1029 *ppobj = NULL; /* assume failure */
1031 if ( IsEqualGUID(riid, &IID_IUnknown) ||
1032 IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1033 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1034 *ppobj = This;
1035 return S_OK;
1038 /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1039 /* a primary buffer can't have a DirectSoundBuffer8 interface */
1040 if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1041 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1042 return E_NOINTERFACE;
1045 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1046 ERR("app requested IDirectSoundNotify on primary buffer\n");
1047 /* FIXME: should we support this? */
1048 return E_NOINTERFACE;
1051 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1052 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1053 return E_NOINTERFACE;
1056 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1057 if (!device->listener)
1058 IDirectSound3DListenerImpl_Create(device, &device->listener);
1059 if (device->listener) {
1060 *ppobj = device->listener;
1061 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
1062 return S_OK;
1065 WARN("IID_IDirectSound3DListener failed\n");
1066 return E_NOINTERFACE;
1069 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1070 FIXME("app requested IKsPropertySet on primary buffer\n");
1071 return E_NOINTERFACE;
1074 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1075 return E_NOINTERFACE;
1078 static const IDirectSoundBufferVtbl dspbvt =
1080 PrimaryBufferImpl_QueryInterface,
1081 PrimaryBufferImpl_AddRef,
1082 PrimaryBufferImpl_Release,
1083 PrimaryBufferImpl_GetCaps,
1084 PrimaryBufferImpl_GetCurrentPosition,
1085 PrimaryBufferImpl_GetFormat,
1086 PrimaryBufferImpl_GetVolume,
1087 PrimaryBufferImpl_GetPan,
1088 PrimaryBufferImpl_GetFrequency,
1089 PrimaryBufferImpl_GetStatus,
1090 PrimaryBufferImpl_Initialize,
1091 PrimaryBufferImpl_Lock,
1092 PrimaryBufferImpl_Play,
1093 PrimaryBufferImpl_SetCurrentPosition,
1094 PrimaryBufferImpl_SetFormat,
1095 PrimaryBufferImpl_SetVolume,
1096 PrimaryBufferImpl_SetPan,
1097 PrimaryBufferImpl_SetFrequency,
1098 PrimaryBufferImpl_Stop,
1099 PrimaryBufferImpl_Unlock,
1100 PrimaryBufferImpl_Restore
1103 HRESULT PrimaryBufferImpl_Create(
1104 DirectSoundDevice * device,
1105 PrimaryBufferImpl ** ppdsb,
1106 LPCDSBUFFERDESC dsbd)
1108 PrimaryBufferImpl *dsb;
1109 TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1111 if (dsbd->lpwfxFormat) {
1112 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1113 *ppdsb = NULL;
1114 return DSERR_INVALIDPARAM;
1117 dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1119 if (dsb == NULL) {
1120 WARN("out of memory\n");
1121 *ppdsb = NULL;
1122 return DSERR_OUTOFMEMORY;
1125 dsb->ref = 0;
1126 dsb->device = device;
1127 dsb->lpVtbl = &dspbvt;
1129 CopyMemory(&device->dsbd, dsbd, sizeof(*dsbd));
1131 TRACE("Created primary buffer at %p\n", dsb);
1132 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1133 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1134 device->pwfx->wFormatTag, device->pwfx->nChannels,
1135 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1136 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1137 device->pwfx->cbSize);
1139 *ppdsb = dsb;
1140 return S_OK;