Attempt to improve handling of EAX1 volume settings
[dsound-openal.git] / primary.c
blob016f1c75ce36c60cd58a7feeb28d0099b38c32f3
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"
34 #ifndef E_PROP_ID_UNSUPPORTED
35 #define E_PROP_ID_UNSUPPORTED ((HRESULT)0x80070490)
36 #endif
39 static const IDirectSoundBufferVtbl DS8Primary_Vtbl;
40 static const IDirectSound3DListenerVtbl DS8Primary3D_Vtbl;
41 static const IKsPropertySetVtbl DS8PrimaryProp_Vtbl;
43 static void DS8Primary_SetParams(DS8Primary *This, const DS3DLISTENER *params, LONG flags);
46 static inline DS8Primary *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
48 return CONTAINING_RECORD(iface, DS8Primary, IDirectSoundBuffer_iface);
51 static inline DS8Primary *impl_from_IDirectSound3DListener(IDirectSound3DListener *iface)
53 return CONTAINING_RECORD(iface, DS8Primary, IDirectSound3DListener_iface);
56 static inline DS8Primary *impl_from_IKsPropertySet(IKsPropertySet *iface)
58 return CONTAINING_RECORD(iface, DS8Primary, IKsPropertySet_iface);
62 static void trigger_elapsed_notifies(DS8Buffer *buf, DWORD lastpos, DWORD curpos)
64 DSBPOSITIONNOTIFY *not = buf->notify;
65 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
66 for(;not != not_end;++not)
68 HANDLE event = not->hEventNotify;
69 DWORD ofs = not->dwOffset;
71 if(ofs == (DWORD)DSBPN_OFFSETSTOP)
72 continue;
74 if(curpos < lastpos) /* Wraparound case */
76 if(ofs < curpos || ofs >= lastpos)
78 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
79 SetEvent(event);
82 else if(ofs >= lastpos && ofs < curpos) /* Normal case */
84 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
85 SetEvent(event);
90 static void trigger_stop_notifies(DS8Buffer *buf)
92 DSBPOSITIONNOTIFY *not = buf->notify;
93 DSBPOSITIONNOTIFY *not_end = not + buf->nnotify;
94 for(;not != not_end;++not)
96 if(not->dwOffset != (DWORD)DSBPN_OFFSETSTOP)
97 continue;
98 TRACE("Triggering notification %d from buffer %p\n", not - buf->notify, buf);
99 SetEvent(not->hEventNotify);
103 void DS8Primary_triggernots(DS8Primary *prim)
105 DS8Buffer **curnot, **endnot;
107 curnot = prim->notifies;
108 endnot = curnot + prim->nnotifies;
109 while(curnot != endnot)
111 DS8Buffer *buf = *curnot;
112 DS8Data *data = buf->buffer;
113 DWORD curpos = buf->lastpos;
114 ALint state = 0;
115 ALint ofs;
117 alGetSourcei(buf->source, AL_BYTE_OFFSET, &ofs);
118 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
119 if(buf->segsize == 0)
120 curpos = (state == AL_STOPPED) ? data->buf_size : ofs;
121 else
123 if(state != AL_STOPPED)
124 curpos = ofs + buf->queue_base;
125 else
127 ALint queued;
128 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
129 curpos = buf->segsize*queued + buf->queue_base;
132 if(curpos >= (DWORD)data->buf_size)
134 if(buf->islooping)
135 curpos %= (DWORD)data->buf_size;
136 else if(buf->isplaying)
138 curpos = data->buf_size;
139 alSourceStop(buf->source);
140 alSourcei(buf->source, AL_BUFFER, 0);
141 buf->curidx = 0;
142 buf->isplaying = FALSE;
146 if(state != AL_PLAYING)
147 state = buf->isplaying ? AL_PLAYING : AL_PAUSED;
149 checkALError();
151 if(buf->lastpos != curpos)
153 trigger_elapsed_notifies(buf, buf->lastpos, curpos);
154 buf->lastpos = curpos;
156 if(state != AL_PLAYING)
158 /* Remove this buffer from list and put another at the current
159 * position; don't increment i
161 trigger_stop_notifies(buf);
162 *curnot = *(--endnot);
163 prim->nnotifies--;
164 continue;
166 curnot++;
168 checkALError();
171 static void do_buffer_stream(DS8Buffer *buf, BYTE *scratch_mem)
173 DS8Data *data = buf->buffer;
174 ALint ofs, done = 0, queued = QBUFFERS, state = AL_PLAYING;
175 ALuint which;
177 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
178 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
179 alGetSourcei(buf->source, AL_BUFFERS_PROCESSED, &done);
181 if(done > 0)
183 ALuint bids[QBUFFERS];
184 queued -= done;
186 alSourceUnqueueBuffers(buf->source, done, bids);
187 buf->queue_base = (buf->queue_base + buf->segsize*done) % data->buf_size;
189 while(queued < QBUFFERS)
191 which = buf->stream_bids[buf->curidx];
192 ofs = buf->data_offset;
194 if(buf->segsize < data->buf_size - ofs)
196 alBufferData(which, data->buf_format, data->data + ofs, buf->segsize,
197 data->format.Format.nSamplesPerSec);
198 buf->data_offset = ofs + buf->segsize;
200 else if(buf->islooping)
202 ALsizei rem = data->buf_size - ofs;
203 if(rem > 2048) rem = 2048;
205 memcpy(scratch_mem, data->data + ofs, rem);
206 while(rem < buf->segsize)
208 ALsizei todo = buf->segsize - rem;
209 if(todo > data->buf_size)
210 todo = data->buf_size;
211 memcpy(scratch_mem + rem, data->data, todo);
212 rem += todo;
214 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
215 data->format.Format.nSamplesPerSec);
216 buf->data_offset = (ofs+buf->segsize) % data->buf_size;
218 else
220 ALsizei rem = data->buf_size - ofs;
221 if(rem > 2048) rem = 2048;
222 if(rem == 0) break;
224 memcpy(scratch_mem, data->data + ofs, rem);
225 memset(scratch_mem+rem, (data->format.Format.wBitsPerSample==8) ? 128 : 0,
226 buf->segsize - rem);
227 alBufferData(which, data->buf_format, scratch_mem, buf->segsize,
228 data->format.Format.nSamplesPerSec);
229 buf->data_offset = data->buf_size;
232 alSourceQueueBuffers(buf->source, 1, &which);
233 buf->curidx = (buf->curidx+1)%QBUFFERS;
234 queued++;
237 if(!queued)
239 buf->data_offset = 0;
240 buf->queue_base = data->buf_size;
241 buf->curidx = 0;
242 buf->isplaying = FALSE;
244 else if(state != AL_PLAYING)
245 alSourcePlay(buf->source);
248 void DS8Primary_streamfeeder(DS8Primary *prim, BYTE *scratch_mem)
250 /* OpenAL doesn't support our lovely buffer extensions so just make sure
251 * enough buffers are queued for streaming
253 if(prim->write_emu)
255 DS8Buffer *buf = &prim->writable_buf;
256 if(buf->segsize != 0 && buf->isplaying)
257 do_buffer_stream(buf, scratch_mem);
259 else
261 struct DSBufferGroup *bufgroup = prim->BufferGroups;
262 struct DSBufferGroup *endgroup = bufgroup + prim->NumBufferGroups;
263 for(;bufgroup != endgroup;++bufgroup)
265 DWORD64 usemask = ~bufgroup->FreeBuffers;
266 while(usemask)
268 int idx = CTZ64(usemask);
269 DS8Buffer *buf = bufgroup->Buffers + idx;
270 usemask &= ~(U64(1) << idx);
272 if(buf->segsize != 0 && buf->isplaying)
273 do_buffer_stream(buf, scratch_mem);
277 checkALError();
281 HRESULT DS8Primary_PreInit(DS8Primary *This, DS8Impl *parent)
283 WAVEFORMATEX *wfx;
284 DWORD num_srcs;
285 DWORD count;
286 HRESULT hr;
287 DWORD i;
289 This->IDirectSoundBuffer_iface.lpVtbl = &DS8Primary_Vtbl;
290 This->IDirectSound3DListener_iface.lpVtbl = &DS8Primary3D_Vtbl;
291 This->IKsPropertySet_iface.lpVtbl = &DS8PrimaryProp_Vtbl;
293 This->parent = parent;
294 This->share = parent->share;
295 This->ctx = parent->share->ctx;
296 This->refresh = parent->share->refresh;
297 This->auxslot = parent->share->auxslot;
299 wfx = &This->format.Format;
300 wfx->wFormatTag = WAVE_FORMAT_PCM;
301 wfx->nChannels = 2;
302 wfx->wBitsPerSample = 8;
303 wfx->nSamplesPerSec = 22050;
304 wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
305 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
306 wfx->cbSize = 0;
308 This->stopped = TRUE;
310 /* Apparently primary buffer size is always 32k,
311 * tested on windows with 192k 24 bits sound @ 6 channels
312 * where it will run out in 60 ms and it isn't pointer aligned
314 This->buf_size = 32768;
316 setALContext(This->ctx);
317 This->deferred.eax = EnvironmentDefaults[EAX_ENVIRONMENT_GENERIC];
318 This->deferred.eax1_volume = 0.5f;
319 This->deferred.eax1_dampening = 0.5f;
320 if(This->auxslot != 0)
322 ALint revid = alGetEnumValue("AL_EFFECT_REVERB");
323 if(revid != 0 && revid != -1)
325 alGenEffects(1, &This->effect);
326 alEffecti(This->effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
327 checkALError();
330 popALContext();
332 num_srcs = This->share->sources.max_alloc;
334 hr = DSERR_OUTOFMEMORY;
335 This->notifies = HeapAlloc(GetProcessHeap(), 0, num_srcs*sizeof(*This->notifies));
336 if(!This->notifies) goto fail;
337 This->sizenotifies = num_srcs;
339 count = (num_srcs+63) / 64;
340 This->BufferGroups = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
341 count*sizeof(*This->BufferGroups));
342 if(!This->BufferGroups) goto fail;
344 for(i = 0;i < count;++i)
346 This->BufferGroups[i].Buffers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
347 64*sizeof(This->BufferGroups[0].Buffers[0]));
348 if(!This->BufferGroups[i].Buffers)
350 while(i > 0)
351 HeapFree(GetProcessHeap(), 0, This->BufferGroups[--i].Buffers);
352 HeapFree(GetProcessHeap(), 0, This->BufferGroups);
353 This->BufferGroups = NULL;
354 goto fail;
356 This->BufferGroups[i].FreeBuffers = ~(DWORD64)0;
358 This->NumBufferGroups = count;
360 return S_OK;
362 fail:
363 DS8Primary_Clear(This);
364 return hr;
367 void DS8Primary_Clear(DS8Primary *This)
369 struct DSBufferGroup *bufgroup;
370 DWORD i;
372 TRACE("Clearing primary %p\n", This);
374 if(!This->parent)
375 return;
377 setALContext(This->ctx);
378 if(This->effect)
379 alDeleteEffects(1, &This->effect);
380 popALContext();
382 bufgroup = This->BufferGroups;
383 for(i = 0;i < This->NumBufferGroups;++i)
385 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
386 while(usemask)
388 int idx = CTZ64(usemask);
389 DS8Buffer *buf = bufgroup[i].Buffers + idx;
390 usemask &= ~(U64(1) << idx);
392 DS8Buffer_Destroy(buf);
394 HeapFree(GetProcessHeap(), 0, This->BufferGroups[i].Buffers);
397 HeapFree(GetProcessHeap(), 0, This->BufferGroups);
398 HeapFree(GetProcessHeap(), 0, This->notifies);
399 memset(This, 0, sizeof(*This));
402 static HRESULT WINAPI DS8Primary_QueryInterface(IDirectSoundBuffer *iface, REFIID riid, LPVOID *ppv)
404 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
406 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
408 *ppv = NULL;
409 if(IsEqualIID(riid, &IID_IUnknown) ||
410 IsEqualIID(riid, &IID_IDirectSoundBuffer))
411 *ppv = &This->IDirectSoundBuffer_iface;
412 else if(IsEqualIID(riid, &IID_IDirectSound3DListener))
414 if((This->flags&DSBCAPS_CTRL3D))
415 *ppv = &This->IDirectSound3DListener_iface;
417 else if(IsEqualIID(riid, &IID_IKsPropertySet))
418 *ppv = &This->IKsPropertySet_iface;
419 else
420 FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
422 if(*ppv)
424 IUnknown_AddRef((IUnknown*)*ppv);
425 return S_OK;
428 return E_NOINTERFACE;
431 static ULONG WINAPI DS8Primary_AddRef(IDirectSoundBuffer *iface)
433 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
434 LONG ret;
436 ret = InterlockedIncrement(&This->ref);
437 if(ret == 1) This->flags = 0;
439 return ret;
442 static ULONG WINAPI DS8Primary_Release(IDirectSoundBuffer *iface)
444 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
445 LONG ref, oldval;
447 oldval = *(volatile LONG*)&This->ref;
448 do {
449 ref = oldval;
450 if(!ref) return 0;
451 oldval = InterlockedCompareExchange(&This->ref, ref-1, ref);
452 } while(oldval != ref);
454 return ref-1;
457 static HRESULT WINAPI DS8Primary_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
459 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
461 TRACE("(%p)->(%p)\n", iface, caps);
463 if(!caps || caps->dwSize < sizeof(*caps))
465 WARN("Invalid DSBCAPS (%p, %lu)\n", caps, caps ? caps->dwSize : 0);
466 return DSERR_INVALIDPARAM;
469 caps->dwFlags = This->flags;
470 caps->dwBufferBytes = This->buf_size;
471 caps->dwUnlockTransferRate = 0;
472 caps->dwPlayCpuOverhead = 0;
474 return DS_OK;
477 static HRESULT WINAPI DS8Primary_GetCurrentPosition(IDirectSoundBuffer *iface, DWORD *playpos, DWORD *curpos)
479 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
480 HRESULT hr = DSERR_PRIOLEVELNEEDED;
482 EnterCriticalSection(&This->share->crst);
483 if(This->write_emu)
484 hr = IDirectSoundBuffer8_GetCurrentPosition(This->write_emu, playpos, curpos);
485 LeaveCriticalSection(&This->share->crst);
487 return hr;
490 static HRESULT WINAPI DS8Primary_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
492 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
493 HRESULT hr = S_OK;
494 UINT size;
496 if(!wfx && !written)
498 WARN("Cannot report format or format size\n");
499 return DSERR_INVALIDPARAM;
502 EnterCriticalSection(&This->share->crst);
503 size = sizeof(This->format.Format) + This->format.Format.cbSize;
504 if(written)
505 *written = size;
506 if(wfx)
508 if(allocated < size)
509 hr = DSERR_INVALIDPARAM;
510 else
511 memcpy(wfx, &This->format.Format, size);
513 LeaveCriticalSection(&This->share->crst);
515 return hr;
518 static HRESULT WINAPI DS8Primary_GetVolume(IDirectSoundBuffer *iface, LONG *volume)
520 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
521 ALfloat gain;
523 TRACE("(%p)->(%p)\n", iface, volume);
525 if(!volume)
526 return DSERR_INVALIDPARAM;
527 *volume = 0;
529 if(!(This->flags&DSBCAPS_CTRLVOLUME))
530 return DSERR_CONTROLUNAVAIL;
532 setALContext(This->ctx);
533 alGetListenerf(AL_GAIN, &gain);
534 checkALError();
535 popALContext();
537 *volume = clampI(gain_to_mB(gain), DSBVOLUME_MIN, DSBVOLUME_MAX);
538 return DS_OK;
541 static HRESULT WINAPI DS8Primary_GetPan(IDirectSoundBuffer *iface, LONG *pan)
543 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
544 HRESULT hr = DS_OK;
546 WARN("(%p)->(%p): semi-stub\n", iface, pan);
548 if(!pan)
549 return DSERR_INVALIDPARAM;
551 EnterCriticalSection(&This->share->crst);
552 if(This->write_emu)
553 hr = IDirectSoundBuffer8_GetPan(This->write_emu, pan);
554 else if(!(This->flags & DSBCAPS_CTRLPAN))
555 hr = DSERR_CONTROLUNAVAIL;
556 else
557 *pan = 0;
558 LeaveCriticalSection(&This->share->crst);
560 return hr;
563 static HRESULT WINAPI DS8Primary_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
565 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
566 HRESULT hr = DS_OK;
568 WARN("(%p)->(%p): semi-stub\n", iface, freq);
570 if(!freq)
571 return DSERR_INVALIDPARAM;
573 if(!(This->flags&DSBCAPS_CTRLFREQUENCY))
574 return DSERR_CONTROLUNAVAIL;
576 EnterCriticalSection(&This->share->crst);
577 *freq = This->format.Format.nSamplesPerSec;
578 LeaveCriticalSection(&This->share->crst);
580 return hr;
583 static HRESULT WINAPI DS8Primary_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
585 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
587 TRACE("(%p)->(%p)\n", iface, status);
589 if(!status)
590 return DSERR_INVALIDPARAM;
592 EnterCriticalSection(&This->share->crst);
593 *status = DSBSTATUS_PLAYING|DSBSTATUS_LOOPING;
594 if((This->flags&DSBCAPS_LOCDEFER))
595 *status |= DSBSTATUS_LOCHARDWARE;
597 if(This->stopped)
599 struct DSBufferGroup *bufgroup = This->BufferGroups;
600 DWORD i, state = 0;
601 HRESULT hr;
603 for(i = 0;i < This->NumBufferGroups;++i)
605 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
606 while(usemask)
608 int idx = CTZ64(usemask);
609 DS8Buffer *buf = bufgroup[i].Buffers + idx;
610 usemask &= ~(U64(1) << idx);
612 hr = DS8Buffer_GetStatus(&buf->IDirectSoundBuffer8_iface, &state);
613 if(SUCCEEDED(hr) && (state&DSBSTATUS_PLAYING)) break;
616 if(!(state&DSBSTATUS_PLAYING))
618 /* Primary stopped and no buffers playing.. */
619 *status = 0;
622 LeaveCriticalSection(&This->share->crst);
624 return DS_OK;
627 HRESULT WINAPI DS8Primary_Initialize(IDirectSoundBuffer *iface, IDirectSound *ds, const DSBUFFERDESC *desc)
629 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
630 HRESULT hr;
632 TRACE("(%p)->(%p, %p)\n", iface, ds, desc);
634 if(!desc || desc->lpwfxFormat || desc->dwBufferBytes)
636 WARN("Bad DSBDESC for primary buffer\n");
637 return DSERR_INVALIDPARAM;
639 if((desc->dwFlags&DSBCAPS_CTRLFX) ||
640 (desc->dwFlags&DSBCAPS_CTRLPOSITIONNOTIFY) ||
641 (desc->dwFlags&DSBCAPS_LOCSOFTWARE))
643 WARN("Bad dwFlags %08lx\n", desc->dwFlags);
644 return DSERR_INVALIDPARAM;
647 /* Should be 0 if not initialized */
648 if(This->flags)
649 return DSERR_ALREADYINITIALIZED;
651 hr = DS_OK;
652 if(This->parent->prio_level == DSSCL_WRITEPRIMARY)
654 DSBUFFERDESC emudesc;
655 DS8Buffer *emu;
657 if(This->write_emu)
659 ERR("There shouldn't be a write_emu!\n");
660 IDirectSoundBuffer8_Release(This->write_emu);
661 This->write_emu = NULL;
664 memset(&emudesc, 0, sizeof(emudesc));
665 emudesc.dwSize = sizeof(emudesc);
666 emudesc.dwFlags = DSBCAPS_LOCHARDWARE | (desc->dwFlags&DSBCAPS_CTRLPAN);
667 /* Dont play last incomplete sample */
668 emudesc.dwBufferBytes = This->buf_size - (This->buf_size%This->format.Format.nBlockAlign);
669 emudesc.lpwfxFormat = &This->format.Format;
671 hr = DS8Buffer_Create(&emu, This, NULL, TRUE);
672 if(SUCCEEDED(hr))
674 This->write_emu = &emu->IDirectSoundBuffer8_iface;
675 hr = DS8Buffer_Initialize(This->write_emu, ds, &emudesc);
676 if(FAILED(hr))
678 IDirectSoundBuffer8_Release(This->write_emu);
679 This->write_emu = NULL;
684 if(SUCCEEDED(hr))
686 DS3DLISTENER *listener = &This->deferred.ds3d;
687 listener->dwSize = sizeof(This->deferred.ds3d);
688 listener->vPosition.x = 0.0f;
689 listener->vPosition.y = 0.0f;
690 listener->vPosition.z = 0.0f;
691 listener->vVelocity.x = 0.0f;
692 listener->vVelocity.y = 0.0f;
693 listener->vVelocity.z = 0.0f;
694 listener->vOrientFront.x = 0.0f;
695 listener->vOrientFront.y = 0.0f;
696 listener->vOrientFront.z = 1.0f;
697 listener->vOrientTop.x = 0.0f;
698 listener->vOrientTop.y = 1.0f;
699 listener->vOrientTop.z = 0.0f;
700 listener->flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
701 listener->flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
702 listener->flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
704 This->flags = desc->dwFlags | DSBCAPS_LOCHARDWARE;
706 if((This->flags&DSBCAPS_CTRL3D))
708 union PrimaryParamFlags dirty = { 0l };
710 dirty.bit.pos = 1;
711 dirty.bit.vel = 1;
712 dirty.bit.orientation = 1;
713 dirty.bit.distancefactor = 1;
714 dirty.bit.rollofffactor = 1;
715 dirty.bit.dopplerfactor = 1;
716 DS8Primary_SetParams(This, listener, dirty.flags);
719 return hr;
722 static HRESULT WINAPI DS8Primary_Lock(IDirectSoundBuffer *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
724 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
725 HRESULT hr = DSERR_PRIOLEVELNEEDED;
727 TRACE("(%p)->(%lu, %lu, %p, %p, %p, %p, %lu)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
729 EnterCriticalSection(&This->share->crst);
730 if(This->write_emu)
731 hr = IDirectSoundBuffer8_Lock(This->write_emu, ofs, bytes, ptr1, len1, ptr2, len2, flags);
732 LeaveCriticalSection(&This->share->crst);
734 return hr;
737 static HRESULT WINAPI DS8Primary_Play(IDirectSoundBuffer *iface, DWORD res1, DWORD res2, DWORD flags)
739 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
740 HRESULT hr;
742 TRACE("(%p)->(%lu, %lu, %lu)\n", iface, res1, res2, flags);
744 if(!(flags & DSBPLAY_LOOPING))
746 WARN("Flags (%08lx) not set to DSBPLAY_LOOPING\n", flags);
747 return DSERR_INVALIDPARAM;
750 EnterCriticalSection(&This->share->crst);
751 hr = S_OK;
752 if(This->write_emu)
753 hr = IDirectSoundBuffer8_Play(This->write_emu, res1, res2, flags);
754 if(SUCCEEDED(hr))
755 This->stopped = FALSE;
756 LeaveCriticalSection(&This->share->crst);
758 return hr;
761 static HRESULT WINAPI DS8Primary_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD pos)
763 WARN("(%p)->(%lu)\n", iface, pos);
764 return DSERR_INVALIDCALL;
767 /* Just assume the format is crap, and clean up the damage */
768 static HRESULT copy_waveformat(WAVEFORMATEX *wfx, const WAVEFORMATEX *from)
770 if(from->nChannels <= 0)
772 WARN("Invalid Channels %d\n", from->nChannels);
773 return DSERR_INVALIDPARAM;
775 if(from->nSamplesPerSec < DSBFREQUENCY_MIN || from->nSamplesPerSec > DSBFREQUENCY_MAX)
777 WARN("Invalid SamplesPerSec %lu\n", from->nSamplesPerSec);
778 return DSERR_INVALIDPARAM;
780 if(from->nBlockAlign <= 0)
782 WARN("Invalid BlockAlign %d\n", from->nBlockAlign);
783 return DSERR_INVALIDPARAM;
785 if(from->wBitsPerSample == 0 || (from->wBitsPerSample%8) != 0)
787 WARN("Invalid BitsPerSample %d\n", from->wBitsPerSample);
788 return DSERR_INVALIDPARAM;
790 if(from->nBlockAlign != from->nChannels*from->wBitsPerSample/8)
792 WARN("Invalid BlockAlign %d (expected %u = %u*%u/8)\n",
793 from->nBlockAlign, from->nChannels*from->wBitsPerSample/8,
794 from->nChannels, from->wBitsPerSample);
795 return DSERR_INVALIDPARAM;
797 if(from->nAvgBytesPerSec != from->nBlockAlign*from->nSamplesPerSec)
799 WARN("Invalid AvgBytesPerSec %lu (expected %lu = %lu*%u)\n",
800 from->nAvgBytesPerSec, from->nSamplesPerSec*from->nBlockAlign,
801 from->nSamplesPerSec, from->nBlockAlign);
802 return DSERR_INVALIDPARAM;
805 if(from->wFormatTag == WAVE_FORMAT_PCM)
807 if(from->wBitsPerSample > 32)
808 return DSERR_INVALIDPARAM;
809 wfx->cbSize = 0;
811 else if(from->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
813 if(from->wBitsPerSample != 32)
814 return DSERR_INVALIDPARAM;
815 wfx->cbSize = 0;
817 else if(from->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
819 WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx;
820 const WAVEFORMATEXTENSIBLE *fromx = (const WAVEFORMATEXTENSIBLE*)from;
821 const WORD size = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
823 /* Fail silently.. */
824 if(from->cbSize < size) return DS_OK;
825 if(fromx->Samples.wValidBitsPerSample > fromx->Format.wBitsPerSample)
826 return DSERR_INVALIDPARAM;
828 if(IsEqualGUID(&fromx->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
830 if(from->wBitsPerSample > 32)
831 return DSERR_INVALIDPARAM;
833 else if(IsEqualGUID(&fromx->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
835 if(from->wBitsPerSample != 32)
836 return DSERR_INVALIDPARAM;
838 else
840 ERR("Unhandled extensible format: %s\n", debugstr_guid(&fromx->SubFormat));
841 return DSERR_INVALIDPARAM;
844 wfe->Format.cbSize = size;
845 wfe->Samples.wValidBitsPerSample = fromx->Samples.wValidBitsPerSample;
846 if(!wfe->Samples.wValidBitsPerSample)
847 wfe->Samples.wValidBitsPerSample = fromx->Format.wBitsPerSample;
848 wfe->dwChannelMask = fromx->dwChannelMask;
849 wfe->SubFormat = fromx->SubFormat;
851 else
853 ERR("Unhandled format tag %04x\n", from->wFormatTag);
854 return DSERR_INVALIDPARAM;
857 wfx->wFormatTag = from->wFormatTag;
858 wfx->nChannels = from->nChannels;
859 wfx->nSamplesPerSec = from->nSamplesPerSec;
860 wfx->nAvgBytesPerSec = from->nSamplesPerSec * from->nBlockAlign;
861 wfx->nBlockAlign = from->wBitsPerSample * from->nChannels / 8;
862 wfx->wBitsPerSample = from->wBitsPerSample;
863 return DS_OK;
866 static HRESULT WINAPI DS8Primary_SetFormat(IDirectSoundBuffer *iface, const WAVEFORMATEX *wfx)
868 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
869 HRESULT hr = S_OK;
871 TRACE("(%p)->(%p)\n", iface, wfx);
873 if(!wfx)
875 WARN("Missing format\n");
876 return DSERR_INVALIDPARAM;
879 EnterCriticalSection(&This->share->crst);
881 if(This->parent->prio_level < DSSCL_PRIORITY)
883 hr = DSERR_PRIOLEVELNEEDED;
884 goto out;
887 TRACE("Requested primary format:\n"
888 " FormatTag = %04x\n"
889 " Channels = %u\n"
890 " SamplesPerSec = %lu\n"
891 " AvgBytesPerSec = %lu\n"
892 " BlockAlign = %u\n"
893 " BitsPerSample = %u\n",
894 wfx->wFormatTag, wfx->nChannels,
895 wfx->nSamplesPerSec, wfx->nAvgBytesPerSec,
896 wfx->nBlockAlign, wfx->wBitsPerSample);
898 hr = copy_waveformat(&This->format.Format, wfx);
899 if(SUCCEEDED(hr) && This->write_emu)
901 DS8Buffer *buf;
902 DSBUFFERDESC desc;
904 IDirectSoundBuffer8_Release(This->write_emu);
905 This->write_emu = NULL;
907 memset(&desc, 0, sizeof(desc));
908 desc.dwSize = sizeof(desc);
909 desc.dwFlags = DSBCAPS_LOCHARDWARE|DSBCAPS_CTRLPAN;
910 desc.dwBufferBytes = This->buf_size - (This->buf_size % This->format.Format.nBlockAlign);
911 desc.lpwfxFormat = &This->format.Format;
913 hr = DS8Buffer_Create(&buf, This, NULL, TRUE);
914 if(FAILED(hr)) goto out;
916 This->write_emu = &buf->IDirectSoundBuffer8_iface;
917 hr = DS8Buffer_Initialize(This->write_emu, &This->parent->IDirectSound_iface, &desc);
918 if(FAILED(hr))
920 IDirectSoundBuffer8_Release(This->write_emu);
921 This->write_emu = NULL;
925 out:
926 LeaveCriticalSection(&This->share->crst);
927 return hr;
930 static HRESULT WINAPI DS8Primary_SetVolume(IDirectSoundBuffer *iface, LONG vol)
932 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
934 TRACE("(%p)->(%ld)\n", iface, vol);
936 if(vol > DSBVOLUME_MAX || vol < DSBVOLUME_MIN)
938 WARN("Invalid volume (%ld)\n", vol);
939 return DSERR_INVALIDPARAM;
942 if(!(This->flags&DSBCAPS_CTRLVOLUME))
943 return DSERR_CONTROLUNAVAIL;
945 setALContext(This->ctx);
946 alListenerf(AL_GAIN, mB_to_gain(vol));
947 popALContext();
949 return DS_OK;
952 static HRESULT WINAPI DS8Primary_SetPan(IDirectSoundBuffer *iface, LONG pan)
954 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
955 HRESULT hr;
957 TRACE("(%p)->(%ld)\n", iface, pan);
959 if(pan > DSBPAN_RIGHT || pan < DSBPAN_LEFT)
961 WARN("invalid parameter: pan = %ld\n", pan);
962 return DSERR_INVALIDPARAM;
965 EnterCriticalSection(&This->share->crst);
966 if(!(This->flags&DSBCAPS_CTRLPAN))
968 WARN("control unavailable\n");
969 hr = DSERR_CONTROLUNAVAIL;
971 else if(This->write_emu)
972 hr = IDirectSoundBuffer8_SetPan(This->write_emu, pan);
973 else
975 FIXME("Not supported\n");
976 hr = E_NOTIMPL;
978 LeaveCriticalSection(&This->share->crst);
980 return hr;
983 static HRESULT WINAPI DS8Primary_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
985 WARN("(%p)->(%lu)\n", iface, freq);
986 return DSERR_CONTROLUNAVAIL;
989 static HRESULT WINAPI DS8Primary_Stop(IDirectSoundBuffer *iface)
991 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
992 HRESULT hr = S_OK;
994 TRACE("(%p)->()\n", iface);
996 EnterCriticalSection(&This->share->crst);
997 if(This->write_emu)
998 hr = IDirectSoundBuffer8_Stop(This->write_emu);
999 if(SUCCEEDED(hr))
1000 This->stopped = TRUE;
1001 LeaveCriticalSection(&This->share->crst);
1003 return hr;
1006 static HRESULT WINAPI DS8Primary_Unlock(IDirectSoundBuffer *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
1008 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
1009 HRESULT hr = DSERR_INVALIDCALL;
1011 TRACE("(%p)->(%p, %lu, %p, %lu)\n", iface, ptr1, len1, ptr2, len2);
1013 EnterCriticalSection(&This->share->crst);
1014 if(This->write_emu)
1015 hr = IDirectSoundBuffer8_Unlock(This->write_emu, ptr1, len1, ptr2, len2);
1016 LeaveCriticalSection(&This->share->crst);
1018 return hr;
1021 static HRESULT WINAPI DS8Primary_Restore(IDirectSoundBuffer *iface)
1023 DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
1024 HRESULT hr = S_OK;
1026 TRACE("(%p)->()\n", iface);
1028 EnterCriticalSection(&This->share->crst);
1029 if(This->write_emu)
1030 hr = IDirectSoundBuffer8_Restore(This->write_emu);
1031 LeaveCriticalSection(&This->share->crst);
1033 return hr;
1036 static const IDirectSoundBufferVtbl DS8Primary_Vtbl =
1038 DS8Primary_QueryInterface,
1039 DS8Primary_AddRef,
1040 DS8Primary_Release,
1041 DS8Primary_GetCaps,
1042 DS8Primary_GetCurrentPosition,
1043 DS8Primary_GetFormat,
1044 DS8Primary_GetVolume,
1045 DS8Primary_GetPan,
1046 DS8Primary_GetFrequency,
1047 DS8Primary_GetStatus,
1048 DS8Primary_Initialize,
1049 DS8Primary_Lock,
1050 DS8Primary_Play,
1051 DS8Primary_SetCurrentPosition,
1052 DS8Primary_SetFormat,
1053 DS8Primary_SetVolume,
1054 DS8Primary_SetPan,
1055 DS8Primary_SetFrequency,
1056 DS8Primary_Stop,
1057 DS8Primary_Unlock,
1058 DS8Primary_Restore
1062 static void DS8Primary_SetParams(DS8Primary *This, const DS3DLISTENER *params, LONG flags)
1064 union PrimaryParamFlags dirty = { flags };
1065 DWORD i;
1067 if(dirty.bit.pos)
1068 alListener3f(AL_POSITION, params->vPosition.x, params->vPosition.y,
1069 -params->vPosition.z);
1070 if(dirty.bit.vel)
1071 alListener3f(AL_VELOCITY, params->vVelocity.x, params->vVelocity.y,
1072 -params->vVelocity.z);
1073 if(dirty.bit.orientation)
1075 ALfloat orient[6] = {
1076 params->vOrientFront.x, params->vOrientFront.y, -params->vOrientFront.z,
1077 params->vOrientTop.x, params->vOrientTop.y, -params->vOrientTop.z
1079 alListenerfv(AL_ORIENTATION, orient);
1081 if(dirty.bit.distancefactor)
1083 alSpeedOfSound(343.3f/params->flDistanceFactor);
1084 if(BITFIELD_TEST(This->share->Exts, EXT_EFX))
1085 alListenerf(AL_METERS_PER_UNIT, params->flDistanceFactor);
1087 if(dirty.bit.rollofffactor)
1089 struct DSBufferGroup *bufgroup = This->BufferGroups;
1090 ALfloat rolloff = params->flRolloffFactor;
1091 This->rollofffactor = rolloff;
1093 for(i = 0;i < This->NumBufferGroups;++i)
1095 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1096 while(usemask)
1098 int idx = CTZ64(usemask);
1099 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1100 usemask &= ~(U64(1) << idx);
1102 if(buf->ds3dmode != DS3DMODE_DISABLE)
1103 alSourcef(buf->source, AL_ROLLOFF_FACTOR, rolloff);
1107 if(dirty.bit.dopplerfactor)
1108 alDopplerFactor(params->flDopplerFactor);
1109 if(dirty.bit.effect)
1110 alAuxiliaryEffectSloti(This->auxslot, AL_EFFECTSLOT_EFFECT, This->effect);
1113 static HRESULT WINAPI DS8Primary3D_QueryInterface(IDirectSound3DListener *iface, REFIID riid, void **ppv)
1115 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1116 return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1119 static ULONG WINAPI DS8Primary3D_AddRef(IDirectSound3DListener *iface)
1121 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1122 LONG ret;
1124 ret = InterlockedIncrement(&This->ds3d_ref);
1125 TRACE("new refcount %ld\n", ret);
1127 return ret;
1130 static ULONG WINAPI DS8Primary3D_Release(IDirectSound3DListener *iface)
1132 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1133 LONG ret;
1135 ret = InterlockedDecrement(&This->ds3d_ref);
1136 TRACE("new refcount %ld\n", ret);
1138 return ret;
1142 static HRESULT WINAPI DS8Primary3D_GetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE *distancefactor)
1144 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1146 TRACE("(%p)->(%p)\n", iface, distancefactor);
1148 if(!distancefactor)
1150 WARN("Invalid parameter %p\n", distancefactor);
1151 return DSERR_INVALIDPARAM;
1154 setALContext(This->ctx);
1155 *distancefactor = 343.3f/alGetFloat(AL_SPEED_OF_SOUND);
1156 checkALError();
1157 popALContext();
1159 return S_OK;
1162 static HRESULT WINAPI DS8Primary3D_GetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE *dopplerfactor)
1164 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1166 TRACE("(%p)->(%p)\n", iface, dopplerfactor);
1168 if(!dopplerfactor)
1170 WARN("Invalid parameter %p\n", dopplerfactor);
1171 return DSERR_INVALIDPARAM;
1174 setALContext(This->ctx);
1175 *dopplerfactor = alGetFloat(AL_DOPPLER_FACTOR);
1176 checkALError();
1177 popALContext();
1179 return S_OK;
1182 static HRESULT WINAPI DS8Primary3D_GetOrientation(IDirectSound3DListener *iface, D3DVECTOR *front, D3DVECTOR *top)
1184 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1185 ALfloat orient[6];
1187 TRACE("(%p)->(%p, %p)\n", iface, front, top);
1189 if(!front || !top)
1191 WARN("Invalid parameter %p %p\n", front, top);
1192 return DSERR_INVALIDPARAM;
1195 setALContext(This->ctx);
1196 alGetListenerfv(AL_ORIENTATION, orient);
1197 checkALError();
1198 popALContext();
1200 front->x = orient[0];
1201 front->y = orient[1];
1202 front->z = -orient[2];
1203 top->x = orient[3];
1204 top->y = orient[4];
1205 top->z = -orient[5];
1206 return S_OK;
1209 static HRESULT WINAPI DS8Primary3D_GetPosition(IDirectSound3DListener *iface, D3DVECTOR *pos)
1211 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1212 ALfloat alpos[3];
1214 TRACE("(%p)->(%p)\n", iface, pos);
1216 if(!pos)
1218 WARN("Invalid parameter %p\n", pos);
1219 return DSERR_INVALIDPARAM;
1222 setALContext(This->ctx);
1223 alGetListenerfv(AL_POSITION, alpos);
1224 checkALError();
1225 popALContext();
1227 pos->x = alpos[0];
1228 pos->y = alpos[1];
1229 pos->z = -alpos[2];
1230 return S_OK;
1233 static HRESULT WINAPI DS8Primary3D_GetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE *rollofffactor)
1235 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1237 TRACE("(%p)->(%p)\n", iface, rollofffactor);
1239 if(!rollofffactor)
1241 WARN("Invalid parameter %p\n", rollofffactor);
1242 return DSERR_INVALIDPARAM;
1245 EnterCriticalSection(&This->share->crst);
1246 *rollofffactor = This->rollofffactor;
1247 LeaveCriticalSection(&This->share->crst);
1249 return S_OK;
1252 static HRESULT WINAPI DS8Primary3D_GetVelocity(IDirectSound3DListener *iface, D3DVECTOR *velocity)
1254 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1255 ALfloat vel[3];
1257 TRACE("(%p)->(%p)\n", iface, velocity);
1259 if(!velocity)
1261 WARN("Invalid parameter %p\n", velocity);
1262 return DSERR_INVALIDPARAM;
1265 setALContext(This->ctx);
1266 alGetListenerfv(AL_VELOCITY, vel);
1267 checkALError();
1268 popALContext();
1270 velocity->x = vel[0];
1271 velocity->y = vel[1];
1272 velocity->z = -vel[2];
1273 return S_OK;
1276 static HRESULT WINAPI DS8Primary3D_GetAllParameters(IDirectSound3DListener *iface, DS3DLISTENER *listener)
1278 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1280 TRACE("(%p)->(%p)\n", iface, listener);
1282 if(!listener || listener->dwSize < sizeof(*listener))
1284 WARN("Invalid DS3DLISTENER %p %lu\n", listener, listener ? listener->dwSize : 0);
1285 return DSERR_INVALIDPARAM;
1288 EnterCriticalSection(&This->share->crst);
1289 setALContext(This->ctx);
1290 DS8Primary3D_GetPosition(iface, &listener->vPosition);
1291 DS8Primary3D_GetVelocity(iface, &listener->vVelocity);
1292 DS8Primary3D_GetOrientation(iface, &listener->vOrientFront, &listener->vOrientTop);
1293 DS8Primary3D_GetDistanceFactor(iface, &listener->flDistanceFactor);
1294 DS8Primary3D_GetRolloffFactor(iface, &listener->flRolloffFactor);
1295 DS8Primary3D_GetDopplerFactor(iface, &listener->flDopplerFactor);
1296 popALContext();
1297 LeaveCriticalSection(&This->share->crst);
1299 return DS_OK;
1303 static HRESULT WINAPI DS8Primary3D_SetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1305 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1307 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1309 if(factor < DS3D_MINDISTANCEFACTOR ||
1310 factor > DS3D_MAXDISTANCEFACTOR)
1312 WARN("Invalid parameter %f\n", factor);
1313 return DSERR_INVALIDPARAM;
1316 if(apply == DS3D_DEFERRED)
1318 EnterCriticalSection(&This->share->crst);
1319 This->deferred.ds3d.flDistanceFactor = factor;
1320 This->dirty.bit.distancefactor = 1;
1321 LeaveCriticalSection(&This->share->crst);
1323 else
1325 setALContext(This->ctx);
1326 alSpeedOfSound(343.3f/factor);
1327 if(BITFIELD_TEST(This->share->Exts, EXT_EFX))
1328 alListenerf(AL_METERS_PER_UNIT, factor);
1329 checkALError();
1330 popALContext();
1333 return S_OK;
1336 static HRESULT WINAPI DS8Primary3D_SetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1338 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1340 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1342 if(factor < DS3D_MINDOPPLERFACTOR ||
1343 factor > DS3D_MAXDOPPLERFACTOR)
1345 WARN("Invalid parameter %f\n", factor);
1346 return DSERR_INVALIDPARAM;
1349 if(apply == DS3D_DEFERRED)
1351 EnterCriticalSection(&This->share->crst);
1352 This->deferred.ds3d.flDopplerFactor = factor;
1353 This->dirty.bit.dopplerfactor = 1;
1354 LeaveCriticalSection(&This->share->crst);
1356 else
1358 setALContext(This->ctx);
1359 alDopplerFactor(factor);
1360 checkALError();
1361 popALContext();
1364 return S_OK;
1367 static HRESULT WINAPI DS8Primary3D_SetOrientation(IDirectSound3DListener *iface, D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront, D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD apply)
1369 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1371 TRACE("(%p)->(%f, %f, %f, %f, %f, %f, %lu)\n", iface, xFront, yFront, zFront, xTop, yTop, zTop, apply);
1373 if(apply == DS3D_DEFERRED)
1375 EnterCriticalSection(&This->share->crst);
1376 This->deferred.ds3d.vOrientFront.x = xFront;
1377 This->deferred.ds3d.vOrientFront.y = yFront;
1378 This->deferred.ds3d.vOrientFront.z = zFront;
1379 This->deferred.ds3d.vOrientTop.x = xTop;
1380 This->deferred.ds3d.vOrientTop.y = yTop;
1381 This->deferred.ds3d.vOrientTop.z = zTop;
1382 This->dirty.bit.orientation = 1;
1383 LeaveCriticalSection(&This->share->crst);
1385 else
1387 ALfloat orient[6] = {
1388 xFront, yFront, -zFront,
1389 xTop, yTop, -zTop
1391 setALContext(This->ctx);
1392 alListenerfv(AL_ORIENTATION, orient);
1393 checkALError();
1394 popALContext();
1397 return S_OK;
1400 static HRESULT WINAPI DS8Primary3D_SetPosition(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1402 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1404 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1406 if(apply == DS3D_DEFERRED)
1408 EnterCriticalSection(&This->share->crst);
1409 This->deferred.ds3d.vPosition.x = x;
1410 This->deferred.ds3d.vPosition.y = y;
1411 This->deferred.ds3d.vPosition.z = z;
1412 This->dirty.bit.pos = 1;
1413 LeaveCriticalSection(&This->share->crst);
1415 else
1417 setALContext(This->ctx);
1418 alListener3f(AL_POSITION, x, y, -z);
1419 checkALError();
1420 popALContext();
1423 return S_OK;
1426 static HRESULT WINAPI DS8Primary3D_SetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1428 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1430 TRACE("(%p)->(%f, %lu)\n", iface, factor, apply);
1432 if(factor < DS3D_MINROLLOFFFACTOR ||
1433 factor > DS3D_MAXROLLOFFFACTOR)
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.flRolloffFactor = factor;
1443 This->dirty.bit.rollofffactor = 1;
1445 else
1447 struct DSBufferGroup *bufgroup = This->BufferGroups;
1448 DWORD i;
1450 This->rollofffactor = factor;
1452 setALContext(This->ctx);
1453 for(i = 0;i < This->NumBufferGroups;++i)
1455 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1456 while(usemask)
1458 int idx = CTZ64(usemask);
1459 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1460 usemask &= ~(U64(1) << idx);
1462 if(buf->ds3dmode != DS3DMODE_DISABLE)
1463 alSourcef(buf->source, AL_ROLLOFF_FACTOR, factor);
1466 checkALError();
1467 popALContext();
1469 LeaveCriticalSection(&This->share->crst);
1471 return S_OK;
1474 static HRESULT WINAPI DS8Primary3D_SetVelocity(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1476 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1478 TRACE("(%p)->(%f, %f, %f, %lu)\n", iface, x, y, z, apply);
1480 if(apply == DS3D_DEFERRED)
1482 EnterCriticalSection(&This->share->crst);
1483 This->deferred.ds3d.vVelocity.x = x;
1484 This->deferred.ds3d.vVelocity.y = y;
1485 This->deferred.ds3d.vVelocity.z = z;
1486 This->dirty.bit.vel = 1;
1487 LeaveCriticalSection(&This->share->crst);
1489 else
1491 setALContext(This->ctx);
1492 alListener3f(AL_VELOCITY, x, y, -z);
1493 checkALError();
1494 popALContext();
1497 return S_OK;
1500 static HRESULT WINAPI DS8Primary3D_SetAllParameters(IDirectSound3DListener *iface, const DS3DLISTENER *listen, DWORD apply)
1502 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1504 TRACE("(%p)->(%p, %lu)\n", iface, listen, apply);
1506 if(!listen || listen->dwSize < sizeof(*listen))
1508 WARN("Invalid parameter %p %lu\n", listen, listen ? listen->dwSize : 0);
1509 return DSERR_INVALIDPARAM;
1512 if(listen->flDistanceFactor > DS3D_MAXDISTANCEFACTOR ||
1513 listen->flDistanceFactor < DS3D_MINDISTANCEFACTOR)
1515 WARN("Invalid distance factor (%f)\n", listen->flDistanceFactor);
1516 return DSERR_INVALIDPARAM;
1519 if(listen->flDopplerFactor > DS3D_MAXDOPPLERFACTOR ||
1520 listen->flDopplerFactor < DS3D_MINDOPPLERFACTOR)
1522 WARN("Invalid doppler factor (%f)\n", listen->flDopplerFactor);
1523 return DSERR_INVALIDPARAM;
1526 if(listen->flRolloffFactor < DS3D_MINROLLOFFFACTOR ||
1527 listen->flRolloffFactor > DS3D_MAXROLLOFFFACTOR)
1529 WARN("Invalid rolloff factor (%f)\n", listen->flRolloffFactor);
1530 return DSERR_INVALIDPARAM;
1533 if(apply == DS3D_DEFERRED)
1535 EnterCriticalSection(&This->share->crst);
1536 This->deferred.ds3d = *listen;
1537 This->deferred.ds3d.dwSize = sizeof(This->deferred.ds3d);
1538 This->dirty.bit.pos = 1;
1539 This->dirty.bit.vel = 1;
1540 This->dirty.bit.orientation = 1;
1541 This->dirty.bit.distancefactor = 1;
1542 This->dirty.bit.rollofffactor = 1;
1543 This->dirty.bit.dopplerfactor = 1;
1544 LeaveCriticalSection(&This->share->crst);
1546 else
1548 union PrimaryParamFlags dirty = { 0l };
1549 dirty.bit.pos = 1;
1550 dirty.bit.vel = 1;
1551 dirty.bit.orientation = 1;
1552 dirty.bit.distancefactor = 1;
1553 dirty.bit.rollofffactor = 1;
1554 dirty.bit.dopplerfactor = 1;
1556 EnterCriticalSection(&This->share->crst);
1557 setALContext(This->ctx);
1558 DS8Primary_SetParams(This, listen, dirty.flags);
1559 checkALError();
1560 popALContext();
1561 LeaveCriticalSection(&This->share->crst);
1564 return S_OK;
1567 HRESULT WINAPI DS8Primary3D_CommitDeferredSettings(IDirectSound3DListener *iface)
1569 DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1570 struct DSBufferGroup *bufgroup;
1571 LONG flags;
1572 DWORD i;
1574 EnterCriticalSection(&This->share->crst);
1575 setALContext(This->ctx);
1576 alDeferUpdatesSOFT();
1578 if((flags=InterlockedExchange(&This->dirty.flags, 0)) != 0)
1580 DS8Primary_SetParams(This, &This->deferred.ds3d, flags);
1581 /* checkALError is here for debugging */
1582 checkALError();
1584 TRACE("Dirty flags was: 0x%02lx\n", flags);
1586 bufgroup = This->BufferGroups;
1587 for(i = 0;i < This->NumBufferGroups;++i)
1589 DWORD64 usemask = ~bufgroup[i].FreeBuffers;
1590 while(usemask)
1592 int idx = CTZ64(usemask);
1593 DS8Buffer *buf = bufgroup[i].Buffers + idx;
1594 usemask &= ~(U64(1) << idx);
1596 if((flags=InterlockedExchange(&buf->dirty.flags, 0)) != 0)
1597 DS8Buffer_SetParams(buf, &buf->deferred.ds3d, &buf->deferred.eax, flags);
1600 alProcessUpdatesSOFT();
1601 checkALError();
1603 popALContext();
1604 LeaveCriticalSection(&This->share->crst);
1606 return DS_OK;
1609 static const IDirectSound3DListenerVtbl DS8Primary3D_Vtbl =
1611 DS8Primary3D_QueryInterface,
1612 DS8Primary3D_AddRef,
1613 DS8Primary3D_Release,
1614 DS8Primary3D_GetAllParameters,
1615 DS8Primary3D_GetDistanceFactor,
1616 DS8Primary3D_GetDopplerFactor,
1617 DS8Primary3D_GetOrientation,
1618 DS8Primary3D_GetPosition,
1619 DS8Primary3D_GetRolloffFactor,
1620 DS8Primary3D_GetVelocity,
1621 DS8Primary3D_SetAllParameters,
1622 DS8Primary3D_SetDistanceFactor,
1623 DS8Primary3D_SetDopplerFactor,
1624 DS8Primary3D_SetOrientation,
1625 DS8Primary3D_SetPosition,
1626 DS8Primary3D_SetRolloffFactor,
1627 DS8Primary3D_SetVelocity,
1628 DS8Primary3D_CommitDeferredSettings
1632 static HRESULT WINAPI DS8PrimaryProp_QueryInterface(IKsPropertySet *iface, REFIID riid, void **ppv)
1634 DS8Primary *This = impl_from_IKsPropertySet(iface);
1635 return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1638 static ULONG WINAPI DS8PrimaryProp_AddRef(IKsPropertySet *iface)
1640 DS8Primary *This = impl_from_IKsPropertySet(iface);
1641 LONG ret;
1643 ret = InterlockedIncrement(&This->prop_ref);
1644 TRACE("new refcount %ld\n", ret);
1646 return ret;
1649 static ULONG WINAPI DS8PrimaryProp_Release(IKsPropertySet *iface)
1651 DS8Primary *This = impl_from_IKsPropertySet(iface);
1652 LONG ret;
1654 ret = InterlockedDecrement(&This->prop_ref);
1655 TRACE("new refcount %ld\n", ret);
1657 return ret;
1660 static HRESULT WINAPI DS8PrimaryProp_Get(IKsPropertySet *iface,
1661 REFGUID guidPropSet, ULONG dwPropID,
1662 LPVOID pInstanceData, ULONG cbInstanceData,
1663 LPVOID pPropData, ULONG cbPropData,
1664 ULONG *pcbReturned)
1666 (void)iface;
1667 (void)dwPropID;
1668 (void)pInstanceData;
1669 (void)cbInstanceData;
1670 (void)pPropData;
1671 (void)cbPropData;
1672 (void)pcbReturned;
1674 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1676 return E_PROP_ID_UNSUPPORTED;
1679 static HRESULT WINAPI DS8PrimaryProp_Set(IKsPropertySet *iface,
1680 REFGUID guidPropSet, ULONG dwPropID,
1681 LPVOID pInstanceData, ULONG cbInstanceData,
1682 LPVOID pPropData, ULONG cbPropData)
1684 (void)iface;
1685 (void)dwPropID;
1686 (void)pInstanceData;
1687 (void)cbInstanceData;
1688 (void)pPropData;
1689 (void)cbPropData;
1691 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1693 return E_PROP_ID_UNSUPPORTED;
1696 static HRESULT WINAPI DS8PrimaryProp_QuerySupport(IKsPropertySet *iface,
1697 REFGUID guidPropSet, ULONG dwPropID,
1698 ULONG *pTypeSupport)
1700 (void)iface;
1701 (void)dwPropID;
1702 (void)pTypeSupport;
1704 FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1706 return E_PROP_ID_UNSUPPORTED;
1709 static const IKsPropertySetVtbl DS8PrimaryProp_Vtbl =
1711 DS8PrimaryProp_QueryInterface,
1712 DS8PrimaryProp_AddRef,
1713 DS8PrimaryProp_Release,
1714 DS8PrimaryProp_Get,
1715 DS8PrimaryProp_Set,
1716 DS8PrimaryProp_QuerySupport