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 * When PrimarySetFormat (via ReopenDevice or PrimaryOpen) fails,
23 * it leaves dsound in unusable (not really open) state.
29 #define NONAMELESSSTRUCT
30 #define NONAMELESSUNION
37 #include "wine/debug.h"
39 #include "dsound_private.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
43 /** Calculate how long a fragment length of about 10 ms should be in frames
45 * nSamplesPerSec: Frequency rate in samples per second
46 * nBlockAlign: Size of a single blockalign
49 * Size in bytes of a single fragment
51 DWORD DSOUND_fraglen(DWORD nSamplesPerSec, DWORD nBlockAlign)
53 /* Given a timer delay of 10ms, the fragment size is approximately:
54 * fraglen = (nSamplesPerSec * 10 / 1000) * nBlockAlign
55 * ==> fraglen = (nSamplesPerSec / 100) * nBlockSize
57 * ALSA uses buffers that are powers of 2. Because of this, fraglen
58 * is rounded up to the nearest power of 2:
61 if (nSamplesPerSec <= 12800)
62 return 128 * nBlockAlign;
64 if (nSamplesPerSec <= 25600)
65 return 256 * nBlockAlign;
67 if (nSamplesPerSec <= 51200)
68 return 512 * nBlockAlign;
70 return 1024 * nBlockAlign;
73 static void DSOUND_RecalcPrimary(DirectSoundDevice *device)
75 TRACE("(%p)\n", device);
77 device->fraglen = DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign);
78 device->helfrags = device->buflen / device->fraglen;
79 TRACE("fraglen=%d helfrags=%d\n", device->fraglen, device->helfrags);
81 /* calculate the 10ms write lead */
82 device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
85 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
88 REFERENCE_TIME prebuf_rt;
91 TRACE("(%p, %d)\n", device, forcewave);
94 IAudioClient_Release(device->client);
95 device->client = NULL;
98 IAudioRenderClient_Release(device->render);
99 device->render = NULL;
102 IAudioClock_Release(device->clock);
103 device->clock = NULL;
106 IAudioStreamVolume_Release(device->volume);
107 device->volume = NULL;
110 hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
111 CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
113 WARN("Activate failed: %08x\n", hres);
117 prebuf_frames = device->prebuf * DSOUND_fraglen(device->pwfx->nSamplesPerSec, device->pwfx->nBlockAlign) / device->pwfx->nBlockAlign;
118 prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;
120 hres = IAudioClient_Initialize(device->client,
121 AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST,
122 prebuf_rt, 0, device->pwfx, NULL);
124 IAudioClient_Release(device->client);
125 device->client = NULL;
126 WARN("Initialize failed: %08x\n", hres);
130 hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
131 (void**)&device->render);
133 IAudioClient_Release(device->client);
134 device->client = NULL;
135 WARN("GetService failed: %08x\n", hres);
139 hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
140 (void**)&device->clock);
142 IAudioClient_Release(device->client);
143 IAudioRenderClient_Release(device->render);
144 device->client = NULL;
145 device->render = NULL;
146 WARN("GetService failed: %08x\n", hres);
150 hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
151 (void**)&device->volume);
153 IAudioClient_Release(device->client);
154 IAudioRenderClient_Release(device->render);
155 IAudioClock_Release(device->clock);
156 device->client = NULL;
157 device->render = NULL;
158 device->clock = NULL;
159 WARN("GetService failed: %08x\n", hres);
166 static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
171 TRACE("(%p)\n", device);
173 /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
174 on windows this size is always fixed (tested on win-xp) */
176 device->buflen = ds_hel_buflen;
177 buflen = device->buflen;
178 buflen -= buflen % device->pwfx->nBlockAlign;
179 device->buflen = buflen;
181 device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
182 device->mix_buffer = HeapAlloc(GetProcessHeap(), 0, device->mix_buffer_len);
183 if (!device->mix_buffer)
184 return DSERR_OUTOFMEMORY;
186 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
187 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
189 TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
191 /* reallocate emulated primary buffer */
193 newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, buflen);
195 newbuf = HeapAlloc(GetProcessHeap(),0, buflen);
198 ERR("failed to allocate primary buffer\n");
199 return DSERR_OUTOFMEMORY;
200 /* but the old buffer might still exist and must be re-prepared */
203 DSOUND_RecalcPrimary(device);
205 device->buffer = newbuf;
207 TRACE("fraglen=%d\n", device->fraglen);
209 device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
210 device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
211 FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
212 FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
213 device->last_pos_bytes = device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0;
218 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
222 TRACE("(%p)\n", device);
224 device->pwqueue = (DWORD)-1; /* resetting queues */
227 hr = IAudioClient_Stop(device->client);
229 WARN("Stop failed: %08x\n", hr);
232 /* clear the queue */
236 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
239 TRACE("(%p)\n", device);
241 device->buflen = ds_hel_buflen;
242 err = DSOUND_PrimaryOpen(device);
245 WARN("DSOUND_PrimaryOpen failed\n");
249 device->state = STATE_STOPPED;
253 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
255 TRACE("(%p)\n", device);
258 EnterCriticalSection(&(device->mixlock));
260 DSOUND_PrimaryClose(device);
262 if(device->primary && (device->primary->ref || device->primary->numIfaces))
263 WARN("Destroying primary buffer while references held (%u %u)\n", device->primary->ref, device->primary->numIfaces);
265 HeapFree(GetProcessHeap(), 0, device->primary);
266 device->primary = NULL;
268 HeapFree(GetProcessHeap(),0,device->pwfx);
271 LeaveCriticalSection(&(device->mixlock));
277 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
281 TRACE("(%p)\n", device);
283 hr = IAudioClient_Start(device->client);
285 WARN("Start failed: %08x\n", hr);
292 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
296 TRACE("(%p)\n", device);
298 hr = IAudioClient_Stop(device->client);
300 WARN("Stop failed: %08x\n", hr);
307 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
309 TRACE("(%p,%p,%p)\n", device, playpos, writepos);
311 TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
313 /* check if playpos was requested */
315 /* use the cached play position */
316 *playpos = device->pwplay * device->fraglen;
318 /* check if writepos was requested */
320 /* the writepos is the first non-queued position */
321 *writepos = ((device->pwplay + device->pwqueue) % device->helfrags) * device->fraglen;
323 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
327 static DWORD DSOUND_GetFormatSize(LPCWAVEFORMATEX wfex)
329 if (wfex->wFormatTag == WAVE_FORMAT_PCM)
330 return sizeof(WAVEFORMATEX);
332 return sizeof(WAVEFORMATEX) + wfex->cbSize;
335 LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex)
337 DWORD size = DSOUND_GetFormatSize(wfex);
338 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(),0,size);
340 WARN("out of memory\n");
341 } else if (wfex->wFormatTag != WAVE_FORMAT_PCM) {
342 CopyMemory(pwfx, wfex, size);
344 CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT));
346 if (pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample/8) {
347 WARN("Fixing bad nBlockAlign (%u)\n", pwfx->nBlockAlign);
348 pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample/8;
350 if (pwfx->nAvgBytesPerSec != pwfx->nSamplesPerSec * pwfx->nBlockAlign) {
351 WARN("Fixing bad nAvgBytesPerSec (%u)\n", pwfx->nAvgBytesPerSec);
352 pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
358 HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passed_fmt)
360 HRESULT err = DSERR_BUFFERLOST;
362 WAVEFORMATEX *old_fmt;
363 WAVEFORMATEXTENSIBLE *fmtex, *passed_fmtex = (WAVEFORMATEXTENSIBLE*)passed_fmt;
364 BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY);
366 TRACE("(%p,%p)\n", device, passed_fmt);
368 if (device->priolevel == DSSCL_NORMAL) {
369 WARN("failed priority check!\n");
370 return DSERR_PRIOLEVELNEEDED;
373 /* Let's be pedantic! */
374 if (passed_fmt == NULL) {
375 WARN("invalid parameter: passed_fmt==NULL!\n");
376 return DSERR_INVALIDPARAM;
378 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
379 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
380 passed_fmt->wFormatTag, passed_fmt->nChannels, passed_fmt->nSamplesPerSec,
381 passed_fmt->nAvgBytesPerSec, passed_fmt->nBlockAlign,
382 passed_fmt->wBitsPerSample, passed_fmt->cbSize);
384 if(passed_fmt->wBitsPerSample < 8 || passed_fmt->wBitsPerSample % 8 != 0 ||
385 passed_fmt->nChannels == 0 || passed_fmt->nSamplesPerSec == 0 ||
386 passed_fmt->nAvgBytesPerSec == 0 ||
387 passed_fmt->nBlockAlign != passed_fmt->nChannels * passed_fmt->wBitsPerSample / 8)
388 return DSERR_INVALIDPARAM;
390 if(passed_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
391 if(passed_fmtex->Samples.wValidBitsPerSample > passed_fmtex->Format.wBitsPerSample)
392 return DSERR_INVALIDPARAM;
396 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
397 EnterCriticalSection(&(device->mixlock));
399 old_fmt = device->pwfx;
400 device->pwfx = DSOUND_CopyFormat(passed_fmt);
401 fmtex = (WAVEFORMATEXTENSIBLE *)device->pwfx;
402 if (device->pwfx == NULL) {
403 device->pwfx = old_fmt;
405 err = DSERR_OUTOFMEMORY;
409 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
410 if(fmtex->Samples.wValidBitsPerSample == 0){
411 TRACE("Correcting 0 valid bits per sample\n");
412 fmtex->Samples.wValidBitsPerSample = fmtex->Format.wBitsPerSample;
416 DSOUND_PrimaryClose(device);
418 err = DSOUND_ReopenDevice(device, FALSE);
422 /* requested format failed, so try others */
423 if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT){
424 device->pwfx->wFormatTag = WAVE_FORMAT_PCM;
425 device->pwfx->wBitsPerSample = 32;
426 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
427 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
429 err = DSOUND_ReopenDevice(device, FALSE);
434 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
435 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){
436 fmtex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
437 device->pwfx->wBitsPerSample = 32;
438 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
439 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
441 err = DSOUND_ReopenDevice(device, FALSE);
446 device->pwfx->wBitsPerSample = 32;
447 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
448 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
449 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
450 fmtex->Samples.wValidBitsPerSample = device->pwfx->wBitsPerSample;
451 err = DSOUND_ReopenDevice(device, FALSE);
455 device->pwfx->wBitsPerSample = 16;
456 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
457 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
458 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
459 fmtex->Samples.wValidBitsPerSample = device->pwfx->wBitsPerSample;
460 err = DSOUND_ReopenDevice(device, FALSE);
464 device->pwfx->wBitsPerSample = 8;
465 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
466 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
467 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
468 fmtex->Samples.wValidBitsPerSample = device->pwfx->wBitsPerSample;
469 err = DSOUND_ReopenDevice(device, FALSE);
473 device->pwfx->nChannels = (passed_fmt->nChannels == 2) ? 1 : 2;
474 device->pwfx->wBitsPerSample = passed_fmt->wBitsPerSample;
475 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
476 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
477 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
478 fmtex->Samples.wValidBitsPerSample = device->pwfx->wBitsPerSample;
479 err = DSOUND_ReopenDevice(device, FALSE);
483 device->pwfx->wBitsPerSample = 32;
484 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
485 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
486 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
487 fmtex->Samples.wValidBitsPerSample = device->pwfx->wBitsPerSample;
488 err = DSOUND_ReopenDevice(device, FALSE);
492 device->pwfx->wBitsPerSample = 16;
493 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
494 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
495 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
496 fmtex->Samples.wValidBitsPerSample = device->pwfx->wBitsPerSample;
497 err = DSOUND_ReopenDevice(device, FALSE);
501 device->pwfx->wBitsPerSample = 8;
502 device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign;
503 device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8);
504 if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
505 fmtex->Samples.wValidBitsPerSample = device->pwfx->wBitsPerSample;
506 err = DSOUND_ReopenDevice(device, FALSE);
510 WARN("No formats could be opened\n");
514 err = DSOUND_PrimaryOpen(device);
516 WARN("DSOUND_PrimaryOpen failed\n");
520 if (passed_fmt->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer)
522 DSOUND_PrimaryClose(device);
523 device->pwfx->nSamplesPerSec = passed_fmt->nSamplesPerSec;
524 err = DSOUND_ReopenDevice(device, TRUE);
526 WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err);
527 else if (FAILED((err = DSOUND_PrimaryOpen(device))))
528 WARN("DSOUND_PrimaryOpen(2) failed: %08x\n", err);
531 device->mix_buffer_len = DSOUND_bufpos_to_mixpos(device, device->buflen);
532 device->mix_buffer = HeapReAlloc(GetProcessHeap(), 0, device->mix_buffer, device->mix_buffer_len);
533 FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
534 device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1];
535 device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
537 if (old_fmt->nSamplesPerSec != device->pwfx->nSamplesPerSec ||
538 old_fmt->wBitsPerSample != device->pwfx->wBitsPerSample ||
539 old_fmt->nChannels != device->pwfx->nChannels) {
540 IDirectSoundBufferImpl** dsb = device->buffers;
541 for (i = 0; i < device->nrofbuffers; i++, dsb++) {
543 RtlAcquireResourceExclusive(&(*dsb)->lock, TRUE);
545 (*dsb)->freqAdjust = (*dsb)->freq / (float)device->pwfx->nSamplesPerSec;
546 DSOUND_RecalcFormat((*dsb));
547 (*dsb)->primary_mixpos = 0;
549 RtlReleaseResource(&(*dsb)->lock);
555 LeaveCriticalSection(&(device->mixlock));
556 RtlReleaseResource(&(device->buffer_list_lock));
559 HeapFree(GetProcessHeap(), 0, old_fmt);
563 /*******************************************************************************
566 static inline IDirectSoundBufferImpl *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
568 /* IDirectSoundBuffer and IDirectSoundBuffer8 use the same iface. */
569 return CONTAINING_RECORD(iface, IDirectSoundBufferImpl, IDirectSoundBuffer8_iface);
572 /* This sets this format for the primary buffer only */
573 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(IDirectSoundBuffer *iface,
574 const WAVEFORMATEX *wfex)
576 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
577 TRACE("(%p,%p)\n", iface, wfex);
578 return primarybuffer_SetFormat(This->device, wfex);
581 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(IDirectSoundBuffer *iface, LONG vol)
583 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
584 DirectSoundDevice *device = This->device;
588 TRACE("(%p,%d)\n", iface, vol);
590 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
591 WARN("control unavailable\n");
592 return DSERR_CONTROLUNAVAIL;
595 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
596 WARN("invalid parameter: vol = %d\n", vol);
597 return DSERR_INVALIDPARAM;
601 EnterCriticalSection(&device->mixlock);
603 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
605 LeaveCriticalSection(&device->mixlock);
606 WARN("GetChannelVolume failed: %08x\n", hr);
610 if(device->pwfx->nChannels > 1){
611 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
613 LeaveCriticalSection(&device->mixlock);
614 WARN("GetChannelVolume failed: %08x\n", hr);
620 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
621 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
623 DSOUND_AmpFactorToVolPan(&device->volpan);
624 if (vol != device->volpan.lVolume) {
625 device->volpan.lVolume=vol;
626 DSOUND_RecalcVolPan(&device->volpan);
627 lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
628 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
630 LeaveCriticalSection(&device->mixlock);
631 WARN("SetChannelVolume failed: %08x\n", hr);
635 if(device->pwfx->nChannels > 1){
636 rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
637 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
639 LeaveCriticalSection(&device->mixlock);
640 WARN("SetChannelVolume failed: %08x\n", hr);
646 LeaveCriticalSection(&(device->mixlock));
652 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(IDirectSoundBuffer *iface, LONG *vol)
654 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
655 DirectSoundDevice *device = This->device;
658 TRACE("(%p,%p)\n", iface, vol);
660 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
661 WARN("control unavailable\n");
662 return DSERR_CONTROLUNAVAIL;
666 WARN("invalid parameter: vol = NULL\n");
667 return DSERR_INVALIDPARAM;
670 EnterCriticalSection(&device->mixlock);
672 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
674 LeaveCriticalSection(&device->mixlock);
675 WARN("GetChannelVolume failed: %08x\n", hr);
679 if(device->pwfx->nChannels > 1){
680 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
682 LeaveCriticalSection(&device->mixlock);
683 WARN("GetChannelVolume failed: %08x\n", hr);
689 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
690 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
692 DSOUND_AmpFactorToVolPan(&device->volpan);
693 *vol = device->volpan.lVolume;
695 LeaveCriticalSection(&device->mixlock);
700 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
702 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
703 TRACE("(%p,%d)\n",This,freq);
705 /* You cannot set the frequency of the primary buffer */
706 WARN("control unavailable\n");
707 return DSERR_CONTROLUNAVAIL;
710 static HRESULT WINAPI PrimaryBufferImpl_Play(IDirectSoundBuffer *iface, DWORD reserved1,
711 DWORD reserved2, DWORD flags)
713 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
714 DirectSoundDevice *device = This->device;
715 TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
717 if (!(flags & DSBPLAY_LOOPING)) {
718 WARN("invalid parameter: flags = %08x\n", flags);
719 return DSERR_INVALIDPARAM;
723 EnterCriticalSection(&(device->mixlock));
725 if (device->state == STATE_STOPPED)
726 device->state = STATE_STARTING;
727 else if (device->state == STATE_STOPPING)
728 device->state = STATE_PLAYING;
730 LeaveCriticalSection(&(device->mixlock));
736 static HRESULT WINAPI PrimaryBufferImpl_Stop(IDirectSoundBuffer *iface)
738 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
739 DirectSoundDevice *device = This->device;
740 TRACE("(%p)\n", iface);
743 EnterCriticalSection(&(device->mixlock));
745 if (device->state == STATE_PLAYING)
746 device->state = STATE_STOPPING;
747 else if (device->state == STATE_STARTING)
748 device->state = STATE_STOPPED;
750 LeaveCriticalSection(&(device->mixlock));
756 static ULONG WINAPI PrimaryBufferImpl_AddRef(IDirectSoundBuffer *iface)
758 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
759 ULONG ref = InterlockedIncrement(&(This->ref));
760 TRACE("(%p) ref was %d\n", This, ref - 1);
762 InterlockedIncrement(&This->numIfaces);
766 /* Decreases *out by 1 to no less than 0.
767 * Returns the new value of *out. */
768 LONG capped_refcount_dec(LONG *out)
775 oldref = InterlockedCompareExchange(out, ref - 1, ref);
776 } while(oldref != ref);
780 static ULONG WINAPI PrimaryBufferImpl_Release(IDirectSoundBuffer *iface)
782 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
785 ref = capped_refcount_dec(&This->ref);
787 capped_refcount_dec(&This->numIfaces);
789 TRACE("(%p) primary ref is now %d\n", This, ref);
794 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *iface,
795 DWORD *playpos, DWORD *writepos)
798 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
799 DirectSoundDevice *device = This->device;
800 TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
803 EnterCriticalSection(&(device->mixlock));
805 hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
807 WARN("DSOUND_PrimaryGetPosition failed\n");
808 LeaveCriticalSection(&(device->mixlock));
812 if (device->state != STATE_STOPPED)
813 /* apply the documented 10ms lead to writepos */
814 *writepos += device->writelead;
815 while (*writepos >= device->buflen) *writepos -= device->buflen;
818 LeaveCriticalSection(&(device->mixlock));
821 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
825 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
827 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
828 DirectSoundDevice *device = This->device;
829 TRACE("(%p,%p)\n", iface, status);
831 if (status == NULL) {
832 WARN("invalid parameter: status == NULL\n");
833 return DSERR_INVALIDPARAM;
837 if ((device->state == STATE_STARTING) ||
838 (device->state == STATE_PLAYING))
839 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
841 TRACE("status=%x\n", *status);
846 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *lpwf,
847 DWORD wfsize, DWORD *wfwritten)
850 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
851 DirectSoundDevice *device = This->device;
852 TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
854 size = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
856 if (lpwf) { /* NULL is valid */
857 if (wfsize >= size) {
858 CopyMemory(lpwf,device->pwfx,size);
862 WARN("invalid parameter: wfsize too small\n");
865 return DSERR_INVALIDPARAM;
869 *wfwritten = sizeof(WAVEFORMATEX) + device->pwfx->cbSize;
871 WARN("invalid parameter: wfwritten == NULL\n");
872 return DSERR_INVALIDPARAM;
879 static HRESULT WINAPI PrimaryBufferImpl_Lock(IDirectSoundBuffer *iface, DWORD writecursor,
880 DWORD writebytes, void **lplpaudioptr1, DWORD *audiobytes1, void **lplpaudioptr2,
881 DWORD *audiobytes2, DWORD flags)
884 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
885 DirectSoundDevice *device = This->device;
886 TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
899 return DSERR_INVALIDPARAM;
901 if (device->priolevel != DSSCL_WRITEPRIMARY) {
902 WARN("failed priority check!\n");
903 return DSERR_PRIOLEVELNEEDED;
906 /* when this flag is set, writecursor is meaningless and must be calculated */
907 if (flags & DSBLOCK_FROMWRITECURSOR) {
908 /* GetCurrentPosition does too much magic to duplicate here */
909 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
911 WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
916 /* when this flag is set, writebytes is meaningless and must be set */
917 if (flags & DSBLOCK_ENTIREBUFFER)
918 writebytes = device->buflen;
920 if (writecursor >= device->buflen) {
921 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
922 writecursor, device->buflen);
923 return DSERR_INVALIDPARAM;
926 if (writebytes > device->buflen) {
927 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
928 writebytes, device->buflen);
929 return DSERR_INVALIDPARAM;
932 if (writecursor+writebytes <= device->buflen) {
933 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
934 *audiobytes1 = writebytes;
936 *(LPBYTE*)lplpaudioptr2 = NULL;
939 TRACE("->%d.0\n",writebytes);
941 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
942 *audiobytes1 = device->buflen-writecursor;
944 *(LPBYTE*)lplpaudioptr2 = device->buffer;
946 *audiobytes2 = writebytes-(device->buflen-writecursor);
947 TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
952 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD newpos)
954 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
955 TRACE("(%p,%d)\n",This,newpos);
957 /* You cannot set the position of the primary buffer */
958 WARN("invalid call\n");
959 return DSERR_INVALIDCALL;
962 static HRESULT WINAPI PrimaryBufferImpl_SetPan(IDirectSoundBuffer *iface, LONG pan)
964 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
965 DirectSoundDevice *device = This->device;
968 TRACE("(%p,%d)\n", iface, pan);
970 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
971 WARN("control unavailable\n");
972 return DSERR_CONTROLUNAVAIL;
975 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
976 WARN("invalid parameter: pan = %d\n", pan);
977 return DSERR_INVALIDPARAM;
981 EnterCriticalSection(&device->mixlock);
983 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
985 LeaveCriticalSection(&device->mixlock);
986 WARN("GetChannelVolume failed: %08x\n", hr);
990 if(device->pwfx->nChannels > 1){
991 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
993 LeaveCriticalSection(&device->mixlock);
994 WARN("GetChannelVolume failed: %08x\n", hr);
1000 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
1001 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
1003 DSOUND_AmpFactorToVolPan(&device->volpan);
1004 if (pan != device->volpan.lPan) {
1005 device->volpan.lPan=pan;
1006 DSOUND_RecalcVolPan(&device->volpan);
1008 lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
1009 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
1011 LeaveCriticalSection(&device->mixlock);
1012 WARN("SetChannelVolume failed: %08x\n", hr);
1016 if(device->pwfx->nChannels > 1){
1017 rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
1018 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
1020 LeaveCriticalSection(&device->mixlock);
1021 WARN("SetChannelVolume failed: %08x\n", hr);
1027 LeaveCriticalSection(&device->mixlock);
1033 static HRESULT WINAPI PrimaryBufferImpl_GetPan(IDirectSoundBuffer *iface, LONG *pan)
1035 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1036 DirectSoundDevice *device = This->device;
1039 TRACE("(%p,%p)\n", iface, pan);
1041 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
1042 WARN("control unavailable\n");
1043 return DSERR_CONTROLUNAVAIL;
1047 WARN("invalid parameter: pan == NULL\n");
1048 return DSERR_INVALIDPARAM;
1051 EnterCriticalSection(&device->mixlock);
1053 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
1055 LeaveCriticalSection(&device->mixlock);
1056 WARN("GetChannelVolume failed: %08x\n", hr);
1060 if(device->pwfx->nChannels > 1){
1061 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
1063 LeaveCriticalSection(&device->mixlock);
1064 WARN("GetChannelVolume failed: %08x\n", hr);
1070 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
1071 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
1073 DSOUND_AmpFactorToVolPan(&device->volpan);
1074 *pan = device->volpan.lPan;
1076 LeaveCriticalSection(&device->mixlock);
1081 static HRESULT WINAPI PrimaryBufferImpl_Unlock(IDirectSoundBuffer *iface, void *p1, DWORD x1,
1084 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1085 DirectSoundDevice *device = This->device;
1086 TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
1088 if (device->priolevel != DSSCL_WRITEPRIMARY) {
1089 WARN("failed priority check!\n");
1090 return DSERR_PRIOLEVELNEEDED;
1093 if((p1 && ((BYTE*)p1 < device->buffer ||
1094 (BYTE*)p1 >= device->buffer + device->buflen)) ||
1095 (p2 && ((BYTE*)p2 < device->buffer ||
1096 (BYTE*)p2 >= device->buffer + device->buflen)))
1097 return DSERR_INVALIDPARAM;
1102 static HRESULT WINAPI PrimaryBufferImpl_Restore(IDirectSoundBuffer *iface)
1104 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1105 FIXME("(%p):stub\n",This);
1109 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
1111 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1112 DirectSoundDevice *device = This->device;
1113 TRACE("(%p,%p)\n", iface, freq);
1116 WARN("invalid parameter: freq == NULL\n");
1117 return DSERR_INVALIDPARAM;
1120 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1121 WARN("control unavailable\n");
1122 return DSERR_CONTROLUNAVAIL;
1125 *freq = device->pwfx->nSamplesPerSec;
1126 TRACE("-> %d\n", *freq);
1131 static HRESULT WINAPI PrimaryBufferImpl_Initialize(IDirectSoundBuffer *iface, IDirectSound *dsound,
1132 const DSBUFFERDESC *dbsd)
1134 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1135 WARN("(%p) already initialized\n", This);
1136 return DSERR_ALREADYINITIALIZED;
1139 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
1141 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1142 DirectSoundDevice *device = This->device;
1143 TRACE("(%p,%p)\n", iface, caps);
1146 WARN("invalid parameter: caps == NULL\n");
1147 return DSERR_INVALIDPARAM;
1150 if (caps->dwSize < sizeof(*caps)) {
1151 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1152 return DSERR_INVALIDPARAM;
1155 caps->dwFlags = This->dsbd.dwFlags;
1156 caps->dwBufferBytes = device->buflen;
1158 /* Windows reports these as zero */
1159 caps->dwUnlockTransferRate = 0;
1160 caps->dwPlayCpuOverhead = 0;
1165 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(IDirectSoundBuffer *iface, REFIID riid,
1168 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1170 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1172 if (ppobj == NULL) {
1173 WARN("invalid parameter\n");
1174 return E_INVALIDARG;
1177 *ppobj = NULL; /* assume failure */
1179 if ( IsEqualGUID(riid, &IID_IUnknown) ||
1180 IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1181 IDirectSoundBuffer_AddRef(iface);
1186 /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1187 /* a primary buffer can't have a DirectSoundBuffer8 interface */
1188 if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1189 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1190 return E_NOINTERFACE;
1193 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1194 ERR("app requested IDirectSoundNotify on primary buffer\n");
1195 /* FIXME: should we support this? */
1196 return E_NOINTERFACE;
1199 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1200 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1201 return E_NOINTERFACE;
1204 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1205 *ppobj = &This->IDirectSound3DListener_iface;
1206 IDirectSound3DListener_AddRef(&This->IDirectSound3DListener_iface);
1210 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1211 *ppobj = &This->IKsPropertySet_iface;
1212 IKsPropertySet_AddRef(&This->IKsPropertySet_iface);
1216 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1217 return E_NOINTERFACE;
1220 static const IDirectSoundBufferVtbl dspbvt =
1222 PrimaryBufferImpl_QueryInterface,
1223 PrimaryBufferImpl_AddRef,
1224 PrimaryBufferImpl_Release,
1225 PrimaryBufferImpl_GetCaps,
1226 PrimaryBufferImpl_GetCurrentPosition,
1227 PrimaryBufferImpl_GetFormat,
1228 PrimaryBufferImpl_GetVolume,
1229 PrimaryBufferImpl_GetPan,
1230 PrimaryBufferImpl_GetFrequency,
1231 PrimaryBufferImpl_GetStatus,
1232 PrimaryBufferImpl_Initialize,
1233 PrimaryBufferImpl_Lock,
1234 PrimaryBufferImpl_Play,
1235 PrimaryBufferImpl_SetCurrentPosition,
1236 PrimaryBufferImpl_SetFormat,
1237 PrimaryBufferImpl_SetVolume,
1238 PrimaryBufferImpl_SetPan,
1239 PrimaryBufferImpl_SetFrequency,
1240 PrimaryBufferImpl_Stop,
1241 PrimaryBufferImpl_Unlock,
1242 PrimaryBufferImpl_Restore
1245 HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb,
1246 const DSBUFFERDESC *dsbd)
1248 IDirectSoundBufferImpl *dsb;
1249 TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1251 if (dsbd->lpwfxFormat) {
1252 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1254 return DSERR_INVALIDPARAM;
1257 dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1260 WARN("out of memory\n");
1262 return DSERR_OUTOFMEMORY;
1269 dsb->device = device;
1270 dsb->IDirectSoundBuffer8_iface.lpVtbl = (IDirectSoundBuffer8Vtbl *)&dspbvt;
1271 dsb->IDirectSound3DListener_iface.lpVtbl = &ds3dlvt;
1272 dsb->IKsPropertySet_iface.lpVtbl = &iksbvt;
1275 /* IDirectSound3DListener */
1276 device->ds3dl.dwSize = sizeof(DS3DLISTENER);
1277 device->ds3dl.vPosition.x = 0.0;
1278 device->ds3dl.vPosition.y = 0.0;
1279 device->ds3dl.vPosition.z = 0.0;
1280 device->ds3dl.vVelocity.x = 0.0;
1281 device->ds3dl.vVelocity.y = 0.0;
1282 device->ds3dl.vVelocity.z = 0.0;
1283 device->ds3dl.vOrientFront.x = 0.0;
1284 device->ds3dl.vOrientFront.y = 0.0;
1285 device->ds3dl.vOrientFront.z = 1.0;
1286 device->ds3dl.vOrientTop.x = 0.0;
1287 device->ds3dl.vOrientTop.y = 1.0;
1288 device->ds3dl.vOrientTop.z = 0.0;
1289 device->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1290 device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1291 device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
1292 device->ds3dl_need_recalc = TRUE;
1294 TRACE("Created primary buffer at %p\n", dsb);
1295 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1296 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1297 device->pwfx->wFormatTag, device->pwfx->nChannels,
1298 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1299 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1300 device->pwfx->cbSize);
1302 IDirectSoundBuffer_AddRef(&dsb->IDirectSoundBuffer8_iface);