Evilly merge tag 'wine-1.5.11' into dsoal
[wine/multimedia.git] / dlls / dsound / primary.c~
blob322cc8bbbc45f2fee473190a7953615154425c61
1 /* DirectSound COM interface
2  *
3  * Copyright 2009 Maarten Lankhorst
4  *
5  * Some code taken from the original dsound-openal implementation
6  *    Copyright 2007-2009 Chris Robinson
7  *
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.
12  *
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.
17  *
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
21  */
23 #include <stdarg.h>
25 #ifdef __WINESRC__
27 #define COBJMACROS
28 #define NONAMELESSSTRUCT
29 #define NONAMELESSUNION
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winuser.h"
33 #include "winnls.h"
34 #include "winreg.h"
35 #include "vfwmsgs.h"
36 #include "mmsystem.h"
37 #include "winternl.h"
38 #include "mmddk.h"
39 #include "wine/debug.h"
40 #include "dsound.h"
42 #include "dsound_private.h"
44 #include "mmreg.h"
45 #include "ks.h"
46 #include "ksmedia.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
50 #else
52 #define WINVER 0x0600
53 #include <windows.h>
54 #include <dsound.h>
56 #include "dsound_private.h"
58 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
59 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
61 #ifndef E_PROP_ID_UNSUPPORTED
62 #define E_PROP_ID_UNSUPPORTED            ((HRESULT)0x80070490)
63 #endif
65 #endif
67 static const IDirectSoundBufferVtbl DS8Primary_Vtbl;
68 static const IDirectSound3DListenerVtbl DS8Primary3D_Vtbl;
69 static const IKsPropertySetVtbl DS8PrimaryProp_Vtbl;
72 static inline DS8Primary *impl_from_IDirectSoundBuffer(IDirectSoundBuffer *iface)
74     return CONTAINING_RECORD(iface, DS8Primary, IDirectSoundBuffer_iface);
77 static inline DS8Primary *impl_from_IDirectSound3DListener(IDirectSound3DListener *iface)
79     return CONTAINING_RECORD(iface, DS8Primary, IDirectSound3DListener_iface);
82 static inline DS8Primary *impl_from_IKsPropertySet(IKsPropertySet *iface)
84     return CONTAINING_RECORD(iface, DS8Primary, IKsPropertySet_iface);
88 static void AL_APIENTRY wrap_DeferUpdates(void)
89 { alcSuspendContext(alcGetCurrentContext()); }
90 static void AL_APIENTRY wrap_ProcessUpdates(void)
91 { alcProcessContext(alcGetCurrentContext()); }
94 static void trigger_elapsed_notifies(DS8Buffer *buf, DWORD lastpos, DWORD curpos)
96     DWORD i;
97     for(i = 0; i < buf->nnotify; ++i)
98     {
99         DSBPOSITIONNOTIFY *not = &buf->notify[i];
100         HANDLE event = not->hEventNotify;
101         DWORD ofs = not->dwOffset;
103         if(ofs == (DWORD)DSBPN_OFFSETSTOP)
104             continue;
106         /* Wraparound case */
107         if(curpos < lastpos)
108         {
109             if(ofs < curpos || ofs >= lastpos)
110             {
111                 TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
112                 SetEvent(event);
113             }
114             continue;
115         }
117         /* Normal case */
118         if(ofs >= lastpos && ofs < curpos)
119         {
120             TRACE("Triggering notification %"LONGFMT"u (%"LONGFMT"u) from buffer %p\n", i, ofs, buf);
121             SetEvent(event);
122         }
123     }
126 static void trigger_stop_notifies(DS8Buffer *buf)
128     DWORD i;
129     for(i = 0; i < buf->nnotify; ++i)
130     {
131         DSBPOSITIONNOTIFY *not = &buf->notify[i];
132         if(not->dwOffset == (DWORD)DSBPN_OFFSETSTOP)
133         {
134             TRACE("Triggering notification %"LONGFMT"u from buffer %p\n", i, buf);
135             SetEvent(not->hEventNotify);
136         }
137     }
140 static DWORD CALLBACK DS8Primary_thread(void *dwUser)
142     DS8Primary *prim = (DS8Primary*)dwUser;
143     DWORD i, active_notifies;
144     MSG msg;
146     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
148     TRACE("Primary buffer (%p) message loop start\n", prim);
149     while(GetMessageA(&msg, NULL, 0, 0))
150     {
151         if(msg.message != WM_USER)
152             continue;
154         EnterCriticalSection(prim->crst);
155         setALContext(prim->ctx);
157         for(i = 0;i < prim->nnotifies;)
158         {
159             DS8Buffer *buf = prim->notifies[i];
160             IDirectSoundBuffer8 *dsb = &buf->IDirectSoundBuffer8_iface;
161             DWORD status=0, curpos=buf->lastpos;
163             IDirectSoundBuffer8_GetStatus(dsb, &status);
164             IDirectSoundBuffer8_GetCurrentPosition(dsb, &curpos, NULL);
165             if(buf->lastpos != curpos)
166             {
167                 trigger_elapsed_notifies(buf, buf->lastpos, curpos);
168                 buf->lastpos = curpos;
169             }
170             if(!(status&DSBSTATUS_PLAYING))
171             {
172                 /* Remove this buffer from list and put another at the
173                  * current position; don't increment i
174                  */
175                 trigger_stop_notifies(buf);
176                 prim->notifies[i] = prim->notifies[--prim->nnotifies];
177                 continue;
178             }
179             i++;
180         }
181         active_notifies = i;
183         /* OpenAL doesn't support our lovely buffer extensions
184          * so just make sure enough buffers are queued
185          */
186         if(!prim->SupportedExt[SOFT_BUFFER_SAMPLES] &&
187            !prim->SupportedExt[SOFT_BUFFER_SUB_DATA] &&
188            !prim->SupportedExt[EXT_STATIC_BUFFER])
189         {
190             for(i = 0;i < prim->nbuffers;++i)
191             {
192                 DS8Buffer *buf = prim->buffers[i];
193                 ALint done = 0, queued = QBUFFERS, state = AL_PLAYING;
194                 ALuint which, ofs;
196                 if(buf->buffer->numsegs == 1 || !buf->isplaying)
197                     continue;
199                 alGetSourcei(buf->source, AL_SOURCE_STATE, &state);
200                 alGetSourcei(buf->source, AL_BUFFERS_QUEUED, &queued);
201                 alGetSourcei(buf->source, AL_BUFFERS_PROCESSED, &done);
203                 queued -= done;
204                 while(done--)
205                     alSourceUnqueueBuffers(buf->source, 1, &which);
206                 while(queued < QBUFFERS)
207                 {
208                     which = buf->buffer->buffers[buf->curidx];
209                     ofs = buf->curidx*buf->buffer->segsize;
210                     if(buf->curidx < buf->buffer->numsegs-1)
211                         alBufferData(which, buf->buffer->buf_format,
212                                      buf->buffer->data + ofs, buf->buffer->segsize,
213                                      buf->buffer->format.Format.nSamplesPerSec);
214                     else
215                         alBufferData(which, buf->buffer->buf_format,
216                                      buf->buffer->data + ofs, buf->buffer->lastsegsize,
217                                      buf->buffer->format.Format.nSamplesPerSec);
219                     alSourceQueueBuffers(buf->source, 1, &which);
220                     buf->curidx = (buf->curidx+1)%buf->buffer->numsegs;
221                     queued++;
223                     if(!buf->curidx && !buf->islooping)
224                     {
225                         buf->isplaying = FALSE;
226                         break;
227                     }
228                 }
229                 if(state != AL_PLAYING)
230                 {
231                     if(!queued)
232                     {
233                         IDirectSoundBuffer8_Stop(&buf->IDirectSoundBuffer8_iface);
234                         continue;
235                     }
236                     alSourcePlay(buf->source);
237                 }
238             }
239             checkALError();
240         }
241         else if(active_notifies == 0 && prim->timer_id)
242         {
243             TRACE("No more notifies, killing timer\n");
244             timeKillEvent(prim->timer_id);
245             prim->timer_id = 0;
246             timeEndPeriod(prim->timer_res);
247         }
249         popALContext();
250         LeaveCriticalSection(prim->crst);
251     }
252     TRACE("Primary buffer (%p) message loop quit\n", prim);
254     if(prim->timer_id)
255     {
256         timeKillEvent(prim->timer_id);
257         prim->timer_id = 0;
258         timeEndPeriod(prim->timer_res);
259         TRACE("Killed timer\n");
260     }
262     return 0;
265 static void CALLBACK DS8Primary_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
266                                       DWORD_PTR dw1, DWORD_PTR dw2)
268     (void)timerID;
269     (void)msg;
270     (void)dw1;
271     (void)dw2;
272     PostThreadMessageA(dwUser, WM_USER, 0, 0);
275 void DS8Primary_starttimer(DS8Primary *prim)
277     DWORD triggertime, res = DS_TIME_RES;
278     ALint refresh = FAKE_REFRESH_COUNT;
279     TIMECAPS time;
281     if(prim->timer_id)
282         return;
284     timeGetDevCaps(&time, sizeof(TIMECAPS));
286     alcGetIntegerv(prim->parent->device, ALC_REFRESH, 1, &refresh);
287     checkALCError(prim->parent->device);
289     triggertime = 1000 / refresh / 2;
290     if(triggertime < time.wPeriodMin)
291         triggertime = time.wPeriodMin;
292     TRACE("Calling timer every %"LONGFMT"u ms for %i refreshes per second\n", triggertime, refresh);
294     if (res < time.wPeriodMin)
295         res = time.wPeriodMin;
296     if (timeBeginPeriod(res) == TIMERR_NOCANDO)
297         WARN("Could not set minimum resolution, don't expect sound\n");
299     prim->timer_res = res;
300     prim->timer_id = timeSetEvent(triggertime, res, DS8Primary_timer, prim->thread_id, TIME_PERIODIC|TIME_KILL_SYNCHRONOUS);
305 HRESULT DS8Primary_PreInit(DS8Primary *This, DS8Impl *parent)
307     DS3DLISTENER *listener;
308     WAVEFORMATEX *wfx;
309     HRESULT hr;
311     This->IDirectSoundBuffer_iface.lpVtbl = (IDirectSoundBufferVtbl*)&DS8Primary_Vtbl;
312     This->IDirectSound3DListener_iface.lpVtbl = (IDirectSound3DListenerVtbl*)&DS8Primary3D_Vtbl;
313     This->IKsPropertySet_iface.lpVtbl = (IKsPropertySetVtbl*)&DS8PrimaryProp_Vtbl;
315     This->parent = parent;
316     This->crst = &parent->share->crst;
317     This->ctx = parent->share->ctx;
318     This->SupportedExt = parent->share->SupportedExt;
319     This->ExtAL = &parent->share->ExtAL;
320     This->sources = parent->share->sources;
321     This->auxslot = parent->share->auxslot;
323     /* Allocate enough for a WAVEFORMATEXTENSIBLE */
324     wfx = &This->format.Format;
326     wfx->wFormatTag = WAVE_FORMAT_PCM;
327     wfx->nChannels = 2;
328     wfx->wBitsPerSample = 8;
329     wfx->nSamplesPerSec = 22050;
330     wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
331     wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
332     wfx->cbSize = 0;
334     This->stopped = TRUE;
336     /* Apparently primary buffer size is always 32k,
337      * tested on windows with 192k 24 bits sound @ 6 channels
338      * where it will run out in 60 ms and it isn't pointer aligned
339      */
340     This->buf_size = 32768;
342     if(This->SupportedExt[SOFT_DEFERRED_UPDATES])
343     {
344         This->DeferUpdates = This->ExtAL->DeferUpdatesSOFT;
345         This->ProcessUpdates = This->ExtAL->ProcessUpdatesSOFT;
346     }
347     else
348     {
349         This->DeferUpdates = wrap_DeferUpdates;
350         This->ProcessUpdates = wrap_ProcessUpdates;
351     }
353     This->eax_prop = EnvironmentDefaults[EAX_ENVIRONMENT_GENERIC];
354     if(This->SupportedExt[EXT_EFX] && This->auxslot != 0)
355     {
356         ALint revid = alGetEnumValue("AL_EFFECT_REVERB");
357         if(revid != 0 && revid != -1)
358         {
359             This->ExtAL->GenEffects(1, &This->effect);
360             This->ExtAL->Effecti(This->effect, AL_EFFECT_TYPE, AL_EFFECT_REVERB);
361             checkALError();
362         }
363     }
365     /* Make sure DS3DListener defaults are applied to OpenAL */
366     listener = &This->params;
367     listener->dwSize = sizeof(This->params);
368     listener->vPosition.x = 0.0;
369     listener->vPosition.y = 0.0;
370     listener->vPosition.z = 0.0;
371     listener->vVelocity.x = 0.0;
372     listener->vVelocity.y = 0.0;
373     listener->vVelocity.z = 0.0;
374     listener->vOrientFront.x = 0.0;
375     listener->vOrientFront.y = 0.0;
376     listener->vOrientFront.z = 1.0;
377     listener->vOrientTop.x = 0.0;
378     listener->vOrientTop.y = 1.0;
379     listener->vOrientTop.z = 0.0;
380     listener->flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
381     listener->flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
382     listener->flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
383     hr = IDirectSound3DListener_SetAllParameters(&This->IDirectSound3DListener_iface, listener, DS3D_IMMEDIATE);
384     if(FAILED(hr))
385         ERR("Could not set 3d parameters: %08"LONGFMT"x\n", hr);
387     This->sizenotifies = This->sizebuffers = parent->share->max_sources;
389     hr = DSERR_OUTOFMEMORY;
390     This->buffers = HeapAlloc(GetProcessHeap(), 0, This->sizebuffers*sizeof(*This->buffers));
391     This->notifies = HeapAlloc(GetProcessHeap(), 0, This->sizenotifies*sizeof(*This->notifies));
392     if(!This->buffers || !This->notifies)
393         goto fail;
395     This->thread_hdl = CreateThread(NULL, 0, DS8Primary_thread, This, 0, &This->thread_id);
396     if(This->thread_hdl == NULL)
397         goto fail;
399     return S_OK;
401 fail:
402     DS8Primary_Clear(This);
403     return hr;
406 void DS8Primary_Clear(DS8Primary *This)
408     TRACE("Clearing primary %p\n", This);
410     if(!This->parent)
411         return;
413     if(This->thread_hdl)
414     {
415         PostThreadMessageA(This->thread_id, WM_QUIT, 0, 0);
416         if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
417         {
418             /* HACK: Apparently, if the device is initialized (thus the primary
419              * buffer has PreInit called) then immediately deleted (the primary
420              * buffer has Clear called), the WM_QUIT message gets sent before
421              * the thread has a chance to run which apparently prevents it from
422              * receiving the message.
423              * If the wait attempt fails, try sending the message again. */
424             PostThreadMessageA(This->thread_id, WM_QUIT, 0, 0);
425             if(WaitForSingleObject(This->thread_hdl, 1000) != WAIT_OBJECT_0)
426                 ERR("Thread wait timed out\n");
427         }
428         CloseHandle(This->thread_hdl);
429     }
431     setALContext(This->ctx);
432     if(This->effect)
433         This->ExtAL->DeleteEffects(1, &This->effect);
434     popALContext();
435     while(This->nbuffers--)
436         DS8Buffer_Destroy(This->buffers[This->nbuffers]);
438     HeapFree(GetProcessHeap(), 0, This->notifies);
439     HeapFree(GetProcessHeap(), 0, This->buffers);
440     memset(This, 0, sizeof(*This));
443 static HRESULT WINAPI DS8Primary_QueryInterface(IDirectSoundBuffer *iface, REFIID riid, LPVOID *ppv)
445     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
447     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
449     *ppv = NULL;
450     if(IsEqualIID(riid, &IID_IUnknown) ||
451        IsEqualIID(riid, &IID_IDirectSoundBuffer))
452         *ppv = &This->IDirectSoundBuffer_iface;
453     else if(IsEqualIID(riid, &IID_IDirectSound3DListener))
454     {
455         if((This->flags&DSBCAPS_CTRL3D))
456             *ppv = &This->IDirectSound3DListener_iface;
457     }
458     else
459         FIXME("Unhandled GUID: %s\n", debugstr_guid(riid));
461     if(*ppv)
462     {
463         IUnknown_AddRef((IUnknown*)*ppv);
464         return S_OK;
465     }
467     return E_NOINTERFACE;
470 static ULONG WINAPI DS8Primary_AddRef(IDirectSoundBuffer *iface)
472     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
473     LONG ret;
475     ret = InterlockedIncrement(&This->ref);
476     if(ret == 1) This->flags = 0;
478     return ret;
481 static ULONG WINAPI DS8Primary_Release(IDirectSoundBuffer *iface)
483     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
484     LONG ret;
486     ret = InterlockedDecrement(&This->ref);
488     return ret;
491 static HRESULT WINAPI DS8Primary_GetCaps(IDirectSoundBuffer *iface, DSBCAPS *caps)
493     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
495     TRACE("(%p)->(%p)\n", iface, caps);
497     if(!caps || caps->dwSize < sizeof(*caps))
498     {
499         WARN("Invalid DSBCAPS (%p, %"LONGFMT"u)\n", caps, caps ? caps->dwSize : 0);
500         return DSERR_INVALIDPARAM;
501     }
503     caps->dwFlags = This->flags;
504     caps->dwBufferBytes = This->buf_size;
505     caps->dwUnlockTransferRate = 0;
506     caps->dwPlayCpuOverhead = 0;
508     return DS_OK;
511 static HRESULT WINAPI DS8Primary_GetCurrentPosition(IDirectSoundBuffer *iface, DWORD *playpos, DWORD *curpos)
513     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
514     HRESULT hr = DSERR_PRIOLEVELNEEDED;
516     EnterCriticalSection(This->crst);
517     if(This->write_emu)
518         hr = IDirectSoundBuffer8_GetCurrentPosition(This->write_emu, playpos, curpos);
519     LeaveCriticalSection(This->crst);
521     return hr;
524 static HRESULT WINAPI DS8Primary_GetFormat(IDirectSoundBuffer *iface, WAVEFORMATEX *wfx, DWORD allocated, DWORD *written)
526     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
527     HRESULT hr = S_OK;
528     UINT size;
530     if(!wfx && !written)
531     {
532         WARN("Cannot report format or format size\n");
533         return DSERR_INVALIDPARAM;
534     }
536     EnterCriticalSection(This->crst);
537     size = sizeof(This->format.Format) + This->format.Format.cbSize;
538     if(written)
539         *written = size;
540     if(wfx)
541     {
542         if(allocated < size)
543             hr = DSERR_INVALIDPARAM;
544         else
545             memcpy(wfx, &This->format.Format, size);
546     }
547     LeaveCriticalSection(This->crst);
549     return hr;
552 static HRESULT WINAPI DS8Primary_GetVolume(IDirectSoundBuffer *iface, LONG *volume)
554     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
555     ALfloat gain;
557     TRACE("(%p)->(%p)\n", iface, volume);
559     if(!volume)
560         return DSERR_INVALIDPARAM;
561     *volume = 0;
563     if(!(This->flags&DSBCAPS_CTRLVOLUME))
564         return DSERR_CONTROLUNAVAIL;
566     setALContext(This->ctx);
567     alGetListenerf(AL_GAIN, &gain);
568     checkALError();
569     popALContext();
571     *volume = clampI(gain_to_mB(gain), DSBVOLUME_MIN, DSBVOLUME_MAX);
572     return DS_OK;
575 static HRESULT WINAPI DS8Primary_GetPan(IDirectSoundBuffer *iface, LONG *pan)
577     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
578     HRESULT hr = DS_OK;
580     WARN("(%p)->(%p): semi-stub\n", iface, pan);
582     if(!pan)
583         return DSERR_INVALIDPARAM;
585     EnterCriticalSection(This->crst);
586     if(This->write_emu)
587         hr = IDirectSoundBuffer8_GetPan(This->write_emu, pan);
588     else if(!(This->flags & DSBCAPS_CTRLPAN))
589         hr = DSERR_CONTROLUNAVAIL;
590     else
591         *pan = 0;
592     LeaveCriticalSection(This->crst);
594     return hr;
597 static HRESULT WINAPI DS8Primary_GetFrequency(IDirectSoundBuffer *iface, DWORD *freq)
599     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
600     HRESULT hr = DS_OK;
602     WARN("(%p)->(%p): semi-stub\n", iface, freq);
604     if(!freq)
605         return DSERR_INVALIDPARAM;
607     if(!(This->flags&DSBCAPS_CTRLFREQUENCY))
608         return DSERR_CONTROLUNAVAIL;
610     EnterCriticalSection(This->crst);
611     *freq = This->format.Format.nSamplesPerSec;
612     LeaveCriticalSection(This->crst);
614     return hr;
617 static HRESULT WINAPI DS8Primary_GetStatus(IDirectSoundBuffer *iface, DWORD *status)
619     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
621     TRACE("(%p)->(%p)\n", iface, status);
623     if(!status)
624         return DSERR_INVALIDPARAM;
626     EnterCriticalSection(This->crst);
627     *status = DSBSTATUS_PLAYING|DSBSTATUS_LOOPING;
628     if((This->flags&DSBCAPS_LOCDEFER))
629         *status |= DSBSTATUS_LOCHARDWARE;
631     if(This->stopped)
632     {
633         DWORD i, state;
634         HRESULT hr;
636         for(i = 0;i < This->nbuffers;++i)
637         {
638             hr = IDirectSoundBuffer8_GetStatus(&This->buffers[i]->IDirectSoundBuffer8_iface, &state);
639             if(SUCCEEDED(hr) && (state&DSBSTATUS_PLAYING))
640                 break;
641         }
642         if(i == This->nbuffers)
643         {
644             /* Primary stopped and no buffers playing.. */
645             *status = 0;
646         }
647     }
648     LeaveCriticalSection(This->crst);
650     return DS_OK;
653 static HRESULT WINAPI DS8Primary_Initialize(IDirectSoundBuffer *iface, IDirectSound *ds, const DSBUFFERDESC *desc)
655     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
656     HRESULT hr;
658     TRACE("(%p)->(%p, %p)\n", iface, ds, desc);
660     if(!desc || desc->lpwfxFormat || desc->dwBufferBytes)
661     {
662         WARN("Bad DSBDESC for primary buffer\n");
663         return DSERR_INVALIDPARAM;
664     }
665     if((desc->dwFlags&DSBCAPS_CTRLFX) ||
666        (desc->dwFlags&DSBCAPS_CTRLPOSITIONNOTIFY) ||
667        (desc->dwFlags&DSBCAPS_LOCSOFTWARE))
668     {
669         WARN("Bad dwFlags %08"LONGFMT"x\n", desc->dwFlags);
670         return DSERR_INVALIDPARAM;
671     }
673     /* Should be 0 if not initialized */
674     if(This->flags)
675         return DSERR_ALREADYINITIALIZED;
677     hr = DS_OK;
678     if(This->parent->prio_level == DSSCL_WRITEPRIMARY)
679     {
680         DSBUFFERDESC emudesc;
681         DS8Buffer *emu;
683         if(This->write_emu)
684         {
685             ERR("There shouldn't be a write_emu!\n");
686             IDirectSoundBuffer8_Release(This->write_emu);
687             This->write_emu = NULL;
688         }
690         memset(&emudesc, 0, sizeof(emudesc));
691         emudesc.dwSize = sizeof(emudesc);
692         emudesc.dwFlags = DSBCAPS_LOCHARDWARE | (desc->dwFlags&DSBCAPS_CTRLPAN);
693         /* Dont play last incomplete sample */
694         emudesc.dwBufferBytes = This->buf_size - (This->buf_size%This->format.Format.nBlockAlign);
695         emudesc.lpwfxFormat = &This->format.Format;
697         hr = DS8Buffer_Create(&emu, This, NULL);
698         if(SUCCEEDED(hr))
699         {
700             This->write_emu = &emu->IDirectSoundBuffer8_iface;
701             hr = IDirectSoundBuffer8_Initialize(This->write_emu, ds, &emudesc);
702             if(FAILED(hr))
703             {
704                 IDirectSoundBuffer8_Release(This->write_emu);
705                 This->write_emu = NULL;
706             }
707         }
708     }
710     if(SUCCEEDED(hr))
711         This->flags = desc->dwFlags | DSBCAPS_LOCHARDWARE;
712     return hr;
715 static HRESULT WINAPI DS8Primary_Lock(IDirectSoundBuffer *iface, DWORD ofs, DWORD bytes, void **ptr1, DWORD *len1, void **ptr2, DWORD *len2, DWORD flags)
717     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
718     HRESULT hr = DSERR_PRIOLEVELNEEDED;
720     TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %p, %p, %p, %p, %"LONGFMT"u)\n", iface, ofs, bytes, ptr1, len1, ptr2, len2, flags);
722     EnterCriticalSection(This->crst);
723     if(This->write_emu)
724         hr = IDirectSoundBuffer8_Lock(This->write_emu, ofs, bytes, ptr1, len1, ptr2, len2, flags);
725     LeaveCriticalSection(This->crst);
727     return hr;
730 static HRESULT WINAPI DS8Primary_Play(IDirectSoundBuffer *iface, DWORD res1, DWORD res2, DWORD flags)
732     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
733     HRESULT hr;
735     TRACE("(%p)->(%"LONGFMT"u, %"LONGFMT"u, %"LONGFMT"u)\n", iface, res1, res2, flags);
737     if(!(flags & DSBPLAY_LOOPING))
738     {
739         WARN("Flags (%08"LONGFMT"x) not set to DSBPLAY_LOOPING\n", flags);
740         return DSERR_INVALIDPARAM;
741     }
743     EnterCriticalSection(This->crst);
744     hr = S_OK;
745     if(This->write_emu)
746         hr = IDirectSoundBuffer8_Play(This->write_emu, res1, res2, flags);
747     if(SUCCEEDED(hr))
748         This->stopped = FALSE;
749     LeaveCriticalSection(This->crst);
751     return hr;
754 static HRESULT WINAPI DS8Primary_SetCurrentPosition(IDirectSoundBuffer *iface, DWORD pos)
756     WARN("(%p)->(%"LONGFMT"u)\n", iface, pos);
757     return DSERR_INVALIDCALL;
760 /* Just assume the format is crap, and clean up the damage */
761 static void copy_waveformat(WAVEFORMATEX *wfx, const WAVEFORMATEX *from)
763     if(from->wFormatTag == WAVE_FORMAT_PCM)
764     {
765         wfx->cbSize = 0;
766         if(from->wBitsPerSample == 8 ||
767            from->wBitsPerSample == 16 ||
768            from->wBitsPerSample == 24 ||
769            from->wBitsPerSample == 32)
770             wfx->wBitsPerSample = from->wBitsPerSample;
771     }
772     else if(from->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
773     {
774         WAVEFORMATEXTENSIBLE *wfe = (WAVEFORMATEXTENSIBLE*)wfx;
775         const WAVEFORMATEXTENSIBLE *fromx = (const WAVEFORMATEXTENSIBLE*)from;
776         DWORD size = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
778         /* Fail silently.. */
779         if(from->cbSize < size)
780             return;
781         if(!fromx->Samples.wValidBitsPerSample &&
782            !fromx->Format.wBitsPerSample)
783             return;
785         if(!IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) &&
786            !IsEqualGUID(&wfe->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
787         {
788             ERR("Unhandled extensible format: %s\n", debugstr_guid(&wfe->SubFormat));
789             return;
790         }
792         wfe->Format.wBitsPerSample = from->wBitsPerSample;
793         wfe->Samples.wValidBitsPerSample = fromx->Samples.wValidBitsPerSample;
794         if(!wfe->Samples.wValidBitsPerSample)
795             wfe->Samples.wValidBitsPerSample = wfe->Format.wBitsPerSample;
796         wfe->Format.cbSize = size;
797         wfe->dwChannelMask = fromx->dwChannelMask;
798         wfe->SubFormat = fromx->SubFormat;
799     }
800     else
801     {
802         ERR("Unhandled format tag %04x\n", from->wFormatTag);
803         return;
804     }
806     if(from->nChannels)
807         wfx->nChannels = from->nChannels;
808     wfx->wFormatTag = from->wFormatTag;
809     if(from->nSamplesPerSec >= DSBFREQUENCY_MIN &&
810        from->nSamplesPerSec <= DSBFREQUENCY_MAX)
811         wfx->nSamplesPerSec = from->nSamplesPerSec;
812     wfx->nBlockAlign = wfx->wBitsPerSample * wfx->nChannels / 8;
813     wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
816 static HRESULT WINAPI DS8Primary_SetFormat(IDirectSoundBuffer *iface, const WAVEFORMATEX *wfx)
818     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
819     HRESULT hr = S_OK;
820     ALCint freq;
822     TRACE("(%p)->(%p)\n", iface, wfx);
824     if(!wfx)
825     {
826         WARN("Missing format\n");
827         return DSERR_INVALIDPARAM;
828     }
830     EnterCriticalSection(This->crst);
832     if(This->parent->prio_level < DSSCL_PRIORITY)
833     {
834         hr = DSERR_PRIOLEVELNEEDED;
835         goto out;
836     }
838     TRACE("Requested primary format:\n"
839           "    FormatTag      = %04x\n"
840           "    Channels       = %u\n"
841           "    SamplesPerSec  = %"LONGFMT"u\n"
842           "    AvgBytesPerSec = %"LONGFMT"u\n"
843           "    BlockAlign     = %u\n"
844           "    BitsPerSample  = %u\n",
845           wfx->wFormatTag, wfx->nChannels,
846           wfx->nSamplesPerSec, wfx->nAvgBytesPerSec,
847           wfx->nBlockAlign, wfx->wBitsPerSample);
849     copy_waveformat(&This->format.Format, wfx);
851     freq = This->format.Format.nSamplesPerSec;
852     alcGetIntegerv(This->parent->device, ALC_FREQUENCY, 1, &freq);
853     checkALCError(This->parent->device);
855     This->format.Format.nSamplesPerSec = freq;
856     This->format.Format.nAvgBytesPerSec = This->format.Format.nBlockAlign *
857                                           This->format.Format.nSamplesPerSec;
859     if(This->write_emu)
860     {
861         DS8Buffer *buf;
862         DSBUFFERDESC desc;
864         memset(&desc, 0, sizeof(desc));
865         desc.dwSize = sizeof(desc);
866         desc.dwFlags = DSBCAPS_LOCHARDWARE|DSBCAPS_CTRLPAN;
867         desc.dwBufferBytes = This->buf_size - (This->buf_size % This->format.Format.nBlockAlign);
868         desc.lpwfxFormat = &This->format.Format;
870         hr = DS8Buffer_Create(&buf, This, NULL);
871         if(FAILED(hr))
872             goto out;
874         hr = IDirectSoundBuffer8_Initialize(&buf->IDirectSoundBuffer8_iface, &This->parent->IDirectSound_iface, &desc);
875         if(FAILED(hr))
876             DS8Buffer_Destroy(buf);
877         else
878         {
879             IDirectSoundBuffer8_Release(This->write_emu);
880             This->write_emu = &buf->IDirectSoundBuffer8_iface;
881         }
882     }
884 out:
885     LeaveCriticalSection(This->crst);
886     return hr;
889 static HRESULT WINAPI DS8Primary_SetVolume(IDirectSoundBuffer *iface, LONG vol)
891     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
893     TRACE("(%p)->(%"LONGFMT"d)\n", iface, vol);
895     if(vol > DSBVOLUME_MAX || vol < DSBVOLUME_MIN)
896     {
897         WARN("Invalid volume (%"LONGFMT"d)\n", vol);
898         return DSERR_INVALIDPARAM;
899     }
901     if(!(This->flags&DSBCAPS_CTRLVOLUME))
902         return DSERR_CONTROLUNAVAIL;
904     setALContext(This->ctx);
905     alListenerf(AL_GAIN, mB_to_gain(vol));
906     popALContext();
908     return DS_OK;
911 static HRESULT WINAPI DS8Primary_SetPan(IDirectSoundBuffer *iface, LONG pan)
913     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
914     HRESULT hr;
916     TRACE("(%p)->(%"LONGFMT"d)\n", iface, pan);
918     if(pan > DSBPAN_RIGHT || pan < DSBPAN_LEFT)
919     {
920         WARN("invalid parameter: pan = %"LONGFMT"d\n", pan);
921         return DSERR_INVALIDPARAM;
922     }
924     EnterCriticalSection(This->crst);
925     if(!(This->flags&DSBCAPS_CTRLPAN))
926     {
927         WARN("control unavailable\n");
928         hr = DSERR_CONTROLUNAVAIL;
929     }
930     else if(This->write_emu)
931         hr = IDirectSoundBuffer8_SetPan(This->write_emu, pan);
932     else
933     {
934         FIXME("Not supported\n");
935         hr = E_NOTIMPL;
936     }
937     LeaveCriticalSection(This->crst);
939     return hr;
942 static HRESULT WINAPI DS8Primary_SetFrequency(IDirectSoundBuffer *iface, DWORD freq)
944     WARN("(%p)->(%"LONGFMT"u)\n", iface, freq);
945     return DSERR_CONTROLUNAVAIL;
948 static HRESULT WINAPI DS8Primary_Stop(IDirectSoundBuffer *iface)
950     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
951     HRESULT hr = S_OK;
953     TRACE("(%p)->()\n", iface);
955     EnterCriticalSection(This->crst);
956     if(This->write_emu)
957         hr = IDirectSoundBuffer8_Stop(This->write_emu);
958     if(SUCCEEDED(hr))
959         This->stopped = TRUE;
960     LeaveCriticalSection(This->crst);
962     return hr;
965 static HRESULT WINAPI DS8Primary_Unlock(IDirectSoundBuffer *iface, void *ptr1, DWORD len1, void *ptr2, DWORD len2)
967     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
968     HRESULT hr = DSERR_INVALIDCALL;
970     TRACE("(%p)->(%p, %"LONGFMT"u, %p, %"LONGFMT"u)\n", iface, ptr1, len1, ptr2, len2);
972     EnterCriticalSection(This->crst);
973     if(This->write_emu)
974         hr = IDirectSoundBuffer8_Unlock(This->write_emu, ptr1, len1, ptr2, len2);
975     LeaveCriticalSection(This->crst);
977     return hr;
980 static HRESULT WINAPI DS8Primary_Restore(IDirectSoundBuffer *iface)
982     DS8Primary *This = impl_from_IDirectSoundBuffer(iface);
983     HRESULT hr = S_OK;
985     TRACE("(%p)->()\n", iface);
987     EnterCriticalSection(This->crst);
988     if(This->write_emu)
989         hr = IDirectSoundBuffer8_Restore(This->write_emu);
990     LeaveCriticalSection(This->crst);
992     return hr;
995 static const IDirectSoundBufferVtbl DS8Primary_Vtbl =
997     DS8Primary_QueryInterface,
998     DS8Primary_AddRef,
999     DS8Primary_Release,
1000     DS8Primary_GetCaps,
1001     DS8Primary_GetCurrentPosition,
1002     DS8Primary_GetFormat,
1003     DS8Primary_GetVolume,
1004     DS8Primary_GetPan,
1005     DS8Primary_GetFrequency,
1006     DS8Primary_GetStatus,
1007     DS8Primary_Initialize,
1008     DS8Primary_Lock,
1009     DS8Primary_Play,
1010     DS8Primary_SetCurrentPosition,
1011     DS8Primary_SetFormat,
1012     DS8Primary_SetVolume,
1013     DS8Primary_SetPan,
1014     DS8Primary_SetFrequency,
1015     DS8Primary_Stop,
1016     DS8Primary_Unlock,
1017     DS8Primary_Restore
1021 static void DS8Primary_SetParams(DS8Primary *This, const DS3DLISTENER *params, LONG flags)
1023     union PrimaryParamFlags dirty = { flags };
1024     DWORD i;
1026     if(dirty.bit.pos)
1027         alListener3f(AL_POSITION, params->vPosition.x, params->vPosition.y,
1028                                  -params->vPosition.z);
1029     if(dirty.bit.vel)
1030         alListener3f(AL_VELOCITY, params->vVelocity.x, params->vVelocity.y,
1031                                  -params->vVelocity.z);
1032     if(dirty.bit.orientation)
1033     {
1034         ALfloat orient[6] = {
1035             params->vOrientFront.x, params->vOrientFront.y, -params->vOrientFront.z,
1036             params->vOrientTop.x, params->vOrientTop.y, -params->vOrientTop.z
1037         };
1038         alListenerfv(AL_ORIENTATION, orient);
1039     }
1040     if(dirty.bit.distancefactor)
1041     {
1042         alSpeedOfSound(343.3f/params->flDistanceFactor);
1043         if(This->SupportedExt[EXT_EFX])
1044             alListenerf(AL_METERS_PER_UNIT, params->flDistanceFactor);
1045     }
1046     if(dirty.bit.rollofffactor)
1047     {
1048         ALfloat rolloff = params->flRolloffFactor;
1049         This->rollofffactor = rolloff;
1050         for(i = 0;i < This->nbuffers;++i)
1051         {
1052             const DS8Buffer *buf = This->buffers[i];
1053             if(buf->ds3dmode != DS3DMODE_DISABLE)
1054                 alSourcef(buf->source, AL_ROLLOFF_FACTOR, rolloff);
1055         }
1056     }
1057     if(dirty.bit.dopplerfactor)
1058         alDopplerFactor(params->flDopplerFactor);
1059     if(dirty.bit.effect)
1060         This->ExtAL->AuxiliaryEffectSloti(This->auxslot, AL_EFFECTSLOT_EFFECT, This->effect);
1063 static HRESULT WINAPI DS8Primary3D_QueryInterface(IDirectSound3DListener *iface, REFIID riid, void **ppv)
1065     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1066     return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1069 static ULONG WINAPI DS8Primary3D_AddRef(IDirectSound3DListener *iface)
1071     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1072     LONG ret;
1074     ret = InterlockedIncrement(&This->ds3d_ref);
1075     TRACE("new refcount %"LONGFMT"d\n", ret);
1077     return ret;
1080 static ULONG WINAPI DS8Primary3D_Release(IDirectSound3DListener *iface)
1082     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1083     LONG ret;
1085     ret = InterlockedDecrement(&This->ds3d_ref);
1086     TRACE("new refcount %"LONGFMT"d\n", ret);
1088     return ret;
1092 static HRESULT WINAPI DS8Primary3D_GetAllParameters(IDirectSound3DListener *iface, DS3DLISTENER *listener)
1094     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1096     TRACE("(%p)->(%p)\n", iface, listener);
1098     if(!listener || listener->dwSize < sizeof(*listener))
1099     {
1100         WARN("Invalid DS3DLISTENER %p %"LONGFMT"u\n", listener, listener ? listener->dwSize : 0);
1101         return DSERR_INVALIDPARAM;
1102     }
1104     EnterCriticalSection(This->crst);
1105     setALContext(This->ctx);
1106     IDirectSound3DListener_GetPosition(iface, &listener->vPosition);
1107     IDirectSound3DListener_GetVelocity(iface, &listener->vVelocity);
1108     IDirectSound3DListener_GetOrientation(iface, &listener->vOrientFront, &listener->vOrientTop);
1109     IDirectSound3DListener_GetDistanceFactor(iface, &listener->flDistanceFactor);
1110     IDirectSound3DListener_GetRolloffFactor(iface, &listener->flRolloffFactor);
1111     IDirectSound3DListener_GetDopplerFactor(iface, &listener->flDopplerFactor);
1112     popALContext();
1113     LeaveCriticalSection(This->crst);
1115     return DS_OK;
1118 static HRESULT WINAPI DS8Primary3D_GetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE *distancefactor)
1120     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1122     TRACE("(%p)->(%p)\n", iface, distancefactor);
1124     if(!distancefactor)
1125     {
1126         WARN("Invalid parameter %p\n", distancefactor);
1127         return DSERR_INVALIDPARAM;
1128     }
1130     setALContext(This->ctx);
1131     *distancefactor = 343.3f/alGetFloat(AL_SPEED_OF_SOUND);
1132     checkALError();
1133     popALContext();
1135     return S_OK;
1138 static HRESULT WINAPI DS8Primary3D_GetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE *dopplerfactor)
1140     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1142     TRACE("(%p)->(%p)\n", iface, dopplerfactor);
1144     if(!dopplerfactor)
1145     {
1146         WARN("Invalid parameter %p\n", dopplerfactor);
1147         return DSERR_INVALIDPARAM;
1148     }
1150     setALContext(This->ctx);
1151     *dopplerfactor = alGetFloat(AL_DOPPLER_FACTOR);
1152     checkALError();
1153     popALContext();
1155     return S_OK;
1158 static HRESULT WINAPI DS8Primary3D_GetOrientation(IDirectSound3DListener *iface, D3DVECTOR *front, D3DVECTOR *top)
1160     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1161     ALfloat orient[6];
1163     TRACE("(%p)->(%p, %p)\n", iface, front, top);
1165     if(!front || !top)
1166     {
1167         WARN("Invalid parameter %p %p\n", front, top);
1168         return DSERR_INVALIDPARAM;
1169     }
1171     setALContext(This->ctx);
1172     alGetListenerfv(AL_ORIENTATION, orient);
1173     checkALError();
1174     popALContext();
1176     front->x =  orient[0];
1177     front->y =  orient[1];
1178     front->z = -orient[2];
1179     top->x =  orient[3];
1180     top->y =  orient[4];
1181     top->z = -orient[5];
1182     return S_OK;
1185 static HRESULT WINAPI DS8Primary3D_GetPosition(IDirectSound3DListener *iface, D3DVECTOR *pos)
1187     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1188     ALfloat alpos[3];
1190     TRACE("(%p)->(%p)\n", iface, pos);
1192     if(!pos)
1193     {
1194         WARN("Invalid parameter %p\n", pos);
1195         return DSERR_INVALIDPARAM;
1196     }
1198     setALContext(This->ctx);
1199     alGetListenerfv(AL_POSITION, alpos);
1200     checkALError();
1201     popALContext();
1203     pos->x =  alpos[0];
1204     pos->y =  alpos[1];
1205     pos->z = -alpos[2];
1206     return S_OK;
1209 static HRESULT WINAPI DS8Primary3D_GetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE *rollofffactor)
1211     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1213     TRACE("(%p)->(%p)\n", iface, rollofffactor);
1215     if(!rollofffactor)
1216     {
1217         WARN("Invalid parameter %p\n", rollofffactor);
1218         return DSERR_INVALIDPARAM;
1219     }
1221     EnterCriticalSection(This->crst);
1222     *rollofffactor = This->rollofffactor;
1223     LeaveCriticalSection(This->crst);
1225     return S_OK;
1228 static HRESULT WINAPI DS8Primary3D_GetVelocity(IDirectSound3DListener *iface, D3DVECTOR *velocity)
1230     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1231     ALfloat vel[3];
1233     TRACE("(%p)->(%p)\n", iface, velocity);
1235     if(!velocity)
1236     {
1237         WARN("Invalid parameter %p\n", velocity);
1238         return DSERR_INVALIDPARAM;
1239     }
1241     setALContext(This->ctx);
1242     alGetListenerfv(AL_VELOCITY, vel);
1243     checkALError();
1244     popALContext();
1246     velocity->x =  vel[0];
1247     velocity->y =  vel[1];
1248     velocity->z = -vel[2];
1249     return S_OK;
1252 static HRESULT WINAPI DS8Primary3D_SetAllParameters(IDirectSound3DListener *iface, const DS3DLISTENER *listen, DWORD apply)
1254     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1256     TRACE("(%p)->(%p, %"LONGFMT"u)\n", iface, listen, apply);
1258     if(!listen || listen->dwSize < sizeof(*listen))
1259     {
1260         WARN("Invalid parameter %p %"LONGFMT"u\n", listen, listen ? listen->dwSize : 0);
1261         return DSERR_INVALIDPARAM;
1262     }
1264     if(listen->flDistanceFactor > DS3D_MAXDISTANCEFACTOR ||
1265        listen->flDistanceFactor < DS3D_MINDISTANCEFACTOR)
1266     {
1267         WARN("Invalid distance factor (%f)\n", listen->flDistanceFactor);
1268         return DSERR_INVALIDPARAM;
1269     }
1271     if(listen->flDopplerFactor > DS3D_MAXDOPPLERFACTOR ||
1272        listen->flDopplerFactor < DS3D_MINDOPPLERFACTOR)
1273     {
1274         WARN("Invalid doppler factor (%f)\n", listen->flDopplerFactor);
1275         return DSERR_INVALIDPARAM;
1276     }
1278     if(listen->flRolloffFactor < DS3D_MINROLLOFFFACTOR ||
1279        listen->flRolloffFactor > DS3D_MAXROLLOFFFACTOR)
1280     {
1281         WARN("Invalid rolloff factor (%f)\n", listen->flRolloffFactor);
1282         return DSERR_INVALIDPARAM;
1283     }
1285     if(apply == DS3D_DEFERRED)
1286     {
1287         EnterCriticalSection(This->crst);
1288         This->params = *listen;
1289         This->params.dwSize = sizeof(This->params);
1290         This->dirty.bit.pos = 1;
1291         This->dirty.bit.vel = 1;
1292         This->dirty.bit.orientation = 1;
1293         This->dirty.bit.distancefactor = 1;
1294         This->dirty.bit.rollofffactor = 1;
1295         This->dirty.bit.dopplerfactor = 1;
1296         LeaveCriticalSection(This->crst);
1297     }
1298     else
1299     {
1300         union PrimaryParamFlags dirty = { 0l };
1301         dirty.bit.pos = 1;
1302         dirty.bit.vel = 1;
1303         dirty.bit.orientation = 1;
1304         dirty.bit.distancefactor = 1;
1305         dirty.bit.rollofffactor = 1;
1306         dirty.bit.dopplerfactor = 1;
1308         EnterCriticalSection(This->crst);
1309         setALContext(This->ctx);
1310         DS8Primary_SetParams(This, listen, dirty.flags);
1311         checkALError();
1312         popALContext();
1313         LeaveCriticalSection(This->crst);
1314     }
1316     return S_OK;
1319 static HRESULT WINAPI DS8Primary3D_SetDistanceFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1321     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1323     TRACE("(%p)->(%f, %"LONGFMT"u)\n", iface, factor, apply);
1325     if(factor < DS3D_MINDISTANCEFACTOR ||
1326        factor > DS3D_MAXDISTANCEFACTOR)
1327     {
1328         WARN("Invalid parameter %f\n", factor);
1329         return DSERR_INVALIDPARAM;
1330     }
1332     if(apply == DS3D_DEFERRED)
1333     {
1334         EnterCriticalSection(This->crst);
1335         This->params.flDistanceFactor = factor;
1336         This->dirty.bit.distancefactor = 1;
1337         LeaveCriticalSection(This->crst);
1338     }
1339     else
1340     {
1341         setALContext(This->ctx);
1342         alSpeedOfSound(343.3f/factor);
1343         if(This->SupportedExt[EXT_EFX])
1344             alListenerf(AL_METERS_PER_UNIT, factor);
1345         checkALError();
1346         popALContext();
1347     }
1349     return S_OK;
1352 static HRESULT WINAPI DS8Primary3D_SetDopplerFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1354     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1356     TRACE("(%p)->(%f, %"LONGFMT"u)\n", iface, factor, apply);
1358     if(factor < DS3D_MINDOPPLERFACTOR ||
1359        factor > DS3D_MAXDOPPLERFACTOR)
1360     {
1361         WARN("Invalid parameter %f\n", factor);
1362         return DSERR_INVALIDPARAM;
1363     }
1365     if(apply == DS3D_DEFERRED)
1366     {
1367         EnterCriticalSection(This->crst);
1368         This->params.flDopplerFactor = factor;
1369         This->dirty.bit.dopplerfactor = 1;
1370         LeaveCriticalSection(This->crst);
1371     }
1372     else
1373     {
1374         setALContext(This->ctx);
1375         alDopplerFactor(factor);
1376         checkALError();
1377         popALContext();
1378     }
1380     return S_OK;
1383 static HRESULT WINAPI DS8Primary3D_SetOrientation(IDirectSound3DListener *iface, D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront, D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD apply)
1385     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1387     TRACE("(%p)->(%f, %f, %f, %f, %f, %f, %"LONGFMT"u)\n", iface, xFront, yFront, zFront, xTop, yTop, zTop, apply);
1389     if(apply == DS3D_DEFERRED)
1390     {
1391         EnterCriticalSection(This->crst);
1392         This->params.vOrientFront.x = xFront;
1393         This->params.vOrientFront.y = yFront;
1394         This->params.vOrientFront.z = zFront;
1395         This->params.vOrientTop.x = xTop;
1396         This->params.vOrientTop.y = yTop;
1397         This->params.vOrientTop.z = zTop;
1398         This->dirty.bit.orientation = 1;
1399         LeaveCriticalSection(This->crst);
1400     }
1401     else
1402     {
1403         ALfloat orient[6] = {
1404             xFront, yFront, -zFront,
1405             xTop, yTop, -zTop
1406         };
1407         setALContext(This->ctx);
1408         alListenerfv(AL_ORIENTATION, orient);
1409         checkALError();
1410         popALContext();
1411     }
1413     return S_OK;
1416 static HRESULT WINAPI DS8Primary3D_SetPosition(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1418     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1420     TRACE("(%p)->(%f, %f, %f, %"LONGFMT"u)\n", iface, x, y, z, apply);
1422     if(apply == DS3D_DEFERRED)
1423     {
1424         EnterCriticalSection(This->crst);
1425         This->params.vPosition.x = x;
1426         This->params.vPosition.y = y;
1427         This->params.vPosition.z = z;
1428         This->dirty.bit.pos = 1;
1429         LeaveCriticalSection(This->crst);
1430     }
1431     else
1432     {
1433         setALContext(This->ctx);
1434         alListener3f(AL_POSITION, x, y, -z);
1435         checkALError();
1436         popALContext();
1437     }
1439     return S_OK;
1442 static HRESULT WINAPI DS8Primary3D_SetRolloffFactor(IDirectSound3DListener *iface, D3DVALUE factor, DWORD apply)
1444     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1446     TRACE("(%p)->(%f, %"LONGFMT"u)\n", iface, factor, apply);
1448     if(factor < DS3D_MINROLLOFFFACTOR ||
1449        factor > DS3D_MAXROLLOFFFACTOR)
1450     {
1451         WARN("Invalid parameter %f\n", factor);
1452         return DSERR_INVALIDPARAM;
1453     }
1455     EnterCriticalSection(This->crst);
1456     if(apply == DS3D_DEFERRED)
1457     {
1458         This->params.flRolloffFactor = factor;
1459         This->dirty.bit.rollofffactor = 1;
1460     }
1461     else
1462     {
1463         DWORD i;
1465         setALContext(This->ctx);
1466         for(i = 0;i < This->nbuffers;++i)
1467         {
1468             if(This->buffers[i]->ds3dmode != DS3DMODE_DISABLE)
1469                 alSourcef(This->buffers[i]->source, AL_ROLLOFF_FACTOR, factor);
1470         }
1471         checkALError();
1472         popALContext();
1474         This->rollofffactor = factor;
1475     }
1476     LeaveCriticalSection(This->crst);
1478     return S_OK;
1481 static HRESULT WINAPI DS8Primary3D_SetVelocity(IDirectSound3DListener *iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD apply)
1483     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1485     TRACE("(%p)->(%f, %f, %f, %"LONGFMT"u)\n", iface, x, y, z, apply);
1487     if(apply == DS3D_DEFERRED)
1488     {
1489         EnterCriticalSection(This->crst);
1490         This->params.vVelocity.x = x;
1491         This->params.vVelocity.y = y;
1492         This->params.vVelocity.z = z;
1493         This->dirty.bit.vel = 1;
1494         LeaveCriticalSection(This->crst);
1495     }
1496     else
1497     {
1498         setALContext(This->ctx);
1499         alListener3f(AL_VELOCITY, x, y, -z);
1500         checkALError();
1501         popALContext();
1502     }
1504     return S_OK;
1507 static HRESULT WINAPI DS8Primary3D_CommitDeferredSettings(IDirectSound3DListener *iface)
1509     DS8Primary *This = impl_from_IDirectSound3DListener(iface);
1510     LONG flags;
1511     DWORD i;
1513     EnterCriticalSection(This->crst);
1514     setALContext(This->ctx);
1515     This->DeferUpdates();
1517     if((flags=InterlockedExchange(&This->dirty.flags, 0)) != 0)
1518     {
1519         DS8Primary_SetParams(This, &This->params, flags);
1520         /* checkALError is here for debugging */
1521         checkALError();
1522     }
1523     TRACE("Dirty flags was: 0x%02"LONGFMT"x\n", flags);
1525     for(i = 0;i < This->nbuffers;++i)
1526     {
1527         DS8Buffer *buf = This->buffers[i];
1529         if((flags=InterlockedExchange(&buf->dirty.flags, 0)) != 0)
1530             DS8Buffer_SetParams(buf, &buf->params, flags);
1531     }
1532     checkALError();
1534     This->ProcessUpdates();
1535     popALContext();
1536     LeaveCriticalSection(This->crst);
1538     return DS_OK;
1541 static const IDirectSound3DListenerVtbl DS8Primary3D_Vtbl =
1543     DS8Primary3D_QueryInterface,
1544     DS8Primary3D_AddRef,
1545     DS8Primary3D_Release,
1546     DS8Primary3D_GetAllParameters,
1547     DS8Primary3D_GetDistanceFactor,
1548     DS8Primary3D_GetDopplerFactor,
1549     DS8Primary3D_GetOrientation,
1550     DS8Primary3D_GetPosition,
1551     DS8Primary3D_GetRolloffFactor,
1552     DS8Primary3D_GetVelocity,
1553     DS8Primary3D_SetAllParameters,
1554     DS8Primary3D_SetDistanceFactor,
1555     DS8Primary3D_SetDopplerFactor,
1556     DS8Primary3D_SetOrientation,
1557     DS8Primary3D_SetPosition,
1558     DS8Primary3D_SetRolloffFactor,
1559     DS8Primary3D_SetVelocity,
1560     DS8Primary3D_CommitDeferredSettings
1563 /* NOTE: Although the app handles listener properties through secondary buffers,
1564  * we pass the requests to the primary buffer though a propertyset interface.
1565  * These methods are not exposed to the app. */
1566 static HRESULT WINAPI DS8PrimaryProp_QueryInterface(IKsPropertySet *iface, REFIID riid, void **ppv)
1568     DS8Primary *This = impl_from_IKsPropertySet(iface);
1569     return DS8Primary_QueryInterface(&This->IDirectSoundBuffer_iface, riid, ppv);
1572 static ULONG WINAPI DS8PrimaryProp_AddRef(IKsPropertySet *iface)
1574     DS8Primary *This = impl_from_IKsPropertySet(iface);
1575     LONG ret;
1577     ret = InterlockedIncrement(&This->prop_ref);
1578     TRACE("new refcount %"LONGFMT"d\n", ret);
1580     return ret;
1583 static ULONG WINAPI DS8PrimaryProp_Release(IKsPropertySet *iface)
1585     DS8Primary *This = impl_from_IKsPropertySet(iface);
1586     LONG ret;
1588     ret = InterlockedDecrement(&This->prop_ref);
1589     TRACE("new refcount %"LONGFMT"d\n", ret);
1591     return ret;
1594 static HRESULT WINAPI DS8PrimaryProp_Get(IKsPropertySet *iface,
1595   REFGUID guidPropSet, ULONG dwPropID,
1596   LPVOID pInstanceData, ULONG cbInstanceData,
1597   LPVOID pPropData, ULONG cbPropData,
1598   PULONG pcbReturned)
1600     DS8Primary *This = impl_from_IKsPropertySet(iface);
1601     HRESULT res = E_PROP_ID_UNSUPPORTED;
1602     (void)pInstanceData;
1603     (void)cbInstanceData;
1605     if(IsEqualIID(guidPropSet, &DSPROPSETID_EAX20_ListenerProperties))
1606     {
1607         EnterCriticalSection(This->crst);
1609         if(This->effect == 0)
1610             res = E_PROP_ID_UNSUPPORTED;
1611         else switch(dwPropID)
1612         {
1613         case DSPROPERTY_EAXLISTENER_ALLPARAMETERS:
1614             res = DSERR_INVALIDPARAM;
1615             if(cbPropData >= sizeof(EAXLISTENERPROPERTIES))
1616             {
1617                 union {
1618                     void *v;
1619                     EAXLISTENERPROPERTIES *props;
1620                 } data = { pPropData };
1622                 *data.props = This->eax_prop;
1623                 *pcbReturned = sizeof(EAXLISTENERPROPERTIES);
1624                 res = DS_OK;
1625             }
1626             break;
1628         case DSPROPERTY_EAXLISTENER_ROOM:
1629             res = DSERR_INVALIDPARAM;
1630             if(cbPropData >= sizeof(LONG))
1631             {
1632                 union {
1633                     void *v;
1634                     LONG *l;
1635                 } data = { pPropData };
1637                 *data.l = This->eax_prop.lRoom;
1638                 *pcbReturned = sizeof(LONG);
1639                 res = DS_OK;
1640             }
1641             break;
1642         case DSPROPERTY_EAXLISTENER_ROOMHF:
1643             res = DSERR_INVALIDPARAM;
1644             if(cbPropData >= sizeof(LONG))
1645             {
1646                 union {
1647                     void *v;
1648                     LONG *l;
1649                 } data = { pPropData };
1651                 *data.l = This->eax_prop.lRoomHF;
1652                 *pcbReturned = sizeof(LONG);
1653                 res = DS_OK;
1654             }
1655             break;
1657         case DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR:
1658             res = DSERR_INVALIDPARAM;
1659             if(cbPropData >= sizeof(FLOAT))
1660             {
1661                 union {
1662                     void *v;
1663                     FLOAT *fl;
1664                 } data = { pPropData };
1666                 *data.fl = This->eax_prop.flRoomRolloffFactor;
1667                 *pcbReturned = sizeof(FLOAT);
1668                 res = DS_OK;
1669             }
1670             break;
1672         case DSPROPERTY_EAXLISTENER_ENVIRONMENT:
1673             res = DSERR_INVALIDPARAM;
1674             if(cbPropData >= sizeof(DWORD))
1675             {
1676                 union {
1677                     void *v;
1678                     DWORD *dw;
1679                 } data = { pPropData };
1681                 *data.dw = This->eax_prop.dwEnvironment;
1682                 *pcbReturned = sizeof(DWORD);
1683                 res = DS_OK;
1684             }
1685             break;
1687         case DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE:
1688             res = DSERR_INVALIDPARAM;
1689             if(cbPropData >= sizeof(FLOAT))
1690             {
1691                 union {
1692                     void *v;
1693                     FLOAT *fl;
1694                 } data = { pPropData };
1696                 *data.fl = This->eax_prop.flEnvironmentSize;
1697                 *pcbReturned = sizeof(FLOAT);
1698                 res = DS_OK;
1699             }
1700             break;
1701         case DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION:
1702             res = DSERR_INVALIDPARAM;
1703             if(cbPropData >= sizeof(FLOAT))
1704             {
1705                 union {
1706                     void *v;
1707                     FLOAT *fl;
1708                 } data = { pPropData };
1710                 *data.fl = This->eax_prop.flEnvironmentDiffusion;
1711                 *pcbReturned = sizeof(FLOAT);
1712                 res = DS_OK;
1713             }
1714             break;
1716         case DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF:
1717             res = DSERR_INVALIDPARAM;
1718             if(cbPropData >= sizeof(FLOAT))
1719             {
1720                 union {
1721                     void *v;
1722                     FLOAT *fl;
1723                 } data = { pPropData };
1725                 *data.fl = This->eax_prop.flAirAbsorptionHF;
1726                 *pcbReturned = sizeof(FLOAT);
1727                 res = DS_OK;
1728             }
1729             break;
1731         case DSPROPERTY_EAXLISTENER_FLAGS:
1732             res = DSERR_INVALIDPARAM;
1733             if(cbPropData >= sizeof(DWORD))
1734             {
1735                 union {
1736                     void *v;
1737                     DWORD *dw;
1738                 } data = { pPropData };
1740                 *data.dw = This->eax_prop.dwFlags;
1741                 *pcbReturned = sizeof(DWORD);
1742                 res = DS_OK;
1743             }
1744             break;
1746         default:
1747             FIXME("Unhandled propid: 0x%08"LONGFMT"x\n", dwPropID);
1748             break;
1749         }
1751         LeaveCriticalSection(This->crst);
1752     }
1753     else
1754         FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
1756     return res;
1759 static HRESULT WINAPI DS8PrimaryProp_Set(IKsPropertySet *iface,
1760   REFGUID guidPropSet, ULONG dwPropID,
1761   LPVOID pInstanceData, ULONG cbInstanceData,
1762   LPVOID pPropData, ULONG cbPropData)
1764     DS8Primary *This = impl_from_IKsPropertySet(iface);
1765     HRESULT res = E_PROP_ID_UNSUPPORTED;
1766     (void)pInstanceData;
1767     (void)cbInstanceData;
1769     if(IsEqualIID(guidPropSet, &DSPROPSETID_EAX20_ListenerProperties))
1770     {
1771         DWORD propid = dwPropID & ~DSPROPERTY_EAXLISTENER_DEFERRED;
1772         BOOL immediate = !(dwPropID&DSPROPERTY_EAXLISTENER_DEFERRED);
1774         EnterCriticalSection(This->crst);
1775         setALContext(This->ctx);
1777         if(This->effect == 0)
1778             res = E_PROP_ID_UNSUPPORTED;
1779         else switch(propid)
1780         {
1781         case DSPROPERTY_EAXLISTENER_NONE: /* not setting any property, just applying */
1782             res = DS_OK;
1783             break;
1785         case DSPROPERTY_EAXLISTENER_ALLPARAMETERS:
1786         do_allparams:
1787             res = DSERR_INVALIDPARAM;
1788             if(cbPropData >= sizeof(EAXLISTENERPROPERTIES))
1789             {
1790                 union {
1791                     const void *v;
1792                     const EAXLISTENERPROPERTIES *props;
1793                 } data = { pPropData };
1795                 /* FIXME: Need to validate property values... Ignore? Clamp? Error? */
1796                 This->eax_prop = *data.props;
1797                 This->ExtAL->Effectf(This->effect, AL_REVERB_DENSITY,
1798                                      (data.props->flEnvironmentSize < 2.0f) ?
1799                                      (data.props->flEnvironmentSize - 1.0f) : 1.0f);
1800                 This->ExtAL->Effectf(This->effect, AL_REVERB_DIFFUSION,
1801                                      data.props->flEnvironmentDiffusion);
1803                 This->ExtAL->Effectf(This->effect, AL_REVERB_GAIN,
1804                                      mB_to_gain(data.props->lRoom));
1805                 This->ExtAL->Effectf(This->effect, AL_REVERB_GAINHF,
1806                                      mB_to_gain(data.props->lRoomHF));
1808                 This->ExtAL->Effectf(This->effect, AL_REVERB_ROOM_ROLLOFF_FACTOR,
1809                                      data.props->flRoomRolloffFactor);
1811                 This->ExtAL->Effectf(This->effect, AL_REVERB_DECAY_TIME,
1812                                      data.props->flDecayTime);
1813                 This->ExtAL->Effectf(This->effect, AL_REVERB_DECAY_HFRATIO,
1814                                      data.props->flDecayHFRatio);
1816                 This->ExtAL->Effectf(This->effect, AL_REVERB_REFLECTIONS_GAIN,
1817                                      mB_to_gain(data.props->lReflections));
1818                 This->ExtAL->Effectf(This->effect, AL_REVERB_REFLECTIONS_DELAY,
1819                                      data.props->flReflectionsDelay);
1821                 This->ExtAL->Effectf(This->effect, AL_REVERB_LATE_REVERB_GAIN,
1822                                      mB_to_gain(data.props->lReverb));
1823                 This->ExtAL->Effectf(This->effect, AL_REVERB_LATE_REVERB_DELAY,
1824                                      data.props->flReverbDelay);
1826                 This->ExtAL->Effectf(This->effect, AL_REVERB_AIR_ABSORPTION_GAINHF,
1827                                      mB_to_gain(data.props->flAirAbsorptionHF));
1829                 This->ExtAL->Effecti(This->effect, AL_REVERB_DECAY_HFLIMIT,
1830                                      (data.props->dwFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ?
1831                                      AL_TRUE : AL_FALSE);
1833                 checkALError();
1835                 This->dirty.bit.effect = 1;
1836                 res = DS_OK;
1837             }
1838             break;
1840         case DSPROPERTY_EAXLISTENER_ROOM:
1841             res = DSERR_INVALIDPARAM;
1842             if(cbPropData >= sizeof(LONG))
1843             {
1844                 union {
1845                     const void *v;
1846                     const LONG *l;
1847                 } data = { pPropData };
1849                 This->eax_prop.lRoom = *data.l;
1850                 This->ExtAL->Effectf(This->effect, AL_REVERB_GAIN,
1851                                      mB_to_gain(This->eax_prop.lRoom));
1852                 checkALError();
1854                 This->dirty.bit.effect = 1;
1855                 res = DS_OK;
1856             }
1857             break;
1858         case DSPROPERTY_EAXLISTENER_ROOMHF:
1859             res = DSERR_INVALIDPARAM;
1860             if(cbPropData >= sizeof(LONG))
1861             {
1862                 union {
1863                     const void *v;
1864                     const LONG *l;
1865                 } data = { pPropData };
1867                 This->eax_prop.lRoomHF = *data.l;
1868                 This->ExtAL->Effectf(This->effect, AL_REVERB_GAINHF,
1869                                      mB_to_gain(This->eax_prop.lRoomHF));
1870                 checkALError();
1872                 This->dirty.bit.effect = 1;
1873                 res = DS_OK;
1874             }
1875             break;
1877         case DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR:
1878             res = DSERR_INVALIDPARAM;
1879             if(cbPropData >= sizeof(FLOAT))
1880             {
1881                 union {
1882                     const void *v;
1883                     const FLOAT *fl;
1884                 } data = { pPropData };
1886                 This->eax_prop.flRoomRolloffFactor = *data.fl;
1887                 This->ExtAL->Effectf(This->effect, AL_REVERB_ROOM_ROLLOFF_FACTOR,
1888                                      This->eax_prop.flRoomRolloffFactor);
1889                 checkALError();
1891                 This->dirty.bit.effect = 1;
1892                 res = DS_OK;
1893             }
1894             break;
1896         case DSPROPERTY_EAXLISTENER_ENVIRONMENT:
1897             res = DSERR_INVALIDPARAM;
1898             if(cbPropData >= sizeof(DWORD))
1899             {
1900                 union {
1901                     const void *v;
1902                     const DWORD *dw;
1903                 } data = { pPropData };
1905                 if(*data.dw <= EAX_MAX_ENVIRONMENT)
1906                 {
1907                     /* Get the environment index's default and pass it down to
1908                      * ALLPARAMETERS */
1909                     propid = DSPROPERTY_EAXLISTENER_ALLPARAMETERS;
1910                     pPropData = (void*)&EnvironmentDefaults[*data.dw];
1911                     cbPropData = sizeof(EnvironmentDefaults[*data.dw]);
1912                     goto do_allparams;
1913                 }
1914             }
1915             break;
1917         case DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE:
1918             res = DSERR_INVALIDPARAM;
1919             if(cbPropData >= sizeof(FLOAT))
1920             {
1921                 union {
1922                     const void *v;
1923                     const FLOAT *fl;
1924                 } data = { pPropData };
1926                 if(*data.fl >= 1.0f && *data.fl <= 100.0f)
1927                 {
1928                     double scale = (*data.fl)/This->eax_prop.flEnvironmentSize;
1930                     This->eax_prop.flEnvironmentSize = *data.fl;
1932                     if((This->eax_prop.dwFlags&EAXLISTENERFLAGS_DECAYTIMESCALE))
1933                     {
1934                         This->eax_prop.flDecayTime *= scale;
1935                         This->eax_prop.flDecayTime = clampF(This->eax_prop.flDecayTime, 0.1f, 20.0f);
1936                     }
1937                     if((This->eax_prop.dwFlags&EAXLISTENERFLAGS_REFLECTIONSSCALE))
1938                     {
1939                         This->eax_prop.lReflections += gain_to_mB(1.0/scale);
1940                         This->eax_prop.lReflections = clampI(This->eax_prop.lReflections, -10000, 1000);
1941                     }
1942                     if((This->eax_prop.dwFlags&EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE))
1943                     {
1944                         This->eax_prop.flReflectionsDelay *= scale;
1945                         This->eax_prop.flReflectionsDelay = clampF(This->eax_prop.flReflectionsDelay, 0.0f, 0.3f);
1946                     }
1947                     if((This->eax_prop.dwFlags&EAXLISTENERFLAGS_REVERBSCALE))
1948                     {
1949                         This->eax_prop.lReverb += gain_to_mB(1.0/scale);
1950                         This->eax_prop.lReverb = clampI(This->eax_prop.lReverb, -10000, 2000);
1951                     }
1952                     if((This->eax_prop.dwFlags&EAXLISTENERFLAGS_REVERBDELAYSCALE))
1953                     {
1954                         This->eax_prop.flReverbDelay *= scale;
1955                         This->eax_prop.flReverbDelay = clampF(This->eax_prop.flReverbDelay, 0.0f, 0.1f);
1956                     }
1958                     /* Pass the updated environment properties down to ALLPARAMETERS */
1959                     propid = DSPROPERTY_EAXLISTENER_ALLPARAMETERS;
1960                     pPropData = (void*)&This->eax_prop;
1961                     cbPropData = sizeof(This->eax_prop);
1962                     goto do_allparams;
1963                 }
1964             }
1965             break;
1966         case DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION:
1967             res = DSERR_INVALIDPARAM;
1968             if(cbPropData >= sizeof(FLOAT))
1969             {
1970                 union {
1971                     const void *v;
1972                     const FLOAT *fl;
1973                 } data = { pPropData };
1975                 This->eax_prop.flEnvironmentDiffusion = *data.fl;
1976                 This->ExtAL->Effectf(This->effect, AL_REVERB_DIFFUSION,
1977                                      This->eax_prop.flEnvironmentDiffusion);
1978                 checkALError();
1980                 This->dirty.bit.effect = 1;
1981                 res = DS_OK;
1982             }
1983             break;
1985         case DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF:
1986             res = DSERR_INVALIDPARAM;
1987             if(cbPropData >= sizeof(FLOAT))
1988             {
1989                 union {
1990                     const void *v;
1991                     const FLOAT *fl;
1992                 } data = { pPropData };
1994                 This->eax_prop.flAirAbsorptionHF = *data.fl;
1995                 This->ExtAL->Effectf(This->effect, AL_REVERB_AIR_ABSORPTION_GAINHF,
1996                                      mB_to_gain(This->eax_prop.flAirAbsorptionHF));
1997                 checkALError();
1999                 This->dirty.bit.effect = 1;
2000                 res = DS_OK;
2001             }
2002             break;
2004         case DSPROPERTY_EAXLISTENER_FLAGS:
2005             res = DSERR_INVALIDPARAM;
2006             if(cbPropData >= sizeof(DWORD))
2007             {
2008                 union {
2009                     const void *v;
2010                     const DWORD *dw;
2011                 } data = { pPropData };
2013                 This->eax_prop.dwFlags = *data.dw;
2014                 This->ExtAL->Effecti(This->effect, AL_REVERB_DECAY_HFLIMIT,
2015                                      (This->eax_prop.dwFlags&EAXLISTENERFLAGS_DECAYHFLIMIT) ?
2016                                      AL_TRUE : AL_FALSE);
2017                 checkALError();
2019                 This->dirty.bit.effect = 1;
2020                 res = DS_OK;
2021             }
2022             break;
2024         default:
2025             FIXME("Unhandled propid: 0x%08"LONGFMT"x\n", propid);
2026             break;
2027         }
2029         if(res == DS_OK && immediate)
2030             IDirectSound3DListener_CommitDeferredSettings(&This->IDirectSound3DListener_iface);
2032         popALContext();
2033         LeaveCriticalSection(This->crst);
2034     }
2035     else
2036         FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
2038     return res;
2041 static HRESULT WINAPI DS8PrimaryProp_QuerySupport(IKsPropertySet *iface,
2042   REFGUID guidPropSet, ULONG dwPropID,
2043   PULONG pTypeSupport)
2045     DS8Primary *This = impl_from_IKsPropertySet(iface);
2046     HRESULT res = E_PROP_ID_UNSUPPORTED;
2048     if(IsEqualIID(guidPropSet, &DSPROPSETID_EAX20_ListenerProperties))
2049     {
2050         EnterCriticalSection(This->crst);
2052         if(This->effect == 0)
2053             res = E_PROP_ID_UNSUPPORTED;
2054         else if(dwPropID == DSPROPERTY_EAXLISTENER_ALLPARAMETERS ||
2055                 dwPropID == DSPROPERTY_EAXLISTENER_ROOM ||
2056                 dwPropID == DSPROPERTY_EAXLISTENER_ROOMHF ||
2057                 dwPropID == DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR ||
2058                 dwPropID == DSPROPERTY_EAXLISTENER_ENVIRONMENT ||
2059                 dwPropID == DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE ||
2060                 dwPropID == DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION ||
2061                 dwPropID == DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF ||
2062                 dwPropID == DSPROPERTY_EAXLISTENER_FLAGS)
2063         {
2064             *pTypeSupport = KSPROPERTY_SUPPORT_GET|KSPROPERTY_SUPPORT_SET;
2065             res = DS_OK;
2066         }
2067         else
2068             FIXME("Unhandled propid: 0x%08"LONGFMT"x\n", dwPropID);
2070         LeaveCriticalSection(This->crst);
2071     }
2072     else
2073         FIXME("Unhandled propset: %s\n", debugstr_guid(guidPropSet));
2075     return res;
2078 static const IKsPropertySetVtbl DS8PrimaryProp_Vtbl =
2080     DS8PrimaryProp_QueryInterface,
2081     DS8PrimaryProp_AddRef,
2082     DS8PrimaryProp_Release,
2083     DS8PrimaryProp_Get,
2084     DS8PrimaryProp_Set,
2085     DS8PrimaryProp_QuerySupport