Merge pull request #2 from Mechami/master
[dsound-openal.git] / primary.c
blobc78e56fcdacc412c54755d3124d192eedae6abe3
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_idx = 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)
1158 alSpeedOfSound(343.3f/params->flDistanceFactor);
1159 if(dirty.bit.rollofffactor)
1161 struct DSBufferGroup *bufgroup = This->BufferGroups;
1162 ALfloat rolloff = params->flRolloffFactor;
1164 for(i = 0;i < This->NumBufferGroups;++i)
1166 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1167 while(usemask)
1169 int idx = CTZ64(usemask);
1170 DSBuffer *buf = bufgroup[i].Buffers + idx;
1171 usemask &= ~(U64(1) << idx);
1173 if(buf->source)
1174 alSourcef(buf->source, AL_ROLLOFF_FACTOR,
1175 buf->current.eax.flRolloffFactor + rolloff);
1179 if(dirty.bit.dopplerfactor)
1180 alDopplerFactor(params->flDopplerFactor);
1182 if(dirty.bit.prim_slotid)
1184 struct DSBufferGroup *bufgroup = This->BufferGroups;
1185 ALuint slot;
1187 if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot0))
1188 This->primary_idx = 0;
1189 else if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot1))
1190 This->primary_idx = 1;
1191 else if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot2))
1192 This->primary_idx = 2;
1193 else if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAXPROPERTYID_EAX40_FXSlot3))
1194 This->primary_idx = 3;
1195 else /*if(IsEqualGUID(&This->current.ctx.guidPrimaryFXSlotID, &EAX_NULL_GUID))*/
1196 This->primary_idx = -1;
1197 slot = (This->primary_idx < 0) ? 0 : This->auxslot[This->primary_idx];
1199 for(i = 0;i < This->NumBufferGroups;++i)
1201 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1202 while(usemask)
1204 int idx = CTZ64(usemask);
1205 DSBuffer *buf = bufgroup[i].Buffers + idx;
1206 DSData *data = buf->buffer;
1207 usemask &= ~(U64(1) << idx);
1209 if(buf->source && (data->dsbflags&DSBCAPS_CTRL3D))
1211 ALuint filter = (This->primary_idx < 0) ? 0 : buf->filter[1+This->primary_idx];
1212 if(buf->current.fxslot_targets[0] == FXSLOT_TARGET_PRIMARY)
1213 alSource3i(buf->source, AL_AUXILIARY_SEND_FILTER, slot, 0, filter);
1214 if(buf->current.fxslot_targets[1] == FXSLOT_TARGET_PRIMARY)
1215 alSource3i(buf->source, AL_AUXILIARY_SEND_FILTER, slot, 1, filter);
1220 if(dirty.bit.distancefactor2)
1221 alListenerf(AL_METERS_PER_UNIT, This->current.ctx.flDistanceFactor);
1222 if(dirty.bit.air_absorbhf)
1224 struct DSBufferGroup *bufgroup = This->BufferGroups;
1225 ALfloat mult = This->current.ctx.flAirAbsorptionHF / -5.0f;
1227 for(i = 0;i < This->NumBufferGroups;++i)
1229 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1230 while(usemask)
1232 int idx = CTZ64(usemask);
1233 DSBuffer *buf = bufgroup[i].Buffers + idx;
1234 DSData *data = buf->buffer;
1235 usemask &= ~(U64(1) << idx);
1237 if(buf->source && (data->dsbflags&DSBCAPS_CTRL3D))
1238 alSourcef(buf->source, AL_AIR_ABSORPTION_FACTOR,
1239 clampF(buf->current.eax.flAirAbsorptionFactor*mult, 0.0f, 10.0f)
1244 if(dirty.bit.hfreference) {
1245 /* NOTE: Not currently handlable in OpenAL. */
1248 if(dirty.bit.fxslots)
1250 for(i = 0;i < EAX_MAX_FXSLOTS;++i)
1252 ALuint slot = This->auxslot[i];
1253 if(FXSLOT_IS_DIRTY(dirty.bit, i, FXSLOT_EFFECT_BIT))
1254 alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_EFFECT, This->effect[i]);
1255 if(FXSLOT_IS_DIRTY(dirty.bit, i, FXSLOT_VOL_BIT))
1256 alAuxiliaryEffectSlotf(slot, AL_EFFECTSLOT_GAIN,
1257 mB_to_gain(This->current.fxslot[i].props.lVolume)
1259 if(FXSLOT_IS_DIRTY(dirty.bit, i, FXSLOT_FLAGS_BIT))
1260 alAuxiliaryEffectSloti(slot, AL_EFFECTSLOT_AUXILIARY_SEND_AUTO,
1261 (This->current.fxslot[i].props.dwFlags&EAXFXSLOTFLAGS_ENVIRONMENT) ?
1262 AL_TRUE : AL_FALSE
1268 static HRESULT WINAPI DSPrimary3D_QueryInterface(IDirectSound3DListener *iface, REFIID riid, void **ppv)
1270 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1271 return DSPrimary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1274 static ULONG WINAPI DSPrimary3D_AddRef(IDirectSound3DListener *iface)
1276 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1277 LONG ret;
1279 ret = InterlockedIncrement(&This->ds3d_ref);
1280 TRACE("(%p) ref %lu\n", iface, ret);
1282 return ret;
1285 static ULONG WINAPI DSPrimary3D_Release(IDirectSound3DListener *iface)
1287 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1288 LONG ret;
1290 ret = InterlockedDecrement(&This->ds3d_ref);
1291 TRACE("(%p) ref %lu\n", iface, ret);
1293 return ret;
1297 static HRESULT WINAPI DSPrimary3D_GetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE *distancefactor)
1299 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1301 TRACE("(%p)->(%p)\n", iface, distancefactor);
1303 if(!distancefactor)
1305 WARN("Invalid parameter %p\n", distancefactor);
1306 return DSERR_INVALIDPARAM;
1309 *distancefactor = This->current.ds3d.flDistanceFactor;
1310 return S_OK;
1313 static HRESULT WINAPI DSPrimary3D_GetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE *dopplerfactor)
1315 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1317 TRACE("(%p)->(%p)\n", iface, dopplerfactor);
1319 if(!dopplerfactor)
1321 WARN("Invalid parameter %p\n", dopplerfactor);
1322 return DSERR_INVALIDPARAM;
1325 *dopplerfactor = This->current.ds3d.flDopplerFactor;
1326 return S_OK;
1329 static HRESULT WINAPI DSPrimary3D_GetOrientation(IDirectSound3DListener *iface, D3DVECTOR *front, D3DVECTOR *top)
1331 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1333 TRACE("(%p)->(%p, %p)\n", iface, front, top);
1335 if(!front || !top)
1337 WARN("Invalid parameter %p %p\n", front, top);
1338 return DSERR_INVALIDPARAM;
1341 EnterCriticalSection(&This->share->crst);
1342 *front = This->current.ds3d.vOrientFront;
1343 *top = This->current.ds3d.vOrientTop;
1344 LeaveCriticalSection(&This->share->crst);
1345 return S_OK;
1348 static HRESULT WINAPI DSPrimary3D_GetPosition(IDirectSound3DListener *iface, D3DVECTOR *pos)
1350 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1352 TRACE("(%p)->(%p)\n", iface, pos);
1354 if(!pos)
1356 WARN("Invalid parameter %p\n", pos);
1357 return DSERR_INVALIDPARAM;
1360 EnterCriticalSection(&This->share->crst);
1361 *pos = This->current.ds3d.vPosition;
1362 LeaveCriticalSection(&This->share->crst);
1363 return S_OK;
1366 static HRESULT WINAPI DSPrimary3D_GetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE *rollofffactor)
1368 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1370 TRACE("(%p)->(%p)\n", iface, rollofffactor);
1372 if(!rollofffactor)
1374 WARN("Invalid parameter %p\n", rollofffactor);
1375 return DSERR_INVALIDPARAM;
1378 *rollofffactor = This->current.ds3d.flRolloffFactor;
1379 return S_OK;
1382 static HRESULT WINAPI DSPrimary3D_GetVelocity(IDirectSound3DListener *iface, D3DVECTOR *velocity)
1384 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1386 TRACE("(%p)->(%p)\n", iface, velocity);
1388 if(!velocity)
1390 WARN("Invalid parameter %p\n", velocity);
1391 return DSERR_INVALIDPARAM;
1394 EnterCriticalSection(&This->share->crst);
1395 *velocity = This->current.ds3d.vVelocity;
1396 LeaveCriticalSection(&This->share->crst);
1397 return S_OK;
1400 static HRESULT WINAPI DSPrimary3D_GetAllParameters(IDirectSound3DListener *iface, DS3DLISTENER *listener)
1402 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1404 TRACE("(%p)->(%p)\n", iface, listener);
1406 if(!listener || listener->dwSize < sizeof(*listener))
1408 WARN("Invalid DS3DLISTENER %p %lu\n", listener, listener ? listener->dwSize : 0);
1409 return DSERR_INVALIDPARAM;
1412 EnterCriticalSection(&This->share->crst);
1413 listener->vPosition = This->current.ds3d.vPosition;
1414 listener->vVelocity = This->current.ds3d.vVelocity;
1415 listener->vOrientFront = This->current.ds3d.vOrientFront;
1416 listener->vOrientTop = This->current.ds3d.vOrientTop;
1417 listener->flDistanceFactor = This->current.ds3d.flDistanceFactor;
1418 listener->flRolloffFactor = This->current.ds3d.flRolloffFactor;
1419 listener->flDopplerFactor = This->current.ds3d.flDopplerFactor;
1420 LeaveCriticalSection(&This->share->crst);
1422 return DS_OK;
1426 static HRESULT WINAPI DSPrimary3D_SetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1428 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1430 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1432 if(factor < DS3D_MINDISTANCEFACTOR ||
1433 factor > DS3D_MAXDISTANCEFACTOR)
1435 WARN("Invalid parameter %f\n", factor);
1436 return DSERR_INVALIDPARAM;
1439 EnterCriticalSection(&This->share->crst);
1440 if(apply == DS3D_DEFERRED)
1442 This->deferred.ds3d.flDistanceFactor = factor;
1443 This->dirty.bit.distancefactor = 1;
1445 else
1447 setALContext(This->ctx);
1448 This->current.ds3d.flDistanceFactor = factor;
1449 alSpeedOfSound(343.3f/factor);
1450 checkALError();
1451 popALContext();
1453 LeaveCriticalSection(&This->share->crst);
1455 return S_OK;
1458 static HRESULT WINAPI DSPrimary3D_SetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1460 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1462 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1464 if(factor < DS3D_MINDOPPLERFACTOR ||
1465 factor > DS3D_MAXDOPPLERFACTOR)
1467 WARN("Invalid parameter %f\n", factor);
1468 return DSERR_INVALIDPARAM;
1471 EnterCriticalSection(&This->share->crst);
1472 if(apply == DS3D_DEFERRED)
1474 This->deferred.ds3d.flDopplerFactor = factor;
1475 This->dirty.bit.dopplerfactor = 1;
1477 else
1479 setALContext(This->ctx);
1480 This->current.ds3d.flDopplerFactor = factor;
1481 alDopplerFactor(factor);
1482 checkALError();
1483 popALContext();
1485 LeaveCriticalSection(&This->share->crst);
1487 return S_OK;
1490 static HRESULT WINAPI DSPrimary3D_SetOrientation(IDirectSound3DListener *iface, D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront, D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD apply)
1492 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1494 TRACE("(%p)->(%f, %f, %f, %f, %f, %f, %lu)\n", iface, xFront, yFront, zFront, xTop, yTop, zTop, apply);
1496 EnterCriticalSection(&This->share->crst);
1497 if(apply == DS3D_DEFERRED)
1499 This->deferred.ds3d.vOrientFront.x = xFront;
1500 This->deferred.ds3d.vOrientFront.y = yFront;
1501 This->deferred.ds3d.vOrientFront.z = zFront;
1502 This->deferred.ds3d.vOrientTop.x = xTop;
1503 This->deferred.ds3d.vOrientTop.y = yTop;
1504 This->deferred.ds3d.vOrientTop.z = zTop;
1505 This->dirty.bit.orientation = 1;
1507 else
1509 ALfloat orient[6] = {
1510 xFront, yFront, -zFront,
1511 xTop, yTop, -zTop
1513 This->current.ds3d.vOrientFront.x = xFront;
1514 This->current.ds3d.vOrientFront.y = yFront;
1515 This->current.ds3d.vOrientFront.z = zFront;
1516 This->current.ds3d.vOrientTop.x = xTop;
1517 This->current.ds3d.vOrientTop.y = yTop;
1518 This->current.ds3d.vOrientTop.z = zTop;
1520 setALContext(This->ctx);
1521 alListenerfv(AL_ORIENTATION, orient);
1522 checkALError();
1523 popALContext();
1525 LeaveCriticalSection(&This->share->crst);
1527 return S_OK;
1530 static HRESULT WINAPI DSPrimary3D_SetPosition(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1532 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1534 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1536 EnterCriticalSection(&This->share->crst);
1537 if(apply == DS3D_DEFERRED)
1539 This->deferred.ds3d.vPosition.x = x;
1540 This->deferred.ds3d.vPosition.y = y;
1541 This->deferred.ds3d.vPosition.z = z;
1542 This->dirty.bit.pos = 1;
1544 else
1546 setALContext(This->ctx);
1547 This->current.ds3d.vPosition.x = x;
1548 This->current.ds3d.vPosition.y = y;
1549 This->current.ds3d.vPosition.z = z;
1550 alListener3f(AL_POSITION, x, y, -z);
1551 checkALError();
1552 popALContext();
1554 LeaveCriticalSection(&This->share->crst);
1556 return S_OK;
1559 static HRESULT WINAPI DSPrimary3D_SetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1561 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1563 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1565 if(factor < DS3D_MINROLLOFFFACTOR ||
1566 factor > DS3D_MAXROLLOFFFACTOR)
1568 WARN("Invalid parameter %f\n", factor);
1569 return DSERR_INVALIDPARAM;
1572 EnterCriticalSection(&This->share->crst);
1573 if(apply == DS3D_DEFERRED)
1575 This->deferred.ds3d.flRolloffFactor = factor;
1576 This->dirty.bit.rollofffactor = 1;
1578 else
1580 struct DSBufferGroup *bufgroup = This->BufferGroups;
1581 DWORD i;
1583 This->current.ds3d.flRolloffFactor = factor;
1585 setALContext(This->ctx);
1586 for(i = 0;i < This->NumBufferGroups;++i)
1588 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1589 while(usemask)
1591 int idx = CTZ64(usemask);
1592 DSBuffer *buf = bufgroup[i].Buffers + idx;
1593 usemask &= ~(U64(1) << idx);
1595 if(buf->source)
1596 alSourcef(buf->source, AL_ROLLOFF_FACTOR,
1597 buf->current.eax.flRolloffFactor + factor);
1600 checkALError();
1601 popALContext();
1603 LeaveCriticalSection(&This->share->crst);
1605 return S_OK;
1608 static HRESULT WINAPI DSPrimary3D_SetVelocity(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1610 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1612 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1614 EnterCriticalSection(&This->share->crst);
1615 if(apply == DS3D_DEFERRED)
1617 This->deferred.ds3d.vVelocity.x = x;
1618 This->deferred.ds3d.vVelocity.y = y;
1619 This->deferred.ds3d.vVelocity.z = z;
1620 This->dirty.bit.vel = 1;
1622 else
1624 setALContext(This->ctx);
1625 This->current.ds3d.vVelocity.x = x;
1626 This->current.ds3d.vVelocity.y = y;
1627 This->current.ds3d.vVelocity.z = z;
1628 alListener3f(AL_VELOCITY, x, y, -z);
1629 checkALError();
1630 popALContext();
1632 LeaveCriticalSection(&This->share->crst);
1634 return S_OK;
1637 static HRESULT WINAPI DSPrimary3D_SetAllParameters(IDirectSound3DListener *iface, const DS3DLISTENER *listen, DWORD apply)
1639 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1641 TRACE("(%p)->(%p, %lu)\n", iface, listen, apply);
1643 if(!listen || listen->dwSize < sizeof(*listen))
1645 WARN("Invalid parameter %p %lu\n", listen, listen ? listen->dwSize : 0);
1646 return DSERR_INVALIDPARAM;
1649 if(listen->flDistanceFactor > DS3D_MAXDISTANCEFACTOR ||
1650 listen->flDistanceFactor < DS3D_MINDISTANCEFACTOR)
1652 WARN("Invalid distance factor (%f)\n", listen->flDistanceFactor);
1653 return DSERR_INVALIDPARAM;
1656 if(listen->flDopplerFactor > DS3D_MAXDOPPLERFACTOR ||
1657 listen->flDopplerFactor < DS3D_MINDOPPLERFACTOR)
1659 WARN("Invalid doppler factor (%f)\n", listen->flDopplerFactor);
1660 return DSERR_INVALIDPARAM;
1663 if(listen->flRolloffFactor < DS3D_MINROLLOFFFACTOR ||
1664 listen->flRolloffFactor > DS3D_MAXROLLOFFFACTOR)
1666 WARN("Invalid rolloff factor (%f)\n", listen->flRolloffFactor);
1667 return DSERR_INVALIDPARAM;
1670 if(apply == DS3D_DEFERRED)
1672 EnterCriticalSection(&This->share->crst);
1673 This->deferred.ds3d = *listen;
1674 This->deferred.ds3d.dwSize = sizeof(This->deferred.ds3d);
1675 This->dirty.bit.pos = 1;
1676 This->dirty.bit.vel = 1;
1677 This->dirty.bit.orientation = 1;
1678 This->dirty.bit.distancefactor = 1;
1679 This->dirty.bit.rollofffactor = 1;
1680 This->dirty.bit.dopplerfactor = 1;
1681 LeaveCriticalSection(&This->share->crst);
1683 else
1685 union PrimaryParamFlags dirty = { 0l };
1686 dirty.bit.pos = 1;
1687 dirty.bit.vel = 1;
1688 dirty.bit.orientation = 1;
1689 dirty.bit.distancefactor = 1;
1690 dirty.bit.rollofffactor = 1;
1691 dirty.bit.dopplerfactor = 1;
1693 EnterCriticalSection(&This->share->crst);
1694 setALContext(This->ctx);
1695 DSPrimary_SetParams(This, listen, dirty.flags);
1696 checkALError();
1697 popALContext();
1698 LeaveCriticalSection(&This->share->crst);
1701 return S_OK;
1704 HRESULT WINAPI DSPrimary3D_CommitDeferredSettings(IDirectSound3DListener *iface)
1706 DSPrimary *This = impl_from_IDirectSound3DListener(iface);
1707 struct DSBufferGroup *bufgroup;
1708 LONG flags;
1709 DWORD i;
1711 EnterCriticalSection(&This->share->crst);
1712 setALContext(This->ctx);
1713 alDeferUpdatesSOFT();
1715 if((flags=InterlockedExchange(&This->dirty.flags, 0)) != 0)
1717 DSPrimary_SetParams(This, &This->deferred.ds3d, flags);
1718 /* checkALError is here for debugging */
1719 checkALError();
1721 TRACE("Dirty flags was: 0x%02lx\n", flags);
1723 bufgroup = This->BufferGroups;
1724 for(i = 0;i < This->NumBufferGroups;++i)
1726 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1727 while(usemask)
1729 int idx = CTZ64(usemask);
1730 DSBuffer *buf = bufgroup[i].Buffers + idx;
1731 usemask &= ~(U64(1) << idx);
1733 if((flags=InterlockedExchange(&buf->dirty.flags, 0)) != 0)
1734 DSBuffer_SetParams(buf, &buf->deferred.ds3d, flags);
1737 alProcessUpdatesSOFT();
1738 checkALError();
1740 popALContext();
1741 LeaveCriticalSection(&This->share->crst);
1743 return DS_OK;
1746 static const IDirectSound3DListenerVtbl DSPrimary3D_Vtbl =
1748 DSPrimary3D_QueryInterface,
1749 DSPrimary3D_AddRef,
1750 DSPrimary3D_Release,
1751 DSPrimary3D_GetAllParameters,
1752 DSPrimary3D_GetDistanceFactor,
1753 DSPrimary3D_GetDopplerFactor,
1754 DSPrimary3D_GetOrientation,
1755 DSPrimary3D_GetPosition,
1756 DSPrimary3D_GetRolloffFactor,
1757 DSPrimary3D_GetVelocity,
1758 DSPrimary3D_SetAllParameters,
1759 DSPrimary3D_SetDistanceFactor,
1760 DSPrimary3D_SetDopplerFactor,
1761 DSPrimary3D_SetOrientation,
1762 DSPrimary3D_SetPosition,
1763 DSPrimary3D_SetRolloffFactor,
1764 DSPrimary3D_SetVelocity,
1765 DSPrimary3D_CommitDeferredSettings
1769 static HRESULT WINAPI DSPrimaryProp_QueryInterface(IKsPropertySet *iface, REFIID riid, void **ppv)
1771 DSPrimary *This = impl_from_IKsPropertySet(iface);
1772 return DSPrimary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1775 static ULONG WINAPI DSPrimaryProp_AddRef(IKsPropertySet *iface)
1777 DSPrimary *This = impl_from_IKsPropertySet(iface);
1778 LONG ret;
1780 ret = InterlockedIncrement(&This->prop_ref);
1781 TRACE("(%p) ref %lu\n", iface, ret);
1783 return ret;
1786 static ULONG WINAPI DSPrimaryProp_Release(IKsPropertySet *iface)
1788 DSPrimary *This = impl_from_IKsPropertySet(iface);
1789 LONG ret;
1791 ret = InterlockedDecrement(&This->prop_ref);
1792 TRACE("(%p) ref %lu\n", iface, ret);
1794 return ret;
1797 static HRESULT WINAPI DSPrimaryProp_Get(IKsPropertySet *iface,
1798 REFGUID guidPropSet, ULONG dwPropID,
1799 LPVOID pInstanceData, ULONG cbInstanceData,
1800 LPVOID pPropData, ULONG cbPropData,
1801 ULONG *pcbReturned)
1803 (void)iface;
1804 (void)dwPropID;
1805 (void)pInstanceData;
1806 (void)cbInstanceData;
1807 (void)pPropData;
1808 (void)cbPropData;
1809 (void)pcbReturned;
1811 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1813 return E_PROP_ID_UNSUPPORTED;
1816 static HRESULT WINAPI DSPrimaryProp_Set(IKsPropertySet *iface,
1817 REFGUID guidPropSet, ULONG dwPropID,
1818 LPVOID pInstanceData, ULONG cbInstanceData,
1819 LPVOID pPropData, ULONG cbPropData)
1821 (void)iface;
1822 (void)dwPropID;
1823 (void)pInstanceData;
1824 (void)cbInstanceData;
1825 (void)pPropData;
1826 (void)cbPropData;
1828 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1830 return E_PROP_ID_UNSUPPORTED;
1833 static HRESULT WINAPI DSPrimaryProp_QuerySupport(IKsPropertySet *iface,
1834 REFGUID guidPropSet, ULONG dwPropID,
1835 ULONG *pTypeSupport)
1837 (void)iface;
1838 (void)pTypeSupport;
1840 FIXME("Unhandled propset: %s (propid: %lu)\n", debugstr_guid(guidPropSet), dwPropID);
1842 return E_PROP_ID_UNSUPPORTED;
1845 static const IKsPropertySetVtbl DSPrimaryProp_Vtbl =
1847 DSPrimaryProp_QueryInterface,
1848 DSPrimaryProp_AddRef,
1849 DSPrimaryProp_Release,
1850 DSPrimaryProp_Get,
1851 DSPrimaryProp_Set,
1852 DSPrimaryProp_QuerySupport