Report the EAX last error as settable
[dsound-openal.git] / primary.c
blob314d2ee0f739977049b3e12b6e7a1528d1a198f3
1 /* DirectSound COM interface
3 * Copyright 2009 Maarten Lankhorst
5 * Some code taken from the original dsound-openal implementation
6 * Copyright 2007-2009 Chris Robinson
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define CONST_VTABLE
24 #include <stdarg.h>
25 #include <string.h>
27 #include <windows.h>
28 #include <dsound.h>
29 #include <mmsystem.h>
31 #include "dsound_private.h"
32 #include "eax-presets.h"
35 #ifndef E_PROP_ID_UNSUPPORTED
36 #define E_PROP_ID_UNSUPPORTED ((HRESULT)0x80070490)
37 #endif
40 static const IDirectSoundBufferVtbl DSPrimary_Vtbl;
41 static const IDirectSound3DListenerVtbl DSPrimary3D_Vtbl;
42 static const IKsPropertySetVtbl DSPrimaryProp_Vtbl;
44 static void DSPrimary_SetParams(DSPrimary *This, const DS3DLISTENER *params, LONG flags);
47 static inline DSPrimary *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
49 return CONTAINING_RECORD(iface, DSPrimary, IDirectSoundBuffer_iface);
52 static inline DSPrimary *impl_from_IDirectSound3DListener(IDirectSound3DListener *iface)
54 return CONTAINING_RECORD(iface, DSPrimary, IDirectSound3DListener_iface);
57 static inline DSPrimary *impl_from_IKsPropertySet(IKsPropertySet *iface)
59 return CONTAINING_RECORD(iface, DSPrimary, IKsPropertySet_iface);
63 static void trigger_elapsed_notifies(DSBuffer *buf, DWORD lastpos, DWORD curpos)
65 DSBPOSITIONNOTIFY *not = buf->notify;
66 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
67 for(;not != not_end;++not)
69 HANDLE event = not->hEventNotify;
70 DWORD ofs = not->dwOffset;
72 if(ofs == (DWORD)DSBPN_OFFSETSTOP)
73 continue;
75 if(curpos < lastpos) /* Wraparound case */
77 if(ofs < curpos || ofs >= lastpos)
79 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
80 SetEvent(event);
83 else if(ofs >= lastpos && ofs < curpos) /* Normal case */
85 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
86 SetEvent(event);
91 static void trigger_stop_notifies(DSBuffer *buf)
93 DSBPOSITIONNOTIFY *not = buf->notify;
94 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
95 for(;not != not_end;++not)
97 if(not->dwOffset != (DWORD)DSBPN_OFFSETSTOP)
98 continue;
99 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
100 SetEvent(not->hEventNotify);
104 void DSPrimary_triggernots(DSPrimary *prim)
106 DSBuffer **curnot, **endnot;
108 curnot = prim->notifies;
109 endnot = curnot + prim->nnotifies;
110 while(curnot != endnot)
112 DSBuffer *buf = *curnot;
113 DSData *data = buf->buffer;
114 DWORD curpos = buf->lastpos;
115 ALint state = 0;
116 ALint ofs;
118 alGetSourcei(buf->source, AL_BYTE_OFFSET, &ofs);
119 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
120 if(buf->segsize == 0)
121 curpos = (state == AL_STOPPED) ? data->buf_size : ofs;
122 else
124 if(state != AL_STOPPED)
125 curpos = ofs + buf->queue_base;
126 else
128 ALint queued;
129 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
130 curpos = buf->segsize*queued + buf->queue_base;
133 if(curpos >= (DWORD)data->buf_size)
135 if(buf->islooping)
136 curpos %= (DWORD)data->buf_size;
137 else if(buf->isplaying)
139 curpos = data->buf_size;
140 alSourceStop(buf->source);
141 alSourcei(buf->source, AL_BUFFER, 0);
142 buf->curidx = 0;
143 buf->isplaying = FALSE;
147 if(state != AL_PLAYING)
148 state = buf->isplaying ? AL_PLAYING : AL_PAUSED;
150 checkALError();
152 if(buf->lastpos != curpos)
154 trigger_elapsed_notifies(buf, buf->lastpos, curpos);
155 buf->lastpos = curpos;
157 if(state != AL_PLAYING)
159 /* Remove this buffer from list and put another at the current
160 * position; don't increment i
162 trigger_stop_notifies(buf);
163 *curnot = *(--endnot);
164 prim->nnotifies--;
165 continue;
167 curnot++;
169 checkALError();
172 static void do_buffer_stream(DSBuffer *buf, BYTE *scratch_mem)
174 DSData *data = buf->buffer;
175 ALint ofs, done = 0, queued = QBUFFERS, state = AL_PLAYING;
176 ALuint which;
178 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
179 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
180 alGetSourcei(buf->source, AL_BUFFERS_PROCESSED, &done);
182 if(done > 0)
184 ALuint bids[QBUFFERS];
185 queued -= done;
187 alSourceUnqueueBuffers(buf->source, done, bids);
188 buf->queue_base = (buf->queue_base + buf->segsize*done) % data->buf_size;
190 while(queued < QBUFFERS)
192 which = buf->stream_bids[buf->curidx];
193 ofs = buf->data_offset;
195 if(buf->segsize < data->buf_size - ofs)
197 alBufferData(which, data->buf_format, data->data + ofs, buf->segsize,
198 data->format.Format.nSamplesPerSec);
199 buf->data_offset = ofs + buf->segsize;
201 else if(buf->islooping)
203 ALsizei rem = data->buf_size - ofs;
204 if(rem > 2048) rem = 2048;
206 memcpy(scratch_mem, data->data + ofs, rem);
207 while(rem < buf->segsize)
209 ALsizei todo = buf->segsize - rem;
210 if(todo > data->buf_size)
211 todo = data->buf_size;
212 memcpy(scratch_mem + rem, data->data, todo);
213 rem += todo;
215 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
216 data->format.Format.nSamplesPerSec);
217 buf->data_offset = (ofs+buf->segsize) % data->buf_size;
219 else
221 ALsizei rem = data->buf_size - ofs;
222 if(rem > 2048) rem = 2048;
223 if(rem == 0) break;
225 memcpy(scratch_mem, data->data + ofs, rem);
226 memset(scratch_mem+rem, (data->format.Format.wBitsPerSample==8) ? 128 : 0,
227 buf->segsize - rem);
228 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
229 data->format.Format.nSamplesPerSec);
230 buf->data_offset = data->buf_size;
233 alSourceQueueBuffers(buf->source, 1, &which);
234 buf->curidx = (buf->curidx+1)%QBUFFERS;
235 queued++;
238 if(!queued)
240 buf->data_offset = 0;
241 buf->queue_base = data->buf_size;
242 buf->curidx = 0;
243 buf->isplaying = FALSE;
245 else if(state != AL_PLAYING)
246 alSourcePlay(buf->source);
249 void DSPrimary_streamfeeder(DSPrimary *prim, BYTE *scratch_mem)
251 /* OpenAL doesn't support our lovely buffer extensions so just make sure
252 * enough buffers are queued for streaming
254 if(prim->write_emu)
256 DSBuffer *buf = CONTAINING_RECORD(prim->write_emu, DSBuffer, IDirectSoundBuffer8_iface);
257 if(buf->segsize != 0 && buf->isplaying)
258 do_buffer_stream(buf, scratch_mem);
260 else
262 struct DSBufferGroup *bufgroup = prim->BufferGroups;
263 struct DSBufferGroup *endgroup = bufgroup + prim->NumBufferGroups;
264 for(;bufgroup != endgroup;++bufgroup)
266 DWORD64 usemask = ~bufgroup->FreeBuffers;
267 while(usemask)
269 int idx = CTZ64(usemask);
270 DSBuffer *buf = bufgroup->Buffers + idx;
271 usemask &= ~(U64(1) << idx);
273 if(buf->segsize != 0 && buf->isplaying)
274 do_buffer_stream(buf, scratch_mem);
278 checkALError();
282 HRESULT DSPrimary_PreInit(DSPrimary *This, DSDevice *parent)
284 WAVEFORMATEX *wfx;
285 DWORD num_srcs;
286 DWORD count;
287 HRESULT hr;
288 DWORD i;
290 This->IDirectSoundBuffer_iface.lpVtbl = &DSPrimary_Vtbl;
291 This->IDirectSound3DListener_iface.lpVtbl = &DSPrimary3D_Vtbl;
292 This->IKsPropertySet_iface.lpVtbl = &DSPrimaryProp_Vtbl;
294 This->parent = parent;
295 This->share = parent->share;
296 This->ctx = parent->share->ctx;
297 This->refresh = parent->share->refresh;
298 for(i = 0;i < EAX_MAX_FXSLOTS;++i)
299 This->auxslot[i] = parent->share->auxslot[i];
301 wfx = &This->format.Format;
302 wfx->wFormatTag = WAVE_FORMAT_PCM;
303 wfx->nChannels = 2;
304 wfx->wBitsPerSample = 8;
305 wfx->nSamplesPerSec = 22050;
306 wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
307 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
308 wfx->cbSize = 0;
310 This->stopped = TRUE;
312 /* EAX allows the buffer's direct and room volumes to be up to +1000mB
313 * (~3.162 linear gain), but standard EFX's source filters only allow a
314 * maximum gain of 1 (+0mB). The not-currently-final AL_SOFT_filter_gain_ex
315 * extension increases the maximum filter gain to 4 (about +1200mB), which
316 * is enough to handle the original +1000mB range.
318 This->filter_mBLimit = HAS_EXTENSION(This->share, SOFTX_FILTER_GAIN_EX) ? 1000.0f : 0.0f;
320 /* Apparently primary buffer size is always 32k,
321 * tested on windows with 192k 24 bits sound @ 6 channels
322 * where it will run out in 60 ms and it isn't pointer aligned
324 This->buf_size = 32768;
326 This->eax_error = EAX_OK;
327 This->current.ctx.guidPrimaryFXSlotID = EAXPROPERTYID_EAX40_FXSlot0;
328 This->current.ctx.flDistanceFactor = 1.0f;
329 This->current.ctx.flAirAbsorptionHF = -5.0f;
330 This->current.ctx.flHFReference = 5000.0f;
331 This->current.fxslot[0].effect_type = FXSLOT_EFFECT_REVERB;
332 This->current.fxslot[0].fx.reverb = EnvironmentDefaults[EAX_ENVIRONMENT_GENERIC];
333 This->current.fxslot[0].props.guidLoadEffect = EAX_REVERB_EFFECT;
334 This->current.fxslot[0].props.lVolume = 0;
335 This->current.fxslot[0].props.lLock = EAXFXSLOT_UNLOCKED;
336 This->current.fxslot[0].props.dwFlags = EAXFXSLOTFLAGS_ENVIRONMENT;
337 /* FIXME: Should fxslot[1] be chorus? Or left as a NULL effect? */
338 for(i = 1;i < EAX_MAX_FXSLOTS;++i)
340 This->current.fxslot[i].effect_type = FXSLOT_EFFECT_NULL;
341 This->current.fxslot[i].props.guidLoadEffect = EAX_NULL_GUID;
342 This->current.fxslot[i].props.lVolume = 0;
343 This->current.fxslot[i].props.lLock = EAXFXSLOT_UNLOCKED;
344 This->current.fxslot[i].props.dwFlags = EAXFXSLOTFLAGS_ENVIRONMENT;
346 This->current.eax1_volume = 0.5f;
347 This->current.eax1_dampening = 0.5f;
349 This->deferred.ctx = This->current.ctx;
350 for(i = 0;i < EAX_MAX_FXSLOTS;++i)
351 This->deferred.fxslot[i] = This->current.fxslot[i];
352 This->deferred.eax1_volume = This->current.eax1_volume;
353 This->deferred.eax1_dampening = This->current.eax1_dampening;
355 setALContext(This->ctx);
356 if(This->auxslot[0] != 0)
358 ALenum err;
360 This->primary_slot = This->auxslot[0];
362 alGetError();
363 alGenEffects(EAX_MAX_FXSLOTS, This->effect);
364 if((err=alGetError()) == AL_NO_ERROR)
366 alEffecti(This->effect[0], AL_EFFECT_TYPE, AL_EFFECT_EAXREVERB);
367 if((err=alGetError()) != AL_NO_ERROR)
369 alDeleteEffects(EAX_MAX_FXSLOTS, This->effect);
370 checkALError();
373 if(err != AL_NO_ERROR)
375 ERR("Failed to setup effects: %s (0x%04x)\n", alGetString(err), err);
376 This->effect[0] = This->effect[1] =
377 This->effect[2] = This->effect[3] = 0;
380 popALContext();
382 num_srcs = This->share->sources.maxhw_alloc + This->share->sources.maxsw_alloc;
384 hr = DSERR_OUTOFMEMORY;
385 This->notifies = HeapAlloc(GetProcessHeap(), 0, num_srcs*sizeof(*This->notifies));
386 if(!This->notifies) goto fail;
387 This->sizenotifies = num_srcs;
389 count = (MAX_HWBUFFERS+63) / 64;
390 This->BufferGroups = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
391 count*sizeof(*This->BufferGroups));
392 if(!This->BufferGroups) goto fail;
394 for(i = 0;i < count;++i)
396 This->BufferGroups[i].Buffers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
397 64*sizeof(This->BufferGroups[0].Buffers[0]));
398 if(!This->BufferGroups[i].Buffers)
400 while(i > 0)
401 HeapFree(GetProcessHeap(), 0, This->BufferGroups[--i].Buffers);
402 HeapFree(GetProcessHeap(), 0, This->BufferGroups);
403 This->BufferGroups = NULL;
404 goto fail;
406 This->BufferGroups[i].FreeBuffers = ~(DWORD64)0;
408 This->NumBufferGroups = count;
410 return S_OK;
412 fail:
413 DSPrimary_Clear(This);
414 return hr;
417 void DSPrimary_Clear(DSPrimary *This)
419 struct DSBufferGroup *bufgroup;
420 DWORD i;
422 if(!This->parent)
423 return;
425 setALContext(This->ctx);
426 if(This->effect[0])
427 alDeleteEffects(EAX_MAX_FXSLOTS, This->effect);
428 checkALError();
429 popALContext();
431 bufgroup = This->BufferGroups;
432 for(i = 0;i < This->NumBufferGroups;++i)
434 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
435 while(usemask)
437 int idx = CTZ64(usemask);
438 DSBuffer *buf = bufgroup[i].Buffers + idx;
439 usemask &= ~(U64(1) << idx);
441 DSBuffer_Destroy(buf);
443 HeapFree(GetProcessHeap(), 0, This->BufferGroups[i].Buffers);
446 HeapFree(GetProcessHeap(), 0, This->BufferGroups);
447 HeapFree(GetProcessHeap(), 0, This->notifies);
448 memset(This, 0, sizeof(*This));
451 static HRESULT WINAPI DSPrimary_QueryInterface(IDirectSoundBuffer *iface, REFIID riid, LPVOID *ppv)
453 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
455 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
457 *ppv = NULL;
458 if(IsEqualIID(riid, &IID_IUnknown) ||
459 IsEqualIID(riid, &IID_IDirectSoundBuffer))
460 *ppv = &This->IDirectSoundBuffer_iface;
461 else if(IsEqualIID(riid, &IID_IDirectSound3DListener))
463 if((This->flags&DSBCAPS_CTRL3D))
464 *ppv = &This->IDirectSound3DListener_iface;
466 else if(IsEqualIID(riid, &IID_IKsPropertySet))
467 *ppv = &This->IKsPropertySet_iface;
468 else
469 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
471 if(*ppv)
473 IUnknown_AddRef((IUnknown*)*ppv);
474 return S_OK;
477 return E_NOINTERFACE;
480 static ULONG WINAPI DSPrimary_AddRef(IDirectSoundBuffer *iface)
482 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
483 LONG ret;
485 ret = InterlockedIncrement(&This->ref);
486 if(ret == 1) This->flags = 0;
487 TRACE("(%p) ref %lu\n", iface, ret);
489 return ret;
492 static ULONG WINAPI DSPrimary_Release(IDirectSoundBuffer *iface)
494 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
495 LONG ref, oldval;
497 oldval = *(volatile LONG*)&This->ref;
498 do {
499 ref = oldval;
500 if(ref)
502 --ref;
503 oldval = InterlockedCompareExchange(&This->ref, ref, ref+1)-1;
505 } while(oldval != ref);
506 TRACE("(%p) ref %lu\n", iface, ref);
508 return ref;
511 static HRESULT WINAPI DSPrimary_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
513 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
515 TRACE("(%p)->(%p)\n", iface, caps);
517 if(!caps || caps->dwSize < sizeof(*caps))
519 WARN("Invalid DSBCAPS (%p, %lu)\n", caps, caps ? caps->dwSize : 0);
520 return DSERR_INVALIDPARAM;
523 caps->dwFlags = This->flags;
524 caps->dwBufferBytes = This->buf_size;
525 caps->dwUnlockTransferRate = 0;
526 caps->dwPlayCpuOverhead = 0;
528 return DS_OK;
531 static HRESULT WINAPI DSPrimary_GetCurrentPosition(IDirectSoundBuffer *iface, DWORD *playpos, DWORD *curpos)
533 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
534 HRESULT hr = DSERR_PRIOLEVELNEEDED;
536 EnterCriticalSection(&This->share->crst);
537 if(This->write_emu)
538 hr = IDirectSoundBuffer_GetCurrentPosition(This->write_emu, playpos, curpos);
539 LeaveCriticalSection(&This->share->crst);
541 return hr;
544 static HRESULT WINAPI DSPrimary_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
546 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
547 HRESULT hr = S_OK;
548 UINT size;
550 if(!wfx && !written)
552 WARN("Cannot report format or format size\n");
553 return DSERR_INVALIDPARAM;
556 EnterCriticalSection(&This->share->crst);
557 size = sizeof(This->format.Format) + This->format.Format.cbSize;
558 if(written)
559 *written = size;
560 if(wfx)
562 if(allocated < size)
563 hr = DSERR_INVALIDPARAM;
564 else
565 memcpy(wfx, &This->format.Format, size);
567 LeaveCriticalSection(&This->share->crst);
569 return hr;
572 static HRESULT WINAPI DSPrimary_GetVolume(IDirectSoundBuffer *iface, LONG *volume)
574 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
575 ALfloat gain;
577 TRACE("(%p)->(%p)\n", iface, volume);
579 if(!volume)
580 return DSERR_INVALIDPARAM;
581 *volume = 0;
583 if(!(This->flags&DSBCAPS_CTRLVOLUME))
584 return DSERR_CONTROLUNAVAIL;
586 setALContext(This->ctx);
587 alGetListenerf(AL_GAIN, &gain);
588 checkALError();
589 popALContext();
591 *volume = clampI(gain_to_mB(gain), DSBVOLUME_MIN, DSBVOLUME_MAX);
592 return DS_OK;
595 static HRESULT WINAPI DSPrimary_GetPan(IDirectSoundBuffer *iface, LONG *pan)
597 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
598 HRESULT hr = DS_OK;
600 WARN("(%p)->(%p): semi-stub\n", iface, pan);
602 if(!pan)
603 return DSERR_INVALIDPARAM;
605 EnterCriticalSection(&This->share->crst);
606 if(This->write_emu)
607 hr = IDirectSoundBuffer_GetPan(This->write_emu, pan);
608 else if(!(This->flags & DSBCAPS_CTRLPAN))
609 hr = DSERR_CONTROLUNAVAIL;
610 else
611 *pan = 0;
612 LeaveCriticalSection(&This->share->crst);
614 return hr;
617 static HRESULT WINAPI DSPrimary_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
619 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
620 HRESULT hr = DS_OK;
622 WARN("(%p)->(%p): semi-stub\n", iface, freq);
624 if(!freq)
625 return DSERR_INVALIDPARAM;
627 if(!(This->flags&DSBCAPS_CTRLFREQUENCY))
628 return DSERR_CONTROLUNAVAIL;
630 EnterCriticalSection(&This->share->crst);
631 *freq = This->format.Format.nSamplesPerSec;
632 LeaveCriticalSection(&This->share->crst);
634 return hr;
637 static HRESULT WINAPI DSPrimary_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
639 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
641 TRACE("(%p)->(%p)\n", iface, status);
643 if(!status)
644 return DSERR_INVALIDPARAM;
646 EnterCriticalSection(&This->share->crst);
647 *status = DSBSTATUS_PLAYING|DSBSTATUS_LOOPING;
648 if((This->flags&DSBCAPS_LOCDEFER))
649 *status |= DSBSTATUS_LOCHARDWARE;
651 if(This->stopped)
653 struct DSBufferGroup *bufgroup = This->BufferGroups;
654 DWORD i, state = 0;
655 HRESULT hr;
657 for(i = 0;i < This->NumBufferGroups;++i)
659 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
660 while(usemask)
662 int idx = CTZ64(usemask);
663 DSBuffer *buf = bufgroup[i].Buffers + idx;
664 usemask &= ~(U64(1) << idx);
666 hr = DSBuffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state);
667 if(SUCCEEDED(hr) && (state&DSBSTATUS_PLAYING)) break;
670 if(!(state&DSBSTATUS_PLAYING))
672 /* Primary stopped and no buffers playing.. */
673 *status = 0;
676 LeaveCriticalSection(&This->share->crst);
678 return DS_OK;
681 HRESULT WINAPI DSPrimary_Initialize(IDirectSoundBuffer *iface, IDirectSound *ds, const DSBUFFERDESC *desc)
683 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
684 HRESULT hr;
686 TRACE("(%p)->(%p, %p)\n", iface, ds, desc);
688 if(!desc || desc->lpwfxFormat || desc->dwBufferBytes)
690 WARN("Bad DSBDESC for primary buffer\n");
691 return DSERR_INVALIDPARAM;
693 if((desc->dwFlags&DSBCAPS_CTRLFX) ||
694 (desc->dwFlags&DSBCAPS_CTRLPOSITIONNOTIFY) ||
695 (desc->dwFlags&DSBCAPS_LOCSOFTWARE))
697 WARN("Bad dwFlags %08lx\n", desc->dwFlags);
698 return DSERR_INVALIDPARAM;
701 /* Should be 0 if not initialized */
702 if(This->flags)
703 return DSERR_ALREADYINITIALIZED;
705 hr = DS_OK;
706 if(This->parent->prio_level == DSSCL_WRITEPRIMARY)
708 DSBUFFERDESC emudesc;
709 DSBuffer *emu;
711 if(This->write_emu)
713 ERR("There shouldn't be a write_emu!\n");
714 IDirectSoundBuffer_Release(This->write_emu);
715 This->write_emu = NULL;
718 memset(&emudesc, 0, sizeof(emudesc));
719 emudesc.dwSize = sizeof(emudesc);
720 emudesc.dwFlags = DSBCAPS_LOCHARDWARE | (desc->dwFlags&DSBCAPS_CTRLPAN);
721 /* Dont play last incomplete sample */
722 emudesc.dwBufferBytes = This->buf_size - (This->buf_size%This->format.Format.nBlockAlign);
723 emudesc.lpwfxFormat = &This->format.Format;
725 hr = DSBuffer_Create(&emu, This, NULL);
726 if(SUCCEEDED(hr))
728 hr = DSBuffer_Initialize(&emu->IDirectSoundBuffer8_iface, ds, &emudesc);
729 if(SUCCEEDED(hr))
730 hr = DSBuffer_GetInterface(emu, &IID_IDirectSoundBuffer, (void**)&This->write_emu);
731 if(FAILED(hr))
732 DSBuffer_Destroy(emu);
736 if(SUCCEEDED(hr))
738 This->current.ds3d.dwSize = sizeof(This->current.ds3d);
739 This->current.ds3d.vPosition.x = 0.0f;
740 This->current.ds3d.vPosition.y = 0.0f;
741 This->current.ds3d.vPosition.z = 0.0f;
742 This->current.ds3d.vVelocity.x = 0.0f;
743 This->current.ds3d.vVelocity.y = 0.0f;
744 This->current.ds3d.vVelocity.z = 0.0f;
745 This->current.ds3d.vOrientFront.x = 0.0f;
746 This->current.ds3d.vOrientFront.y = 0.0f;
747 This->current.ds3d.vOrientFront.z = 1.0f;
748 This->current.ds3d.vOrientTop.x = 0.0f;
749 This->current.ds3d.vOrientTop.y = 1.0f;
750 This->current.ds3d.vOrientTop.z = 0.0f;
751 This->current.ds3d.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
752 This->current.ds3d.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
753 This->current.ds3d.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
754 This->deferred.ds3d = This->current.ds3d;
756 This->flags = desc->dwFlags | DSBCAPS_LOCHARDWARE;
758 if((This->flags&DSBCAPS_CTRL3D))
760 union PrimaryParamFlags dirty = { 0l };
762 dirty.bit.pos = 1;
763 dirty.bit.vel = 1;
764 dirty.bit.orientation = 1;
765 dirty.bit.distancefactor = 1;
766 dirty.bit.rollofffactor = 1;
767 dirty.bit.dopplerfactor = 1;
768 DSPrimary_SetParams(This, &This->deferred.ds3d, dirty.flags);
771 return hr;
774 static HRESULT WINAPI DSPrimary_Lock(IDirectSoundBuffer *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
776 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
777 HRESULT hr = DSERR_PRIOLEVELNEEDED;
779 TRACE("(%p)->(%lu, %lu, %p, %p, %p, %p, %lu)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
781 EnterCriticalSection(&This->share->crst);
782 if(This->write_emu)
783 hr = IDirectSoundBuffer_Lock(This->write_emu, ofs, bytes, ptr1, len1, ptr2, len2, flags);
784 LeaveCriticalSection(&This->share->crst);
786 return hr;
789 static HRESULT WINAPI DSPrimary_Play(IDirectSoundBuffer *iface, DWORD res1, DWORD res2, DWORD flags)
791 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
792 HRESULT hr;
794 TRACE("(%p)->(%lu, %lu, %lu)\n", iface, res1, res2, flags);
796 if(!(flags & DSBPLAY_LOOPING))
798 WARN("Flags (%08lx) not set to DSBPLAY_LOOPING\n", flags);
799 return DSERR_INVALIDPARAM;
802 EnterCriticalSection(&This->share->crst);
803 hr = S_OK;
804 if(This->write_emu)
805 hr = IDirectSoundBuffer_Play(This->write_emu, res1, res2, flags);
806 if(SUCCEEDED(hr))
807 This->stopped = FALSE;
808 LeaveCriticalSection(&This->share->crst);
810 return hr;
813 static HRESULT WINAPI DSPrimary_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD pos)
815 WARN("(%p)->(%lu)\n", iface, pos);
816 return DSERR_INVALIDCALL;
819 /* Just assume the format is crap, and clean up the damage */
820 static HRESULT copy_waveformat(WAVEFORMATEX *wfx, const WAVEFORMATEX *from)
822 if(from->nChannels <= 0)
824 WARN("Invalid Channels %d\n", from->nChannels);
825 return DSERR_INVALIDPARAM;
827 if(from->nSamplesPerSec < DSBFREQUENCY_MIN || from->nSamplesPerSec > DSBFREQUENCY_MAX)
829 WARN("Invalid SamplesPerSec %lu\n", from->nSamplesPerSec);
830 return DSERR_INVALIDPARAM;
832 if(from->nBlockAlign <= 0)
834 WARN("Invalid BlockAlign %d\n", from->nBlockAlign);
835 return DSERR_INVALIDPARAM;
837 if(from->wBitsPerSample == 0 || (from->wBitsPerSample%8) != 0)
839 WARN("Invalid BitsPerSample %d\n", from->wBitsPerSample);
840 return DSERR_INVALIDPARAM;
842 if(from->nBlockAlign != from->nChannels*from->wBitsPerSample/8)
844 WARN("Invalid BlockAlign %d (expected %u = %u*%u/8)\n",
845 from->nBlockAlign, from->nChannels*from->wBitsPerSample/8,
846 from->nChannels, from->wBitsPerSample);
847 return DSERR_INVALIDPARAM;
849 if(from->nAvgBytesPerSec != from->nBlockAlign*from->nSamplesPerSec)
851 WARN("Invalid AvgBytesPerSec %lu (expected %lu = %lu*%u)\n",
852 from->nAvgBytesPerSec, from->nSamplesPerSec*from->nBlockAlign,
853 from->nSamplesPerSec, from->nBlockAlign);
854 return DSERR_INVALIDPARAM;
857 if(from->wFormatTag == WAVE_FORMAT_PCM)
859 if(from->wBitsPerSample > 32)
860 return DSERR_INVALIDPARAM;
861 wfx->cbSize = 0;
863 else if(from->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
865 if(from->wBitsPerSample != 32)
866 return DSERR_INVALIDPARAM;
867 wfx->cbSize = 0;
869 else if(from->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
871 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx;
872 const WAVEFORMATEXTENSIBLE *fromx = (const WAVEFORMATEXTENSIBLE*)from;
873 const WORD size = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
875 /* Fail silently.. */
876 if(from->cbSize < size) return DS_OK;
877 if(fromx->Samples.wValidBitsPerSample > fromx->Format.wBitsPerSample)
878 return DSERR_INVALIDPARAM;
880 if(IsEqualGUID(&fromx->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
882 if(from->wBitsPerSample > 32)
883 return DSERR_INVALIDPARAM;
885 else if(IsEqualGUID(&fromx->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
887 if(from->wBitsPerSample != 32)
888 return DSERR_INVALIDPARAM;
890 else
892 ERR("Unhandled extensible format: %s\n", debugstr_guid(&fromx->SubFormat));
893 return DSERR_INVALIDPARAM;
896 wfe->Format.cbSize = size;
897 wfe->Samples.wValidBitsPerSample = fromx->Samples.wValidBitsPerSample;
898 if(!wfe->Samples.wValidBitsPerSample)
899 wfe->Samples.wValidBitsPerSample = fromx->Format.wBitsPerSample;
900 wfe->dwChannelMask = fromx->dwChannelMask;
901 wfe->SubFormat = fromx->SubFormat;
903 else
905 ERR("Unhandled format tag %04x\n", from->wFormatTag);
906 return DSERR_INVALIDPARAM;
909 wfx->wFormatTag = from->wFormatTag;
910 wfx->nChannels = from->nChannels;
911 wfx->nSamplesPerSec = from->nSamplesPerSec;
912 wfx->nAvgBytesPerSec = from->nSamplesPerSec * from->nBlockAlign;
913 wfx->nBlockAlign = from->wBitsPerSample * from->nChannels / 8;
914 wfx->wBitsPerSample = from->wBitsPerSample;
915 return DS_OK;
918 static HRESULT WINAPI DSPrimary_SetFormat(IDirectSoundBuffer *iface, const WAVEFORMATEX *wfx)
920 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
921 HRESULT hr = S_OK;
923 TRACE("(%p)->(%p)\n", iface, wfx);
925 if(!wfx)
927 WARN("Missing format\n");
928 return DSERR_INVALIDPARAM;
931 EnterCriticalSection(&This->share->crst);
933 if(This->parent->prio_level < DSSCL_PRIORITY)
935 hr = DSERR_PRIOLEVELNEEDED;
936 goto out;
939 TRACE("Requested primary format:\n"
940 " FormatTag = %04x\n"
941 " Channels = %u\n"
942 " SamplesPerSec = %lu\n"
943 " AvgBytesPerSec = %lu\n"
944 " BlockAlign = %u\n"
945 " BitsPerSample = %u\n",
946 wfx->wFormatTag, wfx->nChannels,
947 wfx->nSamplesPerSec, wfx->nAvgBytesPerSec,
948 wfx->nBlockAlign, wfx->wBitsPerSample);
950 hr = copy_waveformat(&This->format.Format, wfx);
951 if(SUCCEEDED(hr) && This->write_emu)
953 DSBuffer *buf;
954 DSBUFFERDESC desc;
956 IDirectSoundBuffer_Release(This->write_emu);
957 This->write_emu = NULL;
959 memset(&desc, 0, sizeof(desc));
960 desc.dwSize = sizeof(desc);
961 desc.dwFlags = DSBCAPS_LOCHARDWARE | DSBCAPS_CTRLPAN;
962 desc.dwBufferBytes = This->buf_size - (This->buf_size % This->format.Format.nBlockAlign);
963 desc.lpwfxFormat = &This->format.Format;
965 hr = DSBuffer_Create(&buf, This, NULL);
966 if(SUCCEEDED(hr))
968 hr = DSBuffer_Initialize(&buf->IDirectSoundBuffer8_iface,
969 &This->parent->IDirectSound_iface, &desc);
970 if(SUCCEEDED(hr))
971 hr = DSBuffer_GetInterface(buf, &IID_IDirectSoundBuffer, (void**)&This->write_emu);
972 if(FAILED(hr))
973 DSBuffer_Destroy(buf);
977 out:
978 LeaveCriticalSection(&This->share->crst);
979 return hr;
982 static HRESULT WINAPI DSPrimary_SetVolume(IDirectSoundBuffer *iface, LONG vol)
984 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
986 TRACE("(%p)->(%ld)\n", iface, vol);
988 if(vol > DSBVOLUME_MAX || vol < DSBVOLUME_MIN)
990 WARN("Invalid volume (%ld)\n", vol);
991 return DSERR_INVALIDPARAM;
994 if(!(This->flags&DSBCAPS_CTRLVOLUME))
995 return DSERR_CONTROLUNAVAIL;
997 setALContext(This->ctx);
998 alListenerf(AL_GAIN, mB_to_gain(vol));
999 popALContext();
1001 return DS_OK;
1004 static HRESULT WINAPI DSPrimary_SetPan(IDirectSoundBuffer *iface, LONG pan)
1006 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
1007 HRESULT hr;
1009 TRACE("(%p)->(%ld)\n", iface, pan);
1011 if(pan > DSBPAN_RIGHT || pan < DSBPAN_LEFT)
1013 WARN("invalid parameter: pan = %ld\n", pan);
1014 return DSERR_INVALIDPARAM;
1017 EnterCriticalSection(&This->share->crst);
1018 if(!(This->flags&DSBCAPS_CTRLPAN))
1020 WARN("control unavailable\n");
1021 hr = DSERR_CONTROLUNAVAIL;
1023 else if(This->write_emu)
1024 hr = IDirectSoundBuffer_SetPan(This->write_emu, pan);
1025 else
1027 FIXME("Not supported\n");
1028 hr = E_NOTIMPL;
1030 LeaveCriticalSection(&This->share->crst);
1032 return hr;
1035 static HRESULT WINAPI DSPrimary_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
1037 WARN("(%p)->(%lu)\n", iface, freq);
1038 return DSERR_CONTROLUNAVAIL;
1041 static HRESULT WINAPI DSPrimary_Stop(IDirectSoundBuffer *iface)
1043 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
1044 HRESULT hr = S_OK;
1046 TRACE("(%p)->()\n", iface);
1048 EnterCriticalSection(&This->share->crst);
1049 if(This->write_emu)
1050 hr = IDirectSoundBuffer_Stop(This->write_emu);
1051 if(SUCCEEDED(hr))
1052 This->stopped = TRUE;
1053 LeaveCriticalSection(&This->share->crst);
1055 return hr;
1058 static HRESULT WINAPI DSPrimary_Unlock(IDirectSoundBuffer *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
1060 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
1061 HRESULT hr = DSERR_INVALIDCALL;
1063 TRACE("(%p)->(%p, %lu, %p, %lu)\n", iface, ptr1, len1, ptr2, len2);
1065 EnterCriticalSection(&This->share->crst);
1066 if(This->write_emu)
1067 hr = IDirectSoundBuffer_Unlock(This->write_emu, ptr1, len1, ptr2, len2);
1068 LeaveCriticalSection(&This->share->crst);
1070 return hr;
1073 static HRESULT WINAPI DSPrimary_Restore(IDirectSoundBuffer *iface)
1075 DSPrimary *This = impl_from_IDirectSoundBuffer(iface);
1076 HRESULT hr = S_OK;
1078 TRACE("(%p)->()\n", iface);
1080 EnterCriticalSection(&This->share->crst);
1081 if(This->write_emu)
1082 hr = IDirectSoundBuffer_Restore(This->write_emu);
1083 LeaveCriticalSection(&This->share->crst);
1085 return hr;
1088 static const IDirectSoundBufferVtbl DSPrimary_Vtbl =
1090 DSPrimary_QueryInterface,
1091 DSPrimary_AddRef,
1092 DSPrimary_Release,
1093 DSPrimary_GetCaps,
1094 DSPrimary_GetCurrentPosition,
1095 DSPrimary_GetFormat,
1096 DSPrimary_GetVolume,
1097 DSPrimary_GetPan,
1098 DSPrimary_GetFrequency,
1099 DSPrimary_GetStatus,
1100 DSPrimary_Initialize,
1101 DSPrimary_Lock,
1102 DSPrimary_Play,
1103 DSPrimary_SetCurrentPosition,
1104 DSPrimary_SetFormat,
1105 DSPrimary_SetVolume,
1106 DSPrimary_SetPan,
1107 DSPrimary_SetFrequency,
1108 DSPrimary_Stop,
1109 DSPrimary_Unlock,
1110 DSPrimary_Restore
1114 static void DSPrimary_SetParams(DSPrimary *This, const DS3DLISTENER *params, LONG flags)
1116 union PrimaryParamFlags dirty = { flags };
1117 DWORD i;
1119 if(dirty.bit.pos)
1120 This->current.ds3d.vPosition = params->vPosition;
1121 if(dirty.bit.vel)
1122 This->current.ds3d.vVelocity = params->vVelocity;
1123 if(dirty.bit.orientation)
1125 This->current.ds3d.vOrientFront = params->vOrientFront;
1126 This->current.ds3d.vOrientTop = params->vOrientTop;
1128 if(dirty.bit.distancefactor)
1129 This->current.ds3d.flDistanceFactor = params->flDistanceFactor;
1130 if(dirty.bit.rollofffactor)
1131 This->current.ds3d.flRolloffFactor = params->flRolloffFactor;
1132 if(dirty.bit.dopplerfactor)
1133 This->current.ds3d.flDopplerFactor = params->flDopplerFactor;
1134 /* Always copy EAX params (they're always set deferred first, then applied
1135 * when committing all params).
1137 This->current.ctx = This->deferred.ctx;
1138 for(i = 0;i < EAX_MAX_FXSLOTS;++i)
1139 This->current.fxslot[i] = This->deferred.fxslot[i];
1140 This->current.eax1_volume = This->deferred.eax1_volume;
1141 This->current.eax1_dampening = This->deferred.eax1_dampening;
1143 if(dirty.bit.pos)
1144 alListener3f(AL_POSITION, params->vPosition.x, params->vPosition.y,
1145 -params->vPosition.z);
1146 if(dirty.bit.vel)
1147 alListener3f(AL_VELOCITY, params->vVelocity.x, params->vVelocity.y,
1148 -params->vVelocity.z);
1149 if(dirty.bit.orientation)
1151 ALfloat orient[6] = {
1152 params->vOrientFront.x, params->vOrientFront.y, -params->vOrientFront.z,
1153 params->vOrientTop.x, params->vOrientTop.y, -params->vOrientTop.z
1155 alListenerfv(AL_ORIENTATION, orient);
1157 if(dirty.bit.distancefactor)
1159 alSpeedOfSound(343.3f/params->flDistanceFactor);
1160 if(HAS_EXTENSION(This->share, EXT_EFX))
1161 alListenerf(AL_METERS_PER_UNIT, params->flDistanceFactor);
1163 if(dirty.bit.rollofffactor)
1165 struct DSBufferGroup *bufgroup = This->BufferGroups;
1166 ALfloat rolloff = params->flRolloffFactor;
1168 for(i = 0;i < This->NumBufferGroups;++i)
1170 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1171 while(usemask)
1173 int idx = CTZ64(usemask);
1174 DSBuffer *buf = bufgroup[i].Buffers + idx;
1175 usemask &= ~(U64(1) << idx);
1177 if(buf->source)
1178 alSourcef(buf->source, AL_ROLLOFF_FACTOR,
1179 buf->current.eax.flRolloffFactor + rolloff);
1183 if(dirty.bit.dopplerfactor)
1184 alDopplerFactor(params->flDopplerFactor);
1186 if(dirty.bit.prim_slotid)
1188 struct DSBufferGroup *bufgroup = This->BufferGroups;
1189 ALuint slot;
1191 if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot0))
1192 This->primary_slot = This->auxslot[0];
1193 else if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot1))
1194 This->primary_slot = This->auxslot[1];
1195 else if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot2))
1196 This->primary_slot = This->auxslot[2];
1197 else if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot3))
1198 This->primary_slot = This->auxslot[3];
1199 else /*if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAX_NULL_GUID))*/
1200 This->primary_slot = 0;
1201 slot = This->primary_slot;
1203 for(i = 0;i < This->NumBufferGroups;++i)
1205 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1206 while(usemask)
1208 int idx = CTZ64(usemask);
1209 DSBuffer *buf = bufgroup[i].Buffers + idx;
1210 DSData *data = buf->buffer;
1211 usemask &= ~(U64(1) << idx);
1213 if(buf->source && (data->dsbflags&DSBCAPS_CTRL3D))
1215 if(buf->current.fxslot_targets[0] == FXSLOT_TARGET_PRIMARY)
1216 alSource3i(buf->source, AL_AUXILIARY_SEND_FILTER, slot, 0, buf->filter[1]);
1217 if(buf->current.fxslot_targets[1] == FXSLOT_TARGET_PRIMARY)
1218 alSource3i(buf->source, AL_AUXILIARY_SEND_FILTER, slot, 1, buf->filter[2]);
1223 if(dirty.bit.distancefactor2) {
1224 /* TODO: Find out what this affects. */
1226 if(dirty.bit.air_absorbhf)
1228 struct DSBufferGroup *bufgroup = This->BufferGroups;
1229 ALfloat mult = This->current.ctx.flAirAbsorptionHF / -5.0f;
1231 for(i = 0;i < This->NumBufferGroups;++i)
1233 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1234 while(usemask)
1236 int idx = CTZ64(usemask);
1237 DSBuffer *buf = bufgroup[i].Buffers + idx;
1238 DSData *data = buf->buffer;
1239 usemask &= ~(U64(1) << idx);
1241 if(buf->source && (data->dsbflags&DSBCAPS_CTRL3D))
1242 alSourcef(buf->source, AL_AIR_ABSORPTION_FACTOR,
1243 clampF(buf->current.eax.flAirAbsorptionFactor*mult, 0.0f, 10.0f)
1248 if(dirty.bit.hfreference) {
1249 /* NOTE: Not currently handlable in OpenAL. */
1252 if(dirty.bit.fxslots)
1254 for(i = 0;i < EAX_MAX_FXSLOTS;++i)
1256 ALuint slot = This->auxslot[i];
1257 if(FXSLOT_IS_DIRTY(dirty.bit, i, FXSLOT_EFFECT_BIT))
1258 alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, This->effect[i]);
1259 if(FXSLOT_IS_DIRTY(dirty.bit, i, FXSLOT_VOL_BIT))
1260 alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN,
1261 mB_to_gain(This->current.fxslot[i].props.lVolume)
1263 if(FXSLOT_IS_DIRTY(dirty.bit, i, FXSLOT_FLAGS_BIT))
1264 alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO,
1265 (This->current.fxslot[i].props.dwFlags&EAXFXSLOTFLAGS_ENVIRONMENT) ?
1266 AL_TRUE : AL_FALSE
1272 static HRESULT WINAPI DSPrimary3D_QueryInterface(IDirectSound3DListener *iface, REFIID riid, void **ppv)
1274 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1275 return DSPrimary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1278 static ULONG WINAPI DSPrimary3D_AddRef(IDirectSound3DListener *iface)
1280 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1281 LONG ret;
1283 ret = InterlockedIncrement(&This->ds3d_ref);
1284 TRACE("(%p) ref %lu\n", iface, ret);
1286 return ret;
1289 static ULONG WINAPI DSPrimary3D_Release(IDirectSound3DListener *iface)
1291 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1292 LONG ret;
1294 ret = InterlockedDecrement(&This->ds3d_ref);
1295 TRACE("(%p) ref %lu\n", iface, ret);
1297 return ret;
1301 static HRESULT WINAPI DSPrimary3D_GetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE *distancefactor)
1303 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1305 TRACE("(%p)->(%p)\n", iface, distancefactor);
1307 if(!distancefactor)
1309 WARN("Invalid parameter %p\n", distancefactor);
1310 return DSERR_INVALIDPARAM;
1313 *distancefactor = This->current.ds3d.flDistanceFactor;
1314 return S_OK;
1317 static HRESULT WINAPI DSPrimary3D_GetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE *dopplerfactor)
1319 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1321 TRACE("(%p)->(%p)\n", iface, dopplerfactor);
1323 if(!dopplerfactor)
1325 WARN("Invalid parameter %p\n", dopplerfactor);
1326 return DSERR_INVALIDPARAM;
1329 *dopplerfactor = This->current.ds3d.flDopplerFactor;
1330 return S_OK;
1333 static HRESULT WINAPI DSPrimary3D_GetOrientation(IDirectSound3DListener *iface, D3DVECTOR *front, D3DVECTOR *top)
1335 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1337 TRACE("(%p)->(%p, %p)\n", iface, front, top);
1339 if(!front || !top)
1341 WARN("Invalid parameter %p %p\n", front, top);
1342 return DSERR_INVALIDPARAM;
1345 EnterCriticalSection(&This->share->crst);
1346 *front = This->current.ds3d.vOrientFront;
1347 *top = This->current.ds3d.vOrientTop;
1348 LeaveCriticalSection(&This->share->crst);
1349 return S_OK;
1352 static HRESULT WINAPI DSPrimary3D_GetPosition(IDirectSound3DListener *iface, D3DVECTOR *pos)
1354 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1356 TRACE("(%p)->(%p)\n", iface, pos);
1358 if(!pos)
1360 WARN("Invalid parameter %p\n", pos);
1361 return DSERR_INVALIDPARAM;
1364 EnterCriticalSection(&This->share->crst);
1365 *pos = This->current.ds3d.vPosition;
1366 LeaveCriticalSection(&This->share->crst);
1367 return S_OK;
1370 static HRESULT WINAPI DSPrimary3D_GetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE *rollofffactor)
1372 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1374 TRACE("(%p)->(%p)\n", iface, rollofffactor);
1376 if(!rollofffactor)
1378 WARN("Invalid parameter %p\n", rollofffactor);
1379 return DSERR_INVALIDPARAM;
1382 *rollofffactor = This->current.ds3d.flRolloffFactor;
1383 return S_OK;
1386 static HRESULT WINAPI DSPrimary3D_GetVelocity(IDirectSound3DListener *iface, D3DVECTOR *velocity)
1388 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1390 TRACE("(%p)->(%p)\n", iface, velocity);
1392 if(!velocity)
1394 WARN("Invalid parameter %p\n", velocity);
1395 return DSERR_INVALIDPARAM;
1398 EnterCriticalSection(&This->share->crst);
1399 *velocity = This->current.ds3d.vVelocity;
1400 LeaveCriticalSection(&This->share->crst);
1401 return S_OK;
1404 static HRESULT WINAPI DSPrimary3D_GetAllParameters(IDirectSound3DListener *iface, DS3DLISTENER *listener)
1406 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1408 TRACE("(%p)->(%p)\n", iface, listener);
1410 if(!listener || listener->dwSize < sizeof(*listener))
1412 WARN("Invalid DS3DLISTENER %p %lu\n", listener, listener ? listener->dwSize : 0);
1413 return DSERR_INVALIDPARAM;
1416 EnterCriticalSection(&This->share->crst);
1417 listener->vPosition = This->current.ds3d.vPosition;
1418 listener->vVelocity = This->current.ds3d.vVelocity;
1419 listener->vOrientFront = This->current.ds3d.vOrientFront;
1420 listener->vOrientTop = This->current.ds3d.vOrientTop;
1421 listener->flDistanceFactor = This->current.ds3d.flDistanceFactor;
1422 listener->flRolloffFactor = This->current.ds3d.flRolloffFactor;
1423 listener->flDopplerFactor = This->current.ds3d.flDopplerFactor;
1424 LeaveCriticalSection(&This->share->crst);
1426 return DS_OK;
1430 static HRESULT WINAPI DSPrimary3D_SetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1432 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1434 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1436 if(factor < DS3D_MINDISTANCEFACTOR ||
1437 factor > DS3D_MAXDISTANCEFACTOR)
1439 WARN("Invalid parameter %f\n", factor);
1440 return DSERR_INVALIDPARAM;
1443 EnterCriticalSection(&This->share->crst);
1444 if(apply == DS3D_DEFERRED)
1446 This->deferred.ds3d.flDistanceFactor = factor;
1447 This->dirty.bit.distancefactor = 1;
1449 else
1451 setALContext(This->ctx);
1452 This->current.ds3d.flDistanceFactor = factor;
1453 alSpeedOfSound(343.3f/factor);
1454 if(HAS_EXTENSION(This->share, EXT_EFX))
1455 alListenerf(AL_METERS_PER_UNIT, factor);
1456 checkALError();
1457 popALContext();
1459 LeaveCriticalSection(&This->share->crst);
1461 return S_OK;
1464 static HRESULT WINAPI DSPrimary3D_SetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1466 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1468 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1470 if(factor < DS3D_MINDOPPLERFACTOR ||
1471 factor > DS3D_MAXDOPPLERFACTOR)
1473 WARN("Invalid parameter %f\n", factor);
1474 return DSERR_INVALIDPARAM;
1477 EnterCriticalSection(&This->share->crst);
1478 if(apply == DS3D_DEFERRED)
1480 This->deferred.ds3d.flDopplerFactor = factor;
1481 This->dirty.bit.dopplerfactor = 1;
1483 else
1485 setALContext(This->ctx);
1486 This->current.ds3d.flDopplerFactor = factor;
1487 alDopplerFactor(factor);
1488 checkALError();
1489 popALContext();
1491 LeaveCriticalSection(&This->share->crst);
1493 return S_OK;
1496 static HRESULT WINAPI DSPrimary3D_SetOrientation(IDirectSound3DListener *iface, D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront, D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD apply)
1498 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1500 TRACE("(%p)->(%f, %f, %f, %f, %f, %f, %lu)\n", iface, xFront, yFront, zFront, xTop, yTop, zTop, apply);
1502 EnterCriticalSection(&This->share->crst);
1503 if(apply == DS3D_DEFERRED)
1505 This->deferred.ds3d.vOrientFront.x = xFront;
1506 This->deferred.ds3d.vOrientFront.y = yFront;
1507 This->deferred.ds3d.vOrientFront.z = zFront;
1508 This->deferred.ds3d.vOrientTop.x = xTop;
1509 This->deferred.ds3d.vOrientTop.y = yTop;
1510 This->deferred.ds3d.vOrientTop.z = zTop;
1511 This->dirty.bit.orientation = 1;
1513 else
1515 ALfloat orient[6] = {
1516 xFront, yFront, -zFront,
1517 xTop, yTop, -zTop
1519 This->current.ds3d.vOrientFront.x = xFront;
1520 This->current.ds3d.vOrientFront.y = yFront;
1521 This->current.ds3d.vOrientFront.z = zFront;
1522 This->current.ds3d.vOrientTop.x = xTop;
1523 This->current.ds3d.vOrientTop.y = yTop;
1524 This->current.ds3d.vOrientTop.z = zTop;
1526 setALContext(This->ctx);
1527 alListenerfv(AL_ORIENTATION, orient);
1528 checkALError();
1529 popALContext();
1531 LeaveCriticalSection(&This->share->crst);
1533 return S_OK;
1536 static HRESULT WINAPI DSPrimary3D_SetPosition(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1538 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1540 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1542 EnterCriticalSection(&This->share->crst);
1543 if(apply == DS3D_DEFERRED)
1545 This->deferred.ds3d.vPosition.x = x;
1546 This->deferred.ds3d.vPosition.y = y;
1547 This->deferred.ds3d.vPosition.z = z;
1548 This->dirty.bit.pos = 1;
1550 else
1552 setALContext(This->ctx);
1553 This->current.ds3d.vPosition.x = x;
1554 This->current.ds3d.vPosition.y = y;
1555 This->current.ds3d.vPosition.z = z;
1556 alListener3f(AL_POSITION, x, y, -z);
1557 checkALError();
1558 popALContext();
1560 LeaveCriticalSection(&This->share->crst);
1562 return S_OK;
1565 static HRESULT WINAPI DSPrimary3D_SetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1567 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1569 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1571 if(factor < DS3D_MINROLLOFFFACTOR ||
1572 factor > DS3D_MAXROLLOFFFACTOR)
1574 WARN("Invalid parameter %f\n", factor);
1575 return DSERR_INVALIDPARAM;
1578 EnterCriticalSection(&This->share->crst);
1579 if(apply == DS3D_DEFERRED)
1581 This->deferred.ds3d.flRolloffFactor = factor;
1582 This->dirty.bit.rollofffactor = 1;
1584 else
1586 struct DSBufferGroup *bufgroup = This->BufferGroups;
1587 DWORD i;
1589 This->current.ds3d.flRolloffFactor = factor;
1591 setALContext(This->ctx);
1592 for(i = 0;i < This->NumBufferGroups;++i)
1594 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1595 while(usemask)
1597 int idx = CTZ64(usemask);
1598 DSBuffer *buf = bufgroup[i].Buffers + idx;
1599 usemask &= ~(U64(1) << idx);
1601 if(buf->source)
1602 alSourcef(buf->source, AL_ROLLOFF_FACTOR,
1603 buf->current.eax.flRolloffFactor + factor);
1606 checkALError();
1607 popALContext();
1609 LeaveCriticalSection(&This->share->crst);
1611 return S_OK;
1614 static HRESULT WINAPI DSPrimary3D_SetVelocity(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1616 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1618 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1620 EnterCriticalSection(&This->share->crst);
1621 if(apply == DS3D_DEFERRED)
1623 This->deferred.ds3d.vVelocity.x = x;
1624 This->deferred.ds3d.vVelocity.y = y;
1625 This->deferred.ds3d.vVelocity.z = z;
1626 This->dirty.bit.vel = 1;
1628 else
1630 setALContext(This->ctx);
1631 This->current.ds3d.vVelocity.x = x;
1632 This->current.ds3d.vVelocity.y = y;
1633 This->current.ds3d.vVelocity.z = z;
1634 alListener3f(AL_VELOCITY, x, y, -z);
1635 checkALError();
1636 popALContext();
1638 LeaveCriticalSection(&This->share->crst);
1640 return S_OK;
1643 static HRESULT WINAPI DSPrimary3D_SetAllParameters(IDirectSound3DListener *iface, const DS3DLISTENER *listen, DWORD apply)
1645 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1647 TRACE("(%p)->(%p, %lu)\n", iface, listen, apply);
1649 if(!listen || listen->dwSize < sizeof(*listen))
1651 WARN("Invalid parameter %p %lu\n", listen, listen ? listen->dwSize : 0);
1652 return DSERR_INVALIDPARAM;
1655 if(listen->flDistanceFactor > DS3D_MAXDISTANCEFACTOR ||
1656 listen->flDistanceFactor < DS3D_MINDISTANCEFACTOR)
1658 WARN("Invalid distance factor (%f)\n", listen->flDistanceFactor);
1659 return DSERR_INVALIDPARAM;
1662 if(listen->flDopplerFactor > DS3D_MAXDOPPLERFACTOR ||
1663 listen->flDopplerFactor < DS3D_MINDOPPLERFACTOR)
1665 WARN("Invalid doppler factor (%f)\n", listen->flDopplerFactor);
1666 return DSERR_INVALIDPARAM;
1669 if(listen->flRolloffFactor < DS3D_MINROLLOFFFACTOR ||
1670 listen->flRolloffFactor > DS3D_MAXROLLOFFFACTOR)
1672 WARN("Invalid rolloff factor (%f)\n", listen->flRolloffFactor);
1673 return DSERR_INVALIDPARAM;
1676 if(apply == DS3D_DEFERRED)
1678 EnterCriticalSection(&This->share->crst);
1679 This->deferred.ds3d = *listen;
1680 This->deferred.ds3d.dwSize = sizeof(This->deferred.ds3d);
1681 This->dirty.bit.pos = 1;
1682 This->dirty.bit.vel = 1;
1683 This->dirty.bit.orientation = 1;
1684 This->dirty.bit.distancefactor = 1;
1685 This->dirty.bit.rollofffactor = 1;
1686 This->dirty.bit.dopplerfactor = 1;
1687 LeaveCriticalSection(&This->share->crst);
1689 else
1691 union PrimaryParamFlags dirty = { 0l };
1692 dirty.bit.pos = 1;
1693 dirty.bit.vel = 1;
1694 dirty.bit.orientation = 1;
1695 dirty.bit.distancefactor = 1;
1696 dirty.bit.rollofffactor = 1;
1697 dirty.bit.dopplerfactor = 1;
1699 EnterCriticalSection(&This->share->crst);
1700 setALContext(This->ctx);
1701 DSPrimary_SetParams(This, listen, dirty.flags);
1702 checkALError();
1703 popALContext();
1704 LeaveCriticalSection(&This->share->crst);
1707 return S_OK;
1710 HRESULT WINAPI DSPrimary3D_CommitDeferredSettings(IDirectSound3DListener *iface)
1712 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1713 struct DSBufferGroup *bufgroup;
1714 LONG flags;
1715 DWORD i;
1717 EnterCriticalSection(&This->share->crst);
1718 setALContext(This->ctx);
1719 alDeferUpdatesSOFT();
1721 if((flags=InterlockedExchange(&This->dirty.flags, 0)) != 0)
1723 DSPrimary_SetParams(This, &This->deferred.ds3d, flags);
1724 /* checkALError is here for debugging */
1725 checkALError();
1727 TRACE("Dirty flags was: 0x%02lx\n", flags);
1729 bufgroup = This->BufferGroups;
1730 for(i = 0;i < This->NumBufferGroups;++i)
1732 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1733 while(usemask)
1735 int idx = CTZ64(usemask);
1736 DSBuffer *buf = bufgroup[i].Buffers + idx;
1737 usemask &= ~(U64(1) << idx);
1739 if((flags=InterlockedExchange(&buf->dirty.flags, 0)) != 0)
1740 DSBuffer_SetParams(buf, &buf->deferred.ds3d, flags);
1743 alProcessUpdatesSOFT();
1744 checkALError();
1746 popALContext();
1747 LeaveCriticalSection(&This->share->crst);
1749 return DS_OK;
1752 static const IDirectSound3DListenerVtbl DSPrimary3D_Vtbl =
1754 DSPrimary3D_QueryInterface,
1755 DSPrimary3D_AddRef,
1756 DSPrimary3D_Release,
1757 DSPrimary3D_GetAllParameters,
1758 DSPrimary3D_GetDistanceFactor,
1759 DSPrimary3D_GetDopplerFactor,
1760 DSPrimary3D_GetOrientation,
1761 DSPrimary3D_GetPosition,
1762 DSPrimary3D_GetRolloffFactor,
1763 DSPrimary3D_GetVelocity,
1764 DSPrimary3D_SetAllParameters,
1765 DSPrimary3D_SetDistanceFactor,
1766 DSPrimary3D_SetDopplerFactor,
1767 DSPrimary3D_SetOrientation,
1768 DSPrimary3D_SetPosition,
1769 DSPrimary3D_SetRolloffFactor,
1770 DSPrimary3D_SetVelocity,
1771 DSPrimary3D_CommitDeferredSettings
1775 static HRESULT WINAPI DSPrimaryProp_QueryInterface(IKsPropertySet *iface, REFIID riid, void **ppv)
1777 DSPrimary *This = impl_from_IKsPropertySet(iface);
1778 return DSPrimary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1781 static ULONG WINAPI DSPrimaryProp_AddRef(IKsPropertySet *iface)
1783 DSPrimary *This = impl_from_IKsPropertySet(iface);
1784 LONG ret;
1786 ret = InterlockedIncrement(&This->prop_ref);
1787 TRACE("(%p) ref %lu\n", iface, ret);
1789 return ret;
1792 static ULONG WINAPI DSPrimaryProp_Release(IKsPropertySet *iface)
1794 DSPrimary *This = impl_from_IKsPropertySet(iface);
1795 LONG ret;
1797 ret = InterlockedDecrement(&This->prop_ref);
1798 TRACE("(%p) ref %lu\n", iface, ret);
1800 return ret;
1803 static HRESULT WINAPI DSPrimaryProp_Get(IKsPropertySet *iface,
1804 REFGUID guidPropSet, ULONG dwPropID,
1805 LPVOID pInstanceData, ULONG cbInstanceData,
1806 LPVOID pPropData, ULONG cbPropData,
1807 ULONG *pcbReturned)
1809 (void)iface;
1810 (void)dwPropID;
1811 (void)pInstanceData;
1812 (void)cbInstanceData;
1813 (void)pPropData;
1814 (void)cbPropData;
1815 (void)pcbReturned;
1817 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1819 return E_PROP_ID_UNSUPPORTED;
1822 static HRESULT WINAPI DSPrimaryProp_Set(IKsPropertySet *iface,
1823 REFGUID guidPropSet, ULONG dwPropID,
1824 LPVOID pInstanceData, ULONG cbInstanceData,
1825 LPVOID pPropData, ULONG cbPropData)
1827 (void)iface;
1828 (void)dwPropID;
1829 (void)pInstanceData;
1830 (void)cbInstanceData;
1831 (void)pPropData;
1832 (void)cbPropData;
1834 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1836 return E_PROP_ID_UNSUPPORTED;
1839 static HRESULT WINAPI DSPrimaryProp_QuerySupport(IKsPropertySet *iface,
1840 REFGUID guidPropSet, ULONG dwPropID,
1841 ULONG *pTypeSupport)
1843 (void)iface;
1844 (void)pTypeSupport;
1846 FIXME("Unhandled propset: %s (propid: %lu)\n", debugstr_guid(guidPropSet), dwPropID);
1848 return E_PROP_ID_UNSUPPORTED;
1851 static const IKsPropertySetVtbl DSPrimaryProp_Vtbl =
1853 DSPrimaryProp_QueryInterface,
1854 DSPrimaryProp_AddRef,
1855 DSPrimaryProp_Release,
1856 DSPrimaryProp_Get,
1857 DSPrimaryProp_Set,
1858 DSPrimaryProp_QuerySupport