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 static DWORD DSOUND_fraglen(DirectSoundDevice *device)
45 REFERENCE_TIME period;
49 hr = IAudioClient_GetDevicePeriod(device->client, &period, NULL);
51 /* just guess at 10ms */
52 WARN("GetDevicePeriod failed: %08x\n", hr);
53 ret = MulDiv(device->pwfx->nBlockAlign, device->pwfx->nSamplesPerSec, 100);
55 ret = MulDiv(device->pwfx->nSamplesPerSec * device->pwfx->nBlockAlign, period, 10000000);
57 ret -= ret % device->pwfx->nBlockAlign;
61 static HRESULT DSOUND_WaveFormat(DirectSoundDevice *device, IAudioClient *client,
62 BOOL forcewave, WAVEFORMATEX **wfx)
64 WAVEFORMATEXTENSIBLE *retwfe = NULL;
69 WAVEFORMATEXTENSIBLE *mixwfe;
70 hr = IAudioClient_GetMixFormat(client, (WAVEFORMATEX**)&mixwfe);
75 if (mixwfe->Format.nChannels > 2) {
78 FIXME("Limiting channels to 2 due to lack of multichannel support\n");
80 mixwfe->Format.nChannels = 2;
81 mixwfe->Format.nBlockAlign = mixwfe->Format.nChannels * mixwfe->Format.wBitsPerSample / 8;
82 mixwfe->Format.nAvgBytesPerSec = mixwfe->Format.nSamplesPerSec * mixwfe->Format.nBlockAlign;
83 mixwfe->dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
86 if (!IsEqualGUID(&mixwfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
87 WAVEFORMATEXTENSIBLE testwfe = *mixwfe;
89 testwfe.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
90 testwfe.Format.wBitsPerSample = 32;
91 testwfe.Format.nBlockAlign = testwfe.Format.nChannels * testwfe.Format.wBitsPerSample / 8;
92 testwfe.Format.nAvgBytesPerSec = testwfe.Format.nSamplesPerSec * testwfe.Format.nBlockAlign;
93 testwfe.Samples.wValidBitsPerSample = 0;
95 if (FAILED(IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &testwfe.Format, (WAVEFORMATEX**)&retwfe)))
96 w = DSOUND_CopyFormat(&mixwfe->Format);
98 w = DSOUND_CopyFormat(&retwfe->Format);
100 w = DSOUND_CopyFormat(&testwfe.Format);
101 CoTaskMemFree(retwfe);
104 w = DSOUND_CopyFormat(&mixwfe->Format);
105 CoTaskMemFree(mixwfe);
106 } else if (device->primary_pwfx->wFormatTag == WAVE_FORMAT_PCM ||
107 device->primary_pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
108 WAVEFORMATEX *wi = device->primary_pwfx;
109 WAVEFORMATEXTENSIBLE *wfe;
111 /* Convert to WAVEFORMATEXTENSIBLE */
112 w = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEXTENSIBLE));
113 wfe = (WAVEFORMATEXTENSIBLE*)w;
115 return DSERR_OUTOFMEMORY;
118 w->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
119 w->cbSize = sizeof(*wfe) - sizeof(*w);
120 w->nBlockAlign = w->nChannels * w->wBitsPerSample / 8;
121 w->nAvgBytesPerSec = w->nSamplesPerSec * w->nBlockAlign;
123 wfe->dwChannelMask = 0;
124 wfe->Samples.wValidBitsPerSample = 0;
125 if (wi->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
126 w->wBitsPerSample = 32;
127 wfe->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
129 wfe->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
131 w = DSOUND_CopyFormat(device->primary_pwfx);
134 return DSERR_OUTOFMEMORY;
136 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, w, (WAVEFORMATEX**)&retwfe);
138 memcpy(w, retwfe, sizeof(WAVEFORMATEX) + retwfe->Format.cbSize);
139 CoTaskMemFree(retwfe);
142 WARN("IsFormatSupported failed: %08x\n", hr);
143 HeapFree(GetProcessHeap(), 0, w);
150 HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
153 REFERENCE_TIME prebuf_rt;
154 WAVEFORMATEX *wfx = NULL;
157 TRACE("(%p, %d)\n", device, forcewave);
160 IAudioClient_Release(device->client);
161 device->client = NULL;
164 IAudioRenderClient_Release(device->render);
165 device->render = NULL;
168 IAudioClock_Release(device->clock);
169 device->clock = NULL;
172 IAudioStreamVolume_Release(device->volume);
173 device->volume = NULL;
176 hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient,
177 CLSCTX_INPROC_SERVER, NULL, (void **)&device->client);
179 WARN("Activate failed: %08x\n", hres);
183 hres = DSOUND_WaveFormat(device, device->client, forcewave, &wfx);
185 IAudioClient_Release(device->client);
186 device->client = NULL;
189 HeapFree(GetProcessHeap(), 0, device->pwfx);
192 prebuf_frames = device->prebuf * DSOUND_fraglen(device) / device->pwfx->nBlockAlign;
193 prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;
195 hres = IAudioClient_Initialize(device->client,
196 AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST,
197 prebuf_rt, 0, device->pwfx, NULL);
199 IAudioClient_Release(device->client);
200 device->client = NULL;
201 WARN("Initialize failed: %08x\n", hres);
205 hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
206 (void**)&device->render);
208 IAudioClient_Release(device->client);
209 device->client = NULL;
210 WARN("GetService failed: %08x\n", hres);
214 hres = IAudioClient_GetService(device->client, &IID_IAudioClock,
215 (void**)&device->clock);
217 IAudioClient_Release(device->client);
218 IAudioRenderClient_Release(device->render);
219 device->client = NULL;
220 device->render = NULL;
221 WARN("GetService failed: %08x\n", hres);
225 hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
226 (void**)&device->volume);
228 IAudioClient_Release(device->client);
229 IAudioRenderClient_Release(device->render);
230 IAudioClock_Release(device->clock);
231 device->client = NULL;
232 device->render = NULL;
233 device->clock = NULL;
234 WARN("GetService failed: %08x\n", hres);
241 HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
243 IDirectSoundBufferImpl** dsb = device->buffers;
247 TRACE("(%p)\n", device);
249 device->fraglen = DSOUND_fraglen(device);
251 /* on original windows, the buffer it set to a fixed size, no matter what the settings are.
252 on windows this size is always fixed (tested on win-xp) */
254 device->buflen = ds_hel_buflen;
255 device->buflen -= device->buflen % device->pwfx->nBlockAlign;
256 while(device->buflen < device->fraglen * device->prebuf){
257 device->buflen += ds_hel_buflen;
258 device->buflen -= device->buflen % device->pwfx->nBlockAlign;
261 HeapFree(GetProcessHeap(), 0, device->mix_buffer);
262 device->mix_buffer_len = (device->buflen / (device->pwfx->wBitsPerSample / 8)) * sizeof(float);
263 device->mix_buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, device->mix_buffer_len);
264 if (!device->mix_buffer)
265 return DSERR_OUTOFMEMORY;
267 if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
268 else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
270 /* reallocate emulated primary buffer */
272 newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer, device->buflen);
274 newbuf = HeapAlloc(GetProcessHeap(),0, device->buflen);
277 ERR("failed to allocate primary buffer\n");
278 return DSERR_OUTOFMEMORY;
279 /* but the old buffer might still exist and must be re-prepared */
282 device->writelead = (device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign;
284 device->buffer = newbuf;
286 TRACE("buflen: %u, fraglen: %u, mix_buffer_len: %u\n",
287 device->buflen, device->fraglen, device->mix_buffer_len);
289 if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
290 (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
291 IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat,
292 &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
293 device->normfunction = normfunctions[4];
295 device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
297 FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
298 FillMemory(device->mix_buffer, device->mix_buffer_len, 0);
301 if (device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
302 (device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
303 IsEqualGUID(&((WAVEFORMATEXTENSIBLE*)device->pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))
304 device->normfunction = normfunctions[4];
306 device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1];
308 for (i = 0; i < device->nrofbuffers; i++) {
309 RtlAcquireResourceExclusive(&dsb[i]->lock, TRUE);
310 DSOUND_RecalcFormat(dsb[i]);
311 RtlReleaseResource(&dsb[i]->lock);
318 static void DSOUND_PrimaryClose(DirectSoundDevice *device)
322 TRACE("(%p)\n", device);
325 hr = IAudioClient_Stop(device->client);
327 WARN("Stop failed: %08x\n", hr);
330 /* clear the queue */
331 device->in_mmdev_bytes = 0;
334 HRESULT DSOUND_PrimaryCreate(DirectSoundDevice *device)
337 TRACE("(%p)\n", device);
339 device->buflen = ds_hel_buflen;
340 err = DSOUND_PrimaryOpen(device);
343 WARN("DSOUND_PrimaryOpen failed\n");
347 device->state = STATE_STOPPED;
351 HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
353 TRACE("(%p)\n", device);
356 EnterCriticalSection(&(device->mixlock));
358 DSOUND_PrimaryClose(device);
360 if(device->primary && (device->primary->ref || device->primary->numIfaces))
361 WARN("Destroying primary buffer while references held (%u %u)\n", device->primary->ref, device->primary->numIfaces);
363 HeapFree(GetProcessHeap(), 0, device->primary);
364 device->primary = NULL;
366 HeapFree(GetProcessHeap(),0,device->primary_pwfx);
367 HeapFree(GetProcessHeap(),0,device->pwfx);
370 LeaveCriticalSection(&(device->mixlock));
376 HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
380 TRACE("(%p)\n", device);
382 hr = IAudioClient_Start(device->client);
384 WARN("Start failed: %08x\n", hr);
391 HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
395 TRACE("(%p)\n", device);
397 hr = IAudioClient_Stop(device->client);
399 WARN("Stop failed: %08x\n", hr);
406 HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos)
408 TRACE("(%p,%p,%p)\n", device, playpos, writepos);
410 /* check if playpos was requested */
412 *playpos = device->playing_offs_bytes;
414 /* check if writepos was requested */
416 /* the writepos is the first non-queued position */
417 *writepos = (device->playing_offs_bytes + device->in_mmdev_bytes) % device->buflen;
419 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:-1, writepos?*writepos:-1, device, GetTickCount());
423 static DWORD DSOUND_GetFormatSize(LPCWAVEFORMATEX wfex)
425 if (wfex->wFormatTag == WAVE_FORMAT_PCM)
426 return sizeof(WAVEFORMATEX);
428 return sizeof(WAVEFORMATEX) + wfex->cbSize;
431 LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex)
433 DWORD size = DSOUND_GetFormatSize(wfex);
434 LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(),0,size);
436 WARN("out of memory\n");
437 } else if (wfex->wFormatTag != WAVE_FORMAT_PCM) {
438 CopyMemory(pwfx, wfex, size);
440 CopyMemory(pwfx, wfex, sizeof(PCMWAVEFORMAT));
442 if (pwfx->nBlockAlign != pwfx->nChannels * pwfx->wBitsPerSample/8) {
443 WARN("Fixing bad nBlockAlign (%u)\n", pwfx->nBlockAlign);
444 pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample/8;
446 if (pwfx->nAvgBytesPerSec != pwfx->nSamplesPerSec * pwfx->nBlockAlign) {
447 WARN("Fixing bad nAvgBytesPerSec (%u)\n", pwfx->nAvgBytesPerSec);
448 pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
454 HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passed_fmt)
457 WAVEFORMATEX *old_fmt;
458 WAVEFORMATEXTENSIBLE *fmtex, *passed_fmtex = (WAVEFORMATEXTENSIBLE*)passed_fmt;
459 BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY);
461 TRACE("(%p,%p)\n", device, passed_fmt);
463 if (device->priolevel == DSSCL_NORMAL) {
464 WARN("failed priority check!\n");
465 return DSERR_PRIOLEVELNEEDED;
468 /* Let's be pedantic! */
469 if (passed_fmt == NULL) {
470 WARN("invalid parameter: passed_fmt==NULL!\n");
471 return DSERR_INVALIDPARAM;
473 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
474 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
475 passed_fmt->wFormatTag, passed_fmt->nChannels, passed_fmt->nSamplesPerSec,
476 passed_fmt->nAvgBytesPerSec, passed_fmt->nBlockAlign,
477 passed_fmt->wBitsPerSample, passed_fmt->cbSize);
479 if(passed_fmt->wBitsPerSample < 8 || passed_fmt->wBitsPerSample % 8 != 0 ||
480 passed_fmt->nChannels == 0 || passed_fmt->nSamplesPerSec == 0 ||
481 passed_fmt->nAvgBytesPerSec == 0 ||
482 passed_fmt->nBlockAlign != passed_fmt->nChannels * passed_fmt->wBitsPerSample / 8)
483 return DSERR_INVALIDPARAM;
485 if(passed_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
486 if(passed_fmtex->Samples.wValidBitsPerSample > passed_fmtex->Format.wBitsPerSample)
487 return DSERR_INVALIDPARAM;
491 RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE);
492 EnterCriticalSection(&(device->mixlock));
494 if (device->priolevel == DSSCL_WRITEPRIMARY) {
495 old_fmt = device->primary_pwfx;
496 device->primary_pwfx = DSOUND_CopyFormat(passed_fmt);
497 fmtex = (WAVEFORMATEXTENSIBLE *)device->primary_pwfx;
498 if (device->primary_pwfx == NULL) {
499 err = DSERR_OUTOFMEMORY;
503 if (fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
504 fmtex->Samples.wValidBitsPerSample == 0) {
505 TRACE("Correcting 0 valid bits per sample\n");
506 fmtex->Samples.wValidBitsPerSample = fmtex->Format.wBitsPerSample;
509 DSOUND_PrimaryClose(device);
511 err = DSOUND_ReopenDevice(device, forced);
513 ERR("No formats could be opened\n");
517 err = DSOUND_PrimaryOpen(device);
519 ERR("DSOUND_PrimaryOpen failed\n");
525 device->primary_pwfx = old_fmt;
527 HeapFree(GetProcessHeap(), 0, old_fmt);
528 } else if (passed_fmt->wFormatTag == WAVE_FORMAT_PCM ||
529 passed_fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
530 /* Fill in "real" values to primary_pwfx */
531 WAVEFORMATEX *fmt = device->primary_pwfx;
533 *fmt = *device->pwfx;
534 fmtex = (void*)device->pwfx;
536 if (IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) &&
537 passed_fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
538 fmt->wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
540 fmt->wFormatTag = WAVE_FORMAT_PCM;
541 fmt->wBitsPerSample = 16;
543 fmt->nBlockAlign = fmt->nChannels * fmt->wBitsPerSample / 8;
544 fmt->nAvgBytesPerSec = fmt->nBlockAlign * fmt->nSamplesPerSec;
547 device->primary_pwfx = HeapReAlloc(GetProcessHeap(), 0, device->primary_pwfx, sizeof(*fmtex));
548 memcpy(device->primary_pwfx, device->pwfx, sizeof(*fmtex));
552 LeaveCriticalSection(&(device->mixlock));
553 RtlReleaseResource(&(device->buffer_list_lock));
559 /*******************************************************************************
562 static inline IDirectSoundBufferImpl *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
564 /* IDirectSoundBuffer and IDirectSoundBuffer8 use the same iface. */
565 return CONTAINING_RECORD(iface, IDirectSoundBufferImpl, IDirectSoundBuffer8_iface);
568 /* This sets this format for the primary buffer only */
569 static HRESULT WINAPI PrimaryBufferImpl_SetFormat(IDirectSoundBuffer *iface,
570 const WAVEFORMATEX *wfex)
572 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
573 TRACE("(%p,%p)\n", iface, wfex);
574 return primarybuffer_SetFormat(This->device, wfex);
577 static HRESULT WINAPI PrimaryBufferImpl_SetVolume(IDirectSoundBuffer *iface, LONG vol)
579 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
580 DirectSoundDevice *device = This->device;
584 TRACE("(%p,%d)\n", iface, vol);
586 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
587 WARN("control unavailable\n");
588 return DSERR_CONTROLUNAVAIL;
591 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) {
592 WARN("invalid parameter: vol = %d\n", vol);
593 return DSERR_INVALIDPARAM;
597 EnterCriticalSection(&device->mixlock);
599 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
601 LeaveCriticalSection(&device->mixlock);
602 WARN("GetChannelVolume failed: %08x\n", hr);
606 if(device->pwfx->nChannels > 1){
607 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
609 LeaveCriticalSection(&device->mixlock);
610 WARN("GetChannelVolume failed: %08x\n", hr);
616 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
617 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
619 DSOUND_AmpFactorToVolPan(&device->volpan);
620 if (vol != device->volpan.lVolume) {
621 device->volpan.lVolume=vol;
622 DSOUND_RecalcVolPan(&device->volpan);
623 lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
624 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
626 LeaveCriticalSection(&device->mixlock);
627 WARN("SetChannelVolume failed: %08x\n", hr);
631 if(device->pwfx->nChannels > 1){
632 rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
633 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
635 LeaveCriticalSection(&device->mixlock);
636 WARN("SetChannelVolume failed: %08x\n", hr);
642 LeaveCriticalSection(&(device->mixlock));
648 static HRESULT WINAPI PrimaryBufferImpl_GetVolume(IDirectSoundBuffer *iface, LONG *vol)
650 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
651 DirectSoundDevice *device = This->device;
654 TRACE("(%p,%p)\n", iface, vol);
656 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) {
657 WARN("control unavailable\n");
658 return DSERR_CONTROLUNAVAIL;
662 WARN("invalid parameter: vol = NULL\n");
663 return DSERR_INVALIDPARAM;
666 EnterCriticalSection(&device->mixlock);
668 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
670 LeaveCriticalSection(&device->mixlock);
671 WARN("GetChannelVolume failed: %08x\n", hr);
675 if(device->pwfx->nChannels > 1){
676 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
678 LeaveCriticalSection(&device->mixlock);
679 WARN("GetChannelVolume failed: %08x\n", hr);
685 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
686 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
688 DSOUND_AmpFactorToVolPan(&device->volpan);
689 *vol = device->volpan.lVolume;
691 LeaveCriticalSection(&device->mixlock);
696 static HRESULT WINAPI PrimaryBufferImpl_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
698 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
699 TRACE("(%p,%d)\n",This,freq);
701 /* You cannot set the frequency of the primary buffer */
702 WARN("control unavailable\n");
703 return DSERR_CONTROLUNAVAIL;
706 static HRESULT WINAPI PrimaryBufferImpl_Play(IDirectSoundBuffer *iface, DWORD reserved1,
707 DWORD reserved2, DWORD flags)
709 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
710 DirectSoundDevice *device = This->device;
711 TRACE("(%p,%08x,%08x,%08x)\n", iface, reserved1, reserved2, flags);
713 if (!(flags & DSBPLAY_LOOPING)) {
714 WARN("invalid parameter: flags = %08x\n", flags);
715 return DSERR_INVALIDPARAM;
719 EnterCriticalSection(&(device->mixlock));
721 if (device->state == STATE_STOPPED)
722 device->state = STATE_STARTING;
723 else if (device->state == STATE_STOPPING)
724 device->state = STATE_PLAYING;
726 LeaveCriticalSection(&(device->mixlock));
732 static HRESULT WINAPI PrimaryBufferImpl_Stop(IDirectSoundBuffer *iface)
734 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
735 DirectSoundDevice *device = This->device;
736 TRACE("(%p)\n", iface);
739 EnterCriticalSection(&(device->mixlock));
741 if (device->state == STATE_PLAYING)
742 device->state = STATE_STOPPING;
743 else if (device->state == STATE_STARTING)
744 device->state = STATE_STOPPED;
746 LeaveCriticalSection(&(device->mixlock));
752 static ULONG WINAPI PrimaryBufferImpl_AddRef(IDirectSoundBuffer *iface)
754 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
755 ULONG ref = InterlockedIncrement(&(This->ref));
756 TRACE("(%p) ref was %d\n", This, ref - 1);
758 InterlockedIncrement(&This->numIfaces);
762 /* Decreases *out by 1 to no less than 0.
763 * Returns the new value of *out. */
764 LONG capped_refcount_dec(LONG *out)
771 oldref = InterlockedCompareExchange(out, ref - 1, ref);
772 } while(oldref != ref);
776 static ULONG WINAPI PrimaryBufferImpl_Release(IDirectSoundBuffer *iface)
778 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
781 ref = capped_refcount_dec(&This->ref);
783 capped_refcount_dec(&This->numIfaces);
785 TRACE("(%p) primary ref is now %d\n", This, ref);
790 static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(IDirectSoundBuffer *iface,
791 DWORD *playpos, DWORD *writepos)
794 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
795 DirectSoundDevice *device = This->device;
796 TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
799 EnterCriticalSection(&(device->mixlock));
801 hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
803 WARN("DSOUND_PrimaryGetPosition failed\n");
804 LeaveCriticalSection(&(device->mixlock));
808 if (device->state != STATE_STOPPED)
809 /* apply the documented 10ms lead to writepos */
810 *writepos += device->writelead;
811 while (*writepos >= device->buflen) *writepos -= device->buflen;
814 LeaveCriticalSection(&(device->mixlock));
817 TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
821 static HRESULT WINAPI PrimaryBufferImpl_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
823 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
824 DirectSoundDevice *device = This->device;
825 TRACE("(%p,%p)\n", iface, status);
827 if (status == NULL) {
828 WARN("invalid parameter: status == NULL\n");
829 return DSERR_INVALIDPARAM;
833 if ((device->state == STATE_STARTING) ||
834 (device->state == STATE_PLAYING))
835 *status |= DSBSTATUS_PLAYING | DSBSTATUS_LOOPING;
837 TRACE("status=%x\n", *status);
842 static HRESULT WINAPI PrimaryBufferImpl_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *lpwf,
843 DWORD wfsize, DWORD *wfwritten)
846 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
847 DirectSoundDevice *device = This->device;
848 TRACE("(%p,%p,%d,%p)\n", iface, lpwf, wfsize, wfwritten);
850 size = sizeof(WAVEFORMATEX) + device->primary_pwfx->cbSize;
852 if (lpwf) { /* NULL is valid */
853 if (wfsize >= size) {
854 CopyMemory(lpwf,device->primary_pwfx,size);
858 WARN("invalid parameter: wfsize too small\n");
861 return DSERR_INVALIDPARAM;
865 *wfwritten = sizeof(WAVEFORMATEX) + device->primary_pwfx->cbSize;
867 WARN("invalid parameter: wfwritten == NULL\n");
868 return DSERR_INVALIDPARAM;
875 static HRESULT WINAPI PrimaryBufferImpl_Lock(IDirectSoundBuffer *iface, DWORD writecursor,
876 DWORD writebytes, void **lplpaudioptr1, DWORD *audiobytes1, void **lplpaudioptr2,
877 DWORD *audiobytes2, DWORD flags)
880 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
881 DirectSoundDevice *device = This->device;
882 TRACE("(%p,%d,%d,%p,%p,%p,%p,0x%08x) at %d\n",
895 return DSERR_INVALIDPARAM;
897 if (device->priolevel != DSSCL_WRITEPRIMARY) {
898 WARN("failed priority check!\n");
899 return DSERR_PRIOLEVELNEEDED;
902 /* when this flag is set, writecursor is meaningless and must be calculated */
903 if (flags & DSBLOCK_FROMWRITECURSOR) {
904 /* GetCurrentPosition does too much magic to duplicate here */
905 hres = IDirectSoundBuffer_GetCurrentPosition(iface, NULL, &writecursor);
907 WARN("IDirectSoundBuffer_GetCurrentPosition failed\n");
912 /* when this flag is set, writebytes is meaningless and must be set */
913 if (flags & DSBLOCK_ENTIREBUFFER)
914 writebytes = device->buflen;
916 if (writecursor >= device->buflen) {
917 WARN("Invalid parameter, writecursor: %u >= buflen: %u\n",
918 writecursor, device->buflen);
919 return DSERR_INVALIDPARAM;
922 if (writebytes > device->buflen) {
923 WARN("Invalid parameter, writebytes: %u > buflen: %u\n",
924 writebytes, device->buflen);
925 return DSERR_INVALIDPARAM;
928 if (writecursor+writebytes <= device->buflen) {
929 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
930 *audiobytes1 = writebytes;
932 *(LPBYTE*)lplpaudioptr2 = NULL;
935 TRACE("->%d.0\n",writebytes);
937 *(LPBYTE*)lplpaudioptr1 = device->buffer+writecursor;
938 *audiobytes1 = device->buflen-writecursor;
940 *(LPBYTE*)lplpaudioptr2 = device->buffer;
942 *audiobytes2 = writebytes-(device->buflen-writecursor);
943 TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
948 static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD newpos)
950 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
951 TRACE("(%p,%d)\n",This,newpos);
953 /* You cannot set the position of the primary buffer */
954 WARN("invalid call\n");
955 return DSERR_INVALIDCALL;
958 static HRESULT WINAPI PrimaryBufferImpl_SetPan(IDirectSoundBuffer *iface, LONG pan)
960 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
961 DirectSoundDevice *device = This->device;
964 TRACE("(%p,%d)\n", iface, pan);
966 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
967 WARN("control unavailable\n");
968 return DSERR_CONTROLUNAVAIL;
971 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) {
972 WARN("invalid parameter: pan = %d\n", pan);
973 return DSERR_INVALIDPARAM;
977 EnterCriticalSection(&device->mixlock);
979 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
981 LeaveCriticalSection(&device->mixlock);
982 WARN("GetChannelVolume failed: %08x\n", hr);
986 if(device->pwfx->nChannels > 1){
987 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
989 LeaveCriticalSection(&device->mixlock);
990 WARN("GetChannelVolume failed: %08x\n", hr);
996 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
997 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
999 DSOUND_AmpFactorToVolPan(&device->volpan);
1000 if (pan != device->volpan.lPan) {
1001 device->volpan.lPan=pan;
1002 DSOUND_RecalcVolPan(&device->volpan);
1004 lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF);
1005 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol);
1007 LeaveCriticalSection(&device->mixlock);
1008 WARN("SetChannelVolume failed: %08x\n", hr);
1012 if(device->pwfx->nChannels > 1){
1013 rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF);
1014 hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol);
1016 LeaveCriticalSection(&device->mixlock);
1017 WARN("SetChannelVolume failed: %08x\n", hr);
1023 LeaveCriticalSection(&device->mixlock);
1029 static HRESULT WINAPI PrimaryBufferImpl_GetPan(IDirectSoundBuffer *iface, LONG *pan)
1031 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1032 DirectSoundDevice *device = This->device;
1035 TRACE("(%p,%p)\n", iface, pan);
1037 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) {
1038 WARN("control unavailable\n");
1039 return DSERR_CONTROLUNAVAIL;
1043 WARN("invalid parameter: pan == NULL\n");
1044 return DSERR_INVALIDPARAM;
1047 EnterCriticalSection(&device->mixlock);
1049 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol);
1051 LeaveCriticalSection(&device->mixlock);
1052 WARN("GetChannelVolume failed: %08x\n", hr);
1056 if(device->pwfx->nChannels > 1){
1057 hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol);
1059 LeaveCriticalSection(&device->mixlock);
1060 WARN("GetChannelVolume failed: %08x\n", hr);
1066 device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF));
1067 device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF));
1069 DSOUND_AmpFactorToVolPan(&device->volpan);
1070 *pan = device->volpan.lPan;
1072 LeaveCriticalSection(&device->mixlock);
1077 static HRESULT WINAPI PrimaryBufferImpl_Unlock(IDirectSoundBuffer *iface, void *p1, DWORD x1,
1080 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1081 DirectSoundDevice *device = This->device;
1082 TRACE("(%p,%p,%d,%p,%d)\n", iface, p1, x1, p2, x2);
1084 if (device->priolevel != DSSCL_WRITEPRIMARY) {
1085 WARN("failed priority check!\n");
1086 return DSERR_PRIOLEVELNEEDED;
1089 if((p1 && ((BYTE*)p1 < device->buffer ||
1090 (BYTE*)p1 >= device->buffer + device->buflen)) ||
1091 (p2 && ((BYTE*)p2 < device->buffer ||
1092 (BYTE*)p2 >= device->buffer + device->buflen)))
1093 return DSERR_INVALIDPARAM;
1098 static HRESULT WINAPI PrimaryBufferImpl_Restore(IDirectSoundBuffer *iface)
1100 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1101 FIXME("(%p):stub\n",This);
1105 static HRESULT WINAPI PrimaryBufferImpl_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
1107 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1108 DirectSoundDevice *device = This->device;
1109 TRACE("(%p,%p)\n", iface, freq);
1112 WARN("invalid parameter: freq == NULL\n");
1113 return DSERR_INVALIDPARAM;
1116 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY)) {
1117 WARN("control unavailable\n");
1118 return DSERR_CONTROLUNAVAIL;
1121 *freq = device->pwfx->nSamplesPerSec;
1122 TRACE("-> %d\n", *freq);
1127 static HRESULT WINAPI PrimaryBufferImpl_Initialize(IDirectSoundBuffer *iface, IDirectSound *dsound,
1128 const DSBUFFERDESC *dbsd)
1130 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1131 WARN("(%p) already initialized\n", This);
1132 return DSERR_ALREADYINITIALIZED;
1135 static HRESULT WINAPI PrimaryBufferImpl_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
1137 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1138 DirectSoundDevice *device = This->device;
1139 TRACE("(%p,%p)\n", iface, caps);
1142 WARN("invalid parameter: caps == NULL\n");
1143 return DSERR_INVALIDPARAM;
1146 if (caps->dwSize < sizeof(*caps)) {
1147 WARN("invalid parameter: caps->dwSize = %d\n", caps->dwSize);
1148 return DSERR_INVALIDPARAM;
1151 caps->dwFlags = This->dsbd.dwFlags;
1152 caps->dwBufferBytes = device->buflen;
1154 /* Windows reports these as zero */
1155 caps->dwUnlockTransferRate = 0;
1156 caps->dwPlayCpuOverhead = 0;
1161 static HRESULT WINAPI PrimaryBufferImpl_QueryInterface(IDirectSoundBuffer *iface, REFIID riid,
1164 IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface);
1166 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(riid), ppobj);
1168 if (ppobj == NULL) {
1169 WARN("invalid parameter\n");
1170 return E_INVALIDARG;
1173 *ppobj = NULL; /* assume failure */
1175 if ( IsEqualGUID(riid, &IID_IUnknown) ||
1176 IsEqualGUID(riid, &IID_IDirectSoundBuffer) ) {
1177 IDirectSoundBuffer_AddRef(iface);
1182 /* DirectSoundBuffer and DirectSoundBuffer8 are different and */
1183 /* a primary buffer can't have a DirectSoundBuffer8 interface */
1184 if ( IsEqualGUID( &IID_IDirectSoundBuffer8, riid ) ) {
1185 WARN("app requested DirectSoundBuffer8 on primary buffer\n");
1186 return E_NOINTERFACE;
1189 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1190 ERR("app requested IDirectSoundNotify on primary buffer\n");
1191 /* FIXME: should we support this? */
1192 return E_NOINTERFACE;
1195 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1196 ERR("app requested IDirectSound3DBuffer on primary buffer\n");
1197 return E_NOINTERFACE;
1200 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1201 *ppobj = &This->IDirectSound3DListener_iface;
1202 IDirectSound3DListener_AddRef(&This->IDirectSound3DListener_iface);
1206 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1207 *ppobj = &This->IKsPropertySet_iface;
1208 IKsPropertySet_AddRef(&This->IKsPropertySet_iface);
1212 FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
1213 return E_NOINTERFACE;
1216 static const IDirectSoundBufferVtbl dspbvt =
1218 PrimaryBufferImpl_QueryInterface,
1219 PrimaryBufferImpl_AddRef,
1220 PrimaryBufferImpl_Release,
1221 PrimaryBufferImpl_GetCaps,
1222 PrimaryBufferImpl_GetCurrentPosition,
1223 PrimaryBufferImpl_GetFormat,
1224 PrimaryBufferImpl_GetVolume,
1225 PrimaryBufferImpl_GetPan,
1226 PrimaryBufferImpl_GetFrequency,
1227 PrimaryBufferImpl_GetStatus,
1228 PrimaryBufferImpl_Initialize,
1229 PrimaryBufferImpl_Lock,
1230 PrimaryBufferImpl_Play,
1231 PrimaryBufferImpl_SetCurrentPosition,
1232 PrimaryBufferImpl_SetFormat,
1233 PrimaryBufferImpl_SetVolume,
1234 PrimaryBufferImpl_SetPan,
1235 PrimaryBufferImpl_SetFrequency,
1236 PrimaryBufferImpl_Stop,
1237 PrimaryBufferImpl_Unlock,
1238 PrimaryBufferImpl_Restore
1241 HRESULT primarybuffer_create(DirectSoundDevice *device, IDirectSoundBufferImpl **ppdsb,
1242 const DSBUFFERDESC *dsbd)
1244 IDirectSoundBufferImpl *dsb;
1245 TRACE("%p,%p,%p)\n",device,ppdsb,dsbd);
1247 if (dsbd->lpwfxFormat) {
1248 WARN("invalid parameter: dsbd->lpwfxFormat != NULL\n");
1250 return DSERR_INVALIDPARAM;
1253 dsb = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
1256 WARN("out of memory\n");
1258 return DSERR_OUTOFMEMORY;
1265 dsb->device = device;
1266 dsb->IDirectSoundBuffer8_iface.lpVtbl = (IDirectSoundBuffer8Vtbl *)&dspbvt;
1267 dsb->IDirectSound3DListener_iface.lpVtbl = &ds3dlvt;
1268 dsb->IKsPropertySet_iface.lpVtbl = &iksbvt;
1271 /* IDirectSound3DListener */
1272 device->ds3dl.dwSize = sizeof(DS3DLISTENER);
1273 device->ds3dl.vPosition.x = 0.0;
1274 device->ds3dl.vPosition.y = 0.0;
1275 device->ds3dl.vPosition.z = 0.0;
1276 device->ds3dl.vVelocity.x = 0.0;
1277 device->ds3dl.vVelocity.y = 0.0;
1278 device->ds3dl.vVelocity.z = 0.0;
1279 device->ds3dl.vOrientFront.x = 0.0;
1280 device->ds3dl.vOrientFront.y = 0.0;
1281 device->ds3dl.vOrientFront.z = 1.0;
1282 device->ds3dl.vOrientTop.x = 0.0;
1283 device->ds3dl.vOrientTop.y = 1.0;
1284 device->ds3dl.vOrientTop.z = 0.0;
1285 device->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1286 device->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1287 device->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
1288 device->ds3dl_need_recalc = TRUE;
1290 TRACE("Created primary buffer at %p\n", dsb);
1291 TRACE("(formattag=0x%04x,chans=%d,samplerate=%d,"
1292 "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1293 device->pwfx->wFormatTag, device->pwfx->nChannels,
1294 device->pwfx->nSamplesPerSec, device->pwfx->nAvgBytesPerSec,
1295 device->pwfx->nBlockAlign, device->pwfx->wBitsPerSample,
1296 device->pwfx->cbSize);
1298 IDirectSoundBuffer_AddRef(&dsb->IDirectSoundBuffer8_iface);