Attempt to improve handling of EAX1 volume settings
[dsound-openal.git] / eax.c
blobb46011653a6a2787b9f3d8f5c4bc7c0e55795cc9
1 /* DirectSound EAX interface
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 #define CONST_VTABLE
19 #include <stdarg.h>
20 #include <string.h>
22 #include "windows.h"
23 #include "dsound.h"
25 #include "dsound_private.h"
28 #define EAX2LISTENERFLAGS_MASK (EAX20LISTENERFLAGS_DECAYTIMESCALE | \
29 EAX20LISTENERFLAGS_REFLECTIONSSCALE | \
30 EAX20LISTENERFLAGS_REFLECTIONSDELAYSCALE | \
31 EAX20LISTENERFLAGS_REVERBSCALE | \
32 EAX20LISTENERFLAGS_REVERBDELAYSCALE | \
33 EAX20LISTENERFLAGS_DECAYHFLIMIT)
35 static EAX20LISTENERPROPERTIES EAX3To2(const EAX30LISTENERPROPERTIES *props)
37 EAX20LISTENERPROPERTIES ret;
38 ret.lRoom = props->lRoom;
39 ret.lRoomHF = props->lRoomHF;
40 ret.flRoomRolloffFactor = props->flRoomRolloffFactor;
41 ret.flDecayTime = props->flDecayTime;
42 ret.flDecayHFRatio = props->flDecayHFRatio;
43 ret.lReflections = props->lReflections;
44 ret.flReflectionsDelay = props->flReflectionsDelay;
45 ret.lReverb = props->lReverb;
46 ret.flReverbDelay = props->flReverbDelay;
47 ret.dwEnvironment = props->dwEnvironment;
48 ret.flEnvironmentSize = props->flEnvironmentSize;
49 ret.flEnvironmentDiffusion = props->flEnvironmentDiffusion;
50 ret.flAirAbsorptionHF = props->flAirAbsorptionHF;
51 ret.dwFlags = props->dwFlags & EAX2LISTENERFLAGS_MASK;
52 return ret;
55 static void ApplyReverbParams(DS8Primary *prim, const EAX30LISTENERPROPERTIES *props)
57 /* FIXME: Need to validate property values... Ignore? Clamp? Error? */
58 prim->deferred.eax = *props;
59 alEffectf(prim->effect, AL_REVERB_DENSITY,
60 clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f)
62 alEffectf(prim->effect, AL_REVERB_DIFFUSION, props->flEnvironmentDiffusion);
64 alEffectf(prim->effect, AL_REVERB_GAIN, mB_to_gain(props->lRoom));
65 alEffectf(prim->effect, AL_REVERB_GAINHF, mB_to_gain(props->lRoomHF));
67 alEffectf(prim->effect, AL_REVERB_DECAY_TIME, props->flDecayTime);
68 alEffectf(prim->effect, AL_REVERB_DECAY_HFRATIO, props->flDecayHFRatio);
70 alEffectf(prim->effect, AL_REVERB_REFLECTIONS_GAIN, mB_to_gain(props->lReflections));
71 alEffectf(prim->effect, AL_REVERB_REFLECTIONS_DELAY, props->flReflectionsDelay);
73 alEffectf(prim->effect, AL_REVERB_LATE_REVERB_GAIN, mB_to_gain(props->lReverb));
74 alEffectf(prim->effect, AL_REVERB_LATE_REVERB_DELAY, props->flReverbDelay);
76 alEffectf(prim->effect, AL_REVERB_AIR_ABSORPTION_GAINHF,
77 mB_to_gain(props->flAirAbsorptionHF));
79 alEffectf(prim->effect, AL_REVERB_ROOM_ROLLOFF_FACTOR, props->flRoomRolloffFactor);
81 alEffecti(prim->effect, AL_REVERB_DECAY_HFLIMIT,
82 (props->dwFlags&EAX30LISTENERFLAGS_DECAYHFLIMIT) ?
83 AL_TRUE : AL_FALSE);
85 checkALError();
87 prim->dirty.bit.effect = 1;
90 #define APPLY_DRY_PARAMS 1
91 #define APPLY_WET_PARAMS 2
92 static void ApplyFilterParams(DS8Buffer *buf, const EAX20BUFFERPROPERTIES *props, int apply)
94 /* The LFRatio properties determine how much the given level applies to low
95 * frequencies as well as high frequencies. Given that the high frequency
96 * levels are specified relative to the low, they should increase as the
97 * low frequency levels reduce.
99 FLOAT occl = props->lOcclusion * props->flOcclusionLFRatio;
100 FLOAT occlhf = props->lOcclusion * (1.0f-props->flOcclusionLFRatio);
102 if((apply&APPLY_DRY_PARAMS))
104 FLOAT obstr = props->lObstruction * props->flObstructionLFRatio;
105 FLOAT obstrhf = props->lObstruction * (1.0f-props->flObstructionLFRatio);
106 FLOAT mb = props->lDirect + obstr + occl;
107 FLOAT mbhf = props->lDirectHF + obstrhf + occlhf;
109 alFilterf(buf->filter[0], AL_LOWPASS_GAIN, mB_to_gain(mb));
110 alFilterf(buf->filter[0], AL_LOWPASS_GAINHF, mB_to_gain(mbhf));
112 if((apply&APPLY_WET_PARAMS))
114 FLOAT occlroom = props->flOcclusionRoomRatio;
115 FLOAT mb = props->lRoom + occlroom*occl;
116 FLOAT mbhf = props->lRoomHF + occlroom*occlhf;
118 alFilterf(buf->filter[1], AL_LOWPASS_GAIN, mB_to_gain(mb));
119 alFilterf(buf->filter[1], AL_LOWPASS_GAINHF, mB_to_gain(mbhf));
121 checkALError();
125 HRESULT EAX2_Set(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData)
127 HRESULT hr;
129 if(prim->effect == 0)
130 return E_PROP_ID_UNSUPPORTED;
132 hr = DSERR_INVALIDPARAM;
133 switch(propid)
135 case DSPROPERTY_EAX20LISTENER_NONE: /* not setting any property, just applying */
136 hr = DS_OK;
137 break;
139 case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS:
140 if(cbPropData >= sizeof(EAX20LISTENERPROPERTIES))
142 union {
143 const void *v;
144 const EAX20LISTENERPROPERTIES *props;
145 } data = { pPropData };
146 EAX30LISTENERPROPERTIES props3 = REVERB_PRESET_GENERIC;
147 if(data.props->dwEnvironment < EAX_ENVIRONMENT_COUNT)
149 props3 = EnvironmentDefaults[data.props->dwEnvironment];
150 props3.dwEnvironment = data.props->dwEnvironment;
152 props3.flEnvironmentSize = data.props->flEnvironmentSize;
153 props3.flEnvironmentDiffusion = data.props->flEnvironmentDiffusion;
154 props3.lRoom = data.props->lRoom;
155 props3.lRoomHF = data.props->lRoomHF;
156 props3.flDecayTime = data.props->flDecayTime;
157 props3.flDecayHFRatio = data.props->flDecayHFRatio;
158 props3.lReflections = data.props->lReflections;
159 props3.flReflectionsDelay = data.props->flReflectionsDelay;
160 props3.lReverb = data.props->lReverb;
161 props3.flReverbDelay = data.props->flReverbDelay;
162 props3.flAirAbsorptionHF = data.props->flAirAbsorptionHF;
163 props3.flRoomRolloffFactor = data.props->flRoomRolloffFactor;
164 props3.dwFlags = data.props->dwFlags;
166 ApplyReverbParams(prim, &props3);
167 hr = DS_OK;
169 break;
171 case DSPROPERTY_EAX20LISTENER_ROOM:
172 if(cbPropData >= sizeof(LONG))
174 union { const void *v; const LONG *l; } data = { pPropData };
176 prim->deferred.eax.lRoom = *data.l;
177 alEffectf(prim->effect, AL_REVERB_GAIN,
178 mB_to_gain(prim->deferred.eax.lRoom));
179 checkALError();
181 prim->dirty.bit.effect = 1;
182 hr = DS_OK;
184 break;
185 case DSPROPERTY_EAX20LISTENER_ROOMHF:
186 if(cbPropData >= sizeof(LONG))
188 union { const void *v; const LONG *l; } data = { pPropData };
190 prim->deferred.eax.lRoomHF = *data.l;
191 alEffectf(prim->effect, AL_REVERB_GAINHF,
192 mB_to_gain(prim->deferred.eax.lRoomHF));
193 checkALError();
195 prim->dirty.bit.effect = 1;
196 hr = DS_OK;
198 break;
200 case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR:
201 if(cbPropData >= sizeof(FLOAT))
203 union { const void *v; const FLOAT *fl; } data = { pPropData };
205 prim->deferred.eax.flRoomRolloffFactor = *data.fl;
206 alEffectf(prim->effect, AL_REVERB_ROOM_ROLLOFF_FACTOR,
207 prim->deferred.eax.flRoomRolloffFactor);
208 checkALError();
210 prim->dirty.bit.effect = 1;
211 hr = DS_OK;
213 break;
215 case DSPROPERTY_EAX20LISTENER_DECAYTIME:
216 if(cbPropData >= sizeof(FLOAT))
218 union { const void *v; const FLOAT *fl; } data = { pPropData };
220 prim->deferred.eax.flDecayTime = *data.fl;
221 alEffectf(prim->effect, AL_REVERB_DECAY_TIME,
222 prim->deferred.eax.flDecayTime);
223 checkALError();
225 prim->dirty.bit.effect = 1;
226 hr = DS_OK;
228 break;
229 case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO:
230 if(cbPropData >= sizeof(FLOAT))
232 union { const void *v; const FLOAT *fl; } data = { pPropData };
234 prim->deferred.eax.flDecayHFRatio = *data.fl;
235 alEffectf(prim->effect, AL_REVERB_DECAY_HFRATIO,
236 prim->deferred.eax.flDecayHFRatio);
237 checkALError();
239 prim->dirty.bit.effect = 1;
240 hr = DS_OK;
242 break;
244 case DSPROPERTY_EAX20LISTENER_REFLECTIONS:
245 if(cbPropData >= sizeof(LONG))
247 union { const void *v; const LONG *l; } data = { pPropData };
249 prim->deferred.eax.lReflections = *data.l;
250 alEffectf(prim->effect, AL_REVERB_REFLECTIONS_GAIN,
251 mB_to_gain(prim->deferred.eax.lReflections));
252 checkALError();
254 prim->dirty.bit.effect = 1;
255 hr = DS_OK;
257 break;
258 case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY:
259 if(cbPropData >= sizeof(FLOAT))
261 union { const void *v; const FLOAT *fl; } data = { pPropData };
263 prim->deferred.eax.flReflectionsDelay = *data.fl;
264 alEffectf(prim->effect, AL_REVERB_REFLECTIONS_DELAY,
265 prim->deferred.eax.flReflectionsDelay);
266 checkALError();
268 prim->dirty.bit.effect = 1;
269 hr = DS_OK;
271 break;
273 case DSPROPERTY_EAX20LISTENER_REVERB:
274 if(cbPropData >= sizeof(LONG))
276 union { const void *v; const LONG *l; } data = { pPropData };
278 prim->deferred.eax.lReverb = *data.l;
279 alEffectf(prim->effect, AL_REVERB_LATE_REVERB_GAIN,
280 mB_to_gain(prim->deferred.eax.lReverb));
281 checkALError();
283 prim->dirty.bit.effect = 1;
284 hr = DS_OK;
286 break;
287 case DSPROPERTY_EAX20LISTENER_REVERBDELAY:
288 if(cbPropData >= sizeof(FLOAT))
290 union { const void *v; const FLOAT *fl; } data = { pPropData };
292 prim->deferred.eax.flReverbDelay = *data.fl;
293 alEffectf(prim->effect, AL_REVERB_LATE_REVERB_DELAY,
294 prim->deferred.eax.flReverbDelay);
295 checkALError();
297 prim->dirty.bit.effect = 1;
298 hr = DS_OK;
300 break;
302 case DSPROPERTY_EAX20LISTENER_ENVIRONMENT:
303 if(cbPropData >= sizeof(DWORD))
305 union { const void *v; const DWORD *dw; } data = { pPropData };
306 if(*data.dw < EAX_ENVIRONMENT_COUNT)
308 ApplyReverbParams(prim, &EnvironmentDefaults[*data.dw]);
309 hr = DS_OK;
312 break;
314 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE:
315 if(cbPropData >= sizeof(FLOAT))
317 union { const void *v; const FLOAT *fl; } data = { pPropData };
318 if(*data.fl >= 1.0f && *data.fl <= 100.0f)
320 float scale = (*data.fl)/prim->deferred.eax.flEnvironmentSize;
322 prim->deferred.eax.flEnvironmentSize = *data.fl;
324 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_DECAYTIMESCALE))
326 prim->deferred.eax.flDecayTime *= scale;
327 prim->deferred.eax.flDecayTime = clampF(prim->deferred.eax.flDecayTime, 0.1f, 20.0f);
329 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REFLECTIONSSCALE))
331 prim->deferred.eax.lReflections -= gain_to_mB(scale);
332 prim->deferred.eax.lReflections = clampI(prim->deferred.eax.lReflections, -10000, 1000);
334 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REFLECTIONSDELAYSCALE))
336 prim->deferred.eax.flReflectionsDelay *= scale;
337 prim->deferred.eax.flReflectionsDelay = clampF(prim->deferred.eax.flReflectionsDelay, 0.0f, 0.3f);
339 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REVERBSCALE))
341 prim->deferred.eax.lReverb -= gain_to_mB(scale);
342 prim->deferred.eax.lReverb = clampI(prim->deferred.eax.lReverb, -10000, 2000);
344 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REVERBDELAYSCALE))
346 prim->deferred.eax.flReverbDelay *= scale;
347 prim->deferred.eax.flReverbDelay = clampF(prim->deferred.eax.flReverbDelay, 0.0f, 0.1f);
349 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_ECHOTIMESCALE))
351 prim->deferred.eax.flEchoTime *= scale;
352 prim->deferred.eax.flEchoTime = clampF(prim->deferred.eax.flEchoTime, 0.075f, 0.25f);
354 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_MODTIMESCALE))
356 prim->deferred.eax.flModulationTime *= scale;
357 prim->deferred.eax.flModulationTime = clampF(prim->deferred.eax.flModulationTime, 0.04f, 4.0f);
360 ApplyReverbParams(prim, &prim->deferred.eax);
361 hr = DS_OK;
364 break;
365 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION:
366 if(cbPropData >= sizeof(FLOAT))
368 union { const void *v; const FLOAT *fl; } data = { pPropData };
370 prim->deferred.eax.flEnvironmentDiffusion = *data.fl;
371 alEffectf(prim->effect, AL_REVERB_DIFFUSION,
372 prim->deferred.eax.flEnvironmentDiffusion);
373 checkALError();
375 prim->dirty.bit.effect = 1;
376 hr = DS_OK;
378 break;
380 case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF:
381 if(cbPropData >= sizeof(FLOAT))
383 union { const void *v; const FLOAT *fl; } data = { pPropData };
385 prim->deferred.eax.flAirAbsorptionHF = *data.fl;
386 alEffectf(prim->effect, AL_REVERB_AIR_ABSORPTION_GAINHF,
387 mB_to_gain(prim->deferred.eax.flAirAbsorptionHF));
388 checkALError();
390 prim->dirty.bit.effect = 1;
391 hr = DS_OK;
393 break;
395 case DSPROPERTY_EAX20LISTENER_FLAGS:
396 if(cbPropData >= sizeof(DWORD))
398 union { const void *v; const DWORD *dw; } data = { pPropData };
400 prim->deferred.eax.dwFlags = *data.dw;
401 alEffecti(prim->effect, AL_REVERB_DECAY_HFLIMIT,
402 (prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_DECAYHFLIMIT) ?
403 AL_TRUE : AL_FALSE);
404 checkALError();
406 prim->dirty.bit.effect = 1;
407 hr = DS_OK;
409 break;
411 default:
412 hr = E_PROP_ID_UNSUPPORTED;
413 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
414 break;
417 return hr;
420 HRESULT EAX2_Get(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
422 HRESULT hr;
424 if(prim->effect == 0)
425 return E_PROP_ID_UNSUPPORTED;
427 #define GET_PROP(src, T) do { \
428 if(cbPropData >= sizeof(T)) \
430 union { void *v; T *props; } data = { pPropData }; \
431 *data.props = src; \
432 *pcbReturned = sizeof(T); \
433 hr = DS_OK; \
435 } while(0)
436 hr = DSERR_INVALIDPARAM;
437 switch(propid)
439 case DSPROPERTY_EAX20LISTENER_NONE:
440 *pcbReturned = 0;
441 hr = DS_OK;
442 break;
444 case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS:
445 GET_PROP(EAX3To2(&prim->deferred.eax), EAX20LISTENERPROPERTIES);
446 break;
448 case DSPROPERTY_EAX20LISTENER_ROOM:
449 GET_PROP(prim->deferred.eax.lRoom, LONG);
450 break;
451 case DSPROPERTY_EAX20LISTENER_ROOMHF:
452 GET_PROP(prim->deferred.eax.lRoomHF, LONG);
453 break;
455 case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR:
456 GET_PROP(prim->deferred.eax.flRoomRolloffFactor, FLOAT);
457 break;
459 case DSPROPERTY_EAX20LISTENER_DECAYTIME:
460 GET_PROP(prim->deferred.eax.flDecayTime, FLOAT);
461 break;
462 case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO:
463 GET_PROP(prim->deferred.eax.flDecayHFRatio, FLOAT);
464 break;
466 case DSPROPERTY_EAX20LISTENER_REFLECTIONS:
467 GET_PROP(prim->deferred.eax.lReflections, LONG);
468 break;
469 case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY:
470 GET_PROP(prim->deferred.eax.flReflectionsDelay, FLOAT);
471 break;
473 case DSPROPERTY_EAX20LISTENER_REVERB:
474 GET_PROP(prim->deferred.eax.lReverb, LONG);
475 break;
476 case DSPROPERTY_EAX20LISTENER_REVERBDELAY:
477 GET_PROP(prim->deferred.eax.flReverbDelay, FLOAT);
478 break;
480 case DSPROPERTY_EAX20LISTENER_ENVIRONMENT:
481 GET_PROP(prim->deferred.eax.dwEnvironment, DWORD);
482 break;
484 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE:
485 GET_PROP(prim->deferred.eax.flEnvironmentSize, FLOAT);
486 break;
487 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION:
488 GET_PROP(prim->deferred.eax.flEnvironmentDiffusion, FLOAT);
489 break;
491 case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF:
492 GET_PROP(prim->deferred.eax.flAirAbsorptionHF, FLOAT);
493 break;
495 case DSPROPERTY_EAX20LISTENER_FLAGS:
496 GET_PROP(prim->deferred.eax.dwFlags&EAX2LISTENERFLAGS_MASK, DWORD);
497 break;
499 default:
500 hr = E_PROP_ID_UNSUPPORTED;
501 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
502 break;
504 #undef GET_PROP
506 return hr;
510 HRESULT EAX2Buffer_Set(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData)
512 HRESULT hr;
514 if(buf->filter[0] == 0)
515 return E_PROP_ID_UNSUPPORTED;
517 hr = DSERR_INVALIDPARAM;
518 switch(propid)
520 case DSPROPERTY_EAX20BUFFER_NONE: /* not setting any property, just applying */
521 hr = DS_OK;
522 break;
524 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS:
525 if(cbPropData >= sizeof(EAX20BUFFERPROPERTIES))
527 union {
528 const void *v;
529 const EAX20BUFFERPROPERTIES *props;
530 } data = { pPropData };
532 buf->deferred.eax = *data.props;
533 ApplyFilterParams(buf, data.props, APPLY_DRY_PARAMS|APPLY_WET_PARAMS);
535 buf->dirty.bit.dry_filter = 1;
536 buf->dirty.bit.wet_filter = 1;
537 buf->dirty.bit.room_rolloff = 1;
538 buf->dirty.bit.cone_outsidevolumehf = 1;
539 buf->dirty.bit.air_absorb = 1;
540 buf->dirty.bit.flags = 1;
541 hr = DS_OK;
543 break;
545 case DSPROPERTY_EAX20BUFFER_DIRECT:
546 if(cbPropData >= sizeof(LONG))
548 union { const void *v; const LONG *l; } data = { pPropData };
550 buf->deferred.eax.lDirect = *data.l;
551 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
553 buf->dirty.bit.dry_filter = 1;
554 hr = DS_OK;
556 break;
557 case DSPROPERTY_EAX20BUFFER_DIRECTHF:
558 if(cbPropData >= sizeof(LONG))
560 union { const void *v; const LONG *l; } data = { pPropData };
562 buf->deferred.eax.lDirectHF = *data.l;
563 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
565 buf->dirty.bit.dry_filter = 1;
566 hr = DS_OK;
568 break;
570 case DSPROPERTY_EAX20BUFFER_ROOM:
571 if(cbPropData >= sizeof(LONG))
573 union { const void *v; const LONG *l; } data = { pPropData };
575 buf->deferred.eax.lRoom = *data.l;
576 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_WET_PARAMS);
578 buf->dirty.bit.wet_filter = 1;
579 hr = DS_OK;
581 break;
582 case DSPROPERTY_EAX20BUFFER_ROOMHF:
583 if(cbPropData >= sizeof(LONG))
585 union { const void *v; const LONG *l; } data = { pPropData };
587 buf->deferred.eax.lRoomHF = *data.l;
588 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_WET_PARAMS);
590 buf->dirty.bit.wet_filter = 1;
591 hr = DS_OK;
593 break;
595 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR:
596 if(cbPropData >= sizeof(FLOAT))
598 union { const void *v; const FLOAT *fl; } data = { pPropData };
600 buf->deferred.eax.flRoomRolloffFactor = *data.fl;
602 buf->dirty.bit.room_rolloff = 1;
603 hr = DS_OK;
605 break;
607 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION:
608 if(cbPropData >= sizeof(LONG))
610 union { const void *v; const LONG *l; } data = { pPropData };
612 buf->deferred.eax.lObstruction = *data.l;
613 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
615 buf->dirty.bit.dry_filter = 1;
616 hr = DS_OK;
618 break;
619 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO:
620 if(cbPropData >= sizeof(FLOAT))
622 union { const void *v; const FLOAT *fl; } data = { pPropData };
624 buf->deferred.eax.flObstructionLFRatio = *data.fl;
625 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
627 buf->dirty.bit.dry_filter = 1;
628 hr = DS_OK;
630 break;
632 case DSPROPERTY_EAX20BUFFER_OCCLUSION:
633 if(cbPropData >= sizeof(LONG))
635 union { const void *v; const LONG *l; } data = { pPropData };
637 buf->deferred.eax.lOcclusion = *data.l;
638 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS|APPLY_WET_PARAMS);
640 buf->dirty.bit.dry_filter = 1;
641 buf->dirty.bit.wet_filter = 1;
642 hr = DS_OK;
644 break;
645 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO:
646 if(cbPropData >= sizeof(FLOAT))
648 union { const void *v; const FLOAT *fl; } data = { pPropData };
650 buf->deferred.eax.flOcclusionLFRatio = *data.fl;
651 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS|APPLY_WET_PARAMS);
653 buf->dirty.bit.dry_filter = 1;
654 buf->dirty.bit.wet_filter = 1;
655 hr = DS_OK;
657 break;
658 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO:
659 if(cbPropData >= sizeof(FLOAT))
661 union { const void *v; const FLOAT *fl; } data = { pPropData };
663 buf->deferred.eax.flOcclusionRoomRatio = *data.fl;
664 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS|APPLY_WET_PARAMS);
666 buf->dirty.bit.dry_filter = 1;
667 buf->dirty.bit.wet_filter = 1;
668 hr = DS_OK;
670 break;
672 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF:
673 if(cbPropData >= sizeof(LONG))
675 union { const void *v; const LONG *l; } data = { pPropData };
677 buf->deferred.eax.lOutsideVolumeHF = *data.l;
679 buf->dirty.bit.cone_outsidevolumehf = 1;
680 hr = DS_OK;
682 break;
684 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR:
685 if(cbPropData >= sizeof(FLOAT))
687 union { const void *v; const FLOAT *fl; } data = { pPropData };
689 buf->deferred.eax.flAirAbsorptionFactor = *data.fl;
691 buf->dirty.bit.air_absorb = 1;
692 hr = DS_OK;
694 break;
696 case DSPROPERTY_EAX20BUFFER_FLAGS:
697 if(cbPropData >= sizeof(DWORD))
699 union { const void *v; const DWORD *dw; } data = { pPropData };
701 buf->deferred.eax.dwFlags = *data.dw;
703 buf->dirty.bit.flags = 1;
704 hr = DS_OK;
706 break;
708 default:
709 hr = E_PROP_ID_UNSUPPORTED;
710 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
711 break;
714 return hr;
717 HRESULT EAX2Buffer_Get(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
719 HRESULT hr;
721 if(buf->filter[0] == 0)
722 return E_PROP_ID_UNSUPPORTED;
724 #define GET_PROP(src, T) do { \
725 if(cbPropData >= sizeof(T)) \
727 union { void *v; T *props; } data = { pPropData }; \
728 *data.props = src; \
729 *pcbReturned = sizeof(T); \
730 hr = DS_OK; \
732 } while(0)
733 hr = DSERR_INVALIDPARAM;
734 switch(propid)
736 case DSPROPERTY_EAX20BUFFER_NONE:
737 *pcbReturned = 0;
738 hr = DS_OK;
739 break;
741 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS:
742 GET_PROP(buf->deferred.eax, EAX20BUFFERPROPERTIES);
743 break;
745 case DSPROPERTY_EAX20BUFFER_DIRECT:
746 GET_PROP(buf->deferred.eax.lDirect, LONG);
747 break;
748 case DSPROPERTY_EAX20BUFFER_DIRECTHF:
749 GET_PROP(buf->deferred.eax.lDirectHF, LONG);
750 break;
752 case DSPROPERTY_EAX20BUFFER_ROOM:
753 GET_PROP(buf->deferred.eax.lRoom, LONG);
754 break;
755 case DSPROPERTY_EAX20BUFFER_ROOMHF:
756 GET_PROP(buf->deferred.eax.lRoomHF, LONG);
757 break;
759 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR:
760 GET_PROP(buf->deferred.eax.flRoomRolloffFactor, FLOAT);
761 break;
763 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION:
764 GET_PROP(buf->deferred.eax.lObstruction, LONG);
765 break;
766 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO:
767 GET_PROP(buf->deferred.eax.flObstructionLFRatio, FLOAT);
768 break;
770 case DSPROPERTY_EAX20BUFFER_OCCLUSION:
771 GET_PROP(buf->deferred.eax.lOcclusion, LONG);
772 break;
773 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO:
774 GET_PROP(buf->deferred.eax.flOcclusionLFRatio, FLOAT);
775 break;
776 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO:
777 GET_PROP(buf->deferred.eax.flOcclusionRoomRatio, FLOAT);
778 break;
780 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF:
781 GET_PROP(buf->deferred.eax.lOutsideVolumeHF, LONG);
782 break;
784 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR:
785 GET_PROP(buf->deferred.eax.flAirAbsorptionFactor, FLOAT);
786 break;
788 case DSPROPERTY_EAX20BUFFER_FLAGS:
789 GET_PROP(buf->deferred.eax.dwFlags, DWORD);
790 break;
792 default:
793 hr = E_PROP_ID_UNSUPPORTED;
794 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
795 break;
797 #undef GET_PROP
799 return hr;
803 HRESULT EAX1_Set(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData)
805 static const float eax1_env_volume[EAX_ENVIRONMENT_COUNT] = {
806 0.5f, 0.25f, 0.417f, 0.653f, 0.208f, 0.5f, 0.403f, 0.5f, 0.5f,
807 0.361f, 0.5f, 0.153f, 0.361f, 0.444f, 0.25f, 0.111f, 0.111f,
808 0.194f, 1.0f, 0.097f, 0.208f, 0.652f, 1.0f, 0.875f, 0.139f, 0.486f
810 static const float eax1_env_dampening[EAX_ENVIRONMENT_COUNT] = {
811 0.5f, 0.0f, 0.666f, 0.166f, 0.0f, 0.888f, 0.5f, 0.5f, 1.304f,
812 0.332f, 0.3f, 2.0f, 0.0f, 0.638f, 0.776f, 0.472f, 0.224f, 0.472f,
813 0.5f, 0.224f, 1.5f, 0.25f, 0.0f, 1.388f, 0.666f, 0.806f
815 HRESULT hr;
817 if(prim->effect == 0)
818 return E_PROP_ID_UNSUPPORTED;
820 hr = DSERR_INVALIDPARAM;
821 switch(propid)
823 case DSPROPERTY_EAX1_ALL:
824 if(cbPropData >= sizeof(EAX1_REVERBPROPERTIES))
826 union {
827 const void *v;
828 const EAX1_REVERBPROPERTIES *props;
829 } data = { pPropData };
831 if(data.props->dwEnvironment < EAX_ENVIRONMENT_COUNT)
833 /* NOTE: I'm not quite sure how to handle the volume. It's
834 * important to deal with since it can have a notable impact on
835 * the output levels, but given the default EAX1 environment
836 * volumes, they don't align with the gain/room volume for
837 * EAX2+ environments. Presuming the default volumes are
838 * correct, it's possible the reverb implementation was
839 * different and relied on different gains to get the intended
840 * output levels.
842 * Rather than just blindly applying the volume, we take the
843 * difference from the EAX1 environment's default volume and
844 * apply that as an offset to the EAX2 environment's volume.
846 EAX30LISTENERPROPERTIES env = EnvironmentDefaults[data.props->dwEnvironment];
847 LONG db_vol = clampI(
848 gain_to_mB(data.props->fVolume / eax1_env_volume[data.props->dwEnvironment]),
849 -10000, 10000
851 env.lRoom = clampI(env.lRoom + db_vol, -10000, 0);
852 env.flDecayTime = data.props->fDecayTime;
853 prim->deferred.eax1_volume = data.props->fVolume;
854 prim->deferred.eax1_dampening = data.props->fDamping;
855 ApplyReverbParams(prim, &env);
856 hr = DS_OK;
859 break;
861 case DSPROPERTY_EAX1_ENVIRONMENT:
862 if(cbPropData >= sizeof(DWORD))
864 union { const void *v; const DWORD *dw; } data = { pPropData };
866 if(*data.dw < EAX_ENVIRONMENT_COUNT)
868 prim->deferred.eax1_volume = eax1_env_volume[*data.dw];
869 prim->deferred.eax1_dampening = eax1_env_dampening[*data.dw];
870 ApplyReverbParams(prim, &EnvironmentDefaults[*data.dw]);
871 hr = DS_OK;
874 break;
876 case DSPROPERTY_EAX1_VOLUME:
877 if(cbPropData >= sizeof(FLOAT))
879 union { const void *v; const FLOAT *fl; } data = { pPropData };
880 LONG db_vol = clampI(
881 gain_to_mB(*data.fl / eax1_env_volume[prim->deferred.eax.dwEnvironment]),
882 -10000, 10000
884 LONG room_vol = clampI(
885 EnvironmentDefaults[prim->deferred.eax.dwEnvironment].lRoom + db_vol,
886 -10000, 0
889 prim->deferred.eax.lRoom = room_vol;
890 prim->deferred.eax1_volume = *data.fl;
891 alEffectf(prim->effect, AL_REVERB_GAIN, mB_to_gain(room_vol));
892 checkALError();
894 prim->dirty.bit.effect = 1;
895 hr = DS_OK;
897 break;
898 case DSPROPERTY_EAX1_DECAYTIME:
899 if(cbPropData >= sizeof(FLOAT))
901 union { const void *v; const FLOAT *fl; } data = { pPropData };
903 prim->deferred.eax.flDecayTime = *data.fl;
904 alEffectf(prim->effect, AL_REVERB_DECAY_TIME,
905 prim->deferred.eax.flDecayTime);
906 checkALError();
908 prim->dirty.bit.effect = 1;
909 hr = DS_OK;
911 break;
912 case DSPROPERTY_EAX1_DAMPING:
913 if(cbPropData >= sizeof(FLOAT))
915 union { const void *v; const FLOAT *fl; } data = { pPropData };
917 prim->deferred.eax1_dampening = *data.fl;
919 hr = DS_OK;
921 break;
923 default:
924 hr = E_PROP_ID_UNSUPPORTED;
925 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
926 break;
929 return hr;
932 HRESULT EAX1_Get(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
934 HRESULT hr;
936 if(prim->effect == 0)
937 return E_PROP_ID_UNSUPPORTED;
939 hr = DSERR_INVALIDPARAM;
940 switch(propid)
942 case DSPROPERTY_EAX1_ALL:
943 if(cbPropData >= sizeof(EAX1_REVERBPROPERTIES))
945 union {
946 void *v;
947 EAX1_REVERBPROPERTIES *props;
948 } data = { pPropData };
950 data.props->dwEnvironment = prim->deferred.eax.dwEnvironment;
951 data.props->fVolume = prim->deferred.eax1_volume;
952 data.props->fDecayTime = prim->deferred.eax.flDecayTime;
953 data.props->fDamping = prim->deferred.eax1_dampening;
955 *pcbReturned = sizeof(EAX1_REVERBPROPERTIES);
956 hr = DS_OK;
958 break;
960 case DSPROPERTY_EAX1_ENVIRONMENT:
961 if(cbPropData >= sizeof(DWORD))
963 union { void *v; DWORD *dw; } data = { pPropData };
965 *data.dw = prim->deferred.eax.dwEnvironment;
967 *pcbReturned = sizeof(DWORD);
968 hr = DS_OK;
970 break;
972 case DSPROPERTY_EAX1_VOLUME:
973 if(cbPropData >= sizeof(FLOAT))
975 union { void *v; FLOAT *fl; } data = { pPropData };
977 *data.fl = prim->deferred.eax1_volume;
979 *pcbReturned = sizeof(FLOAT);
980 hr = DS_OK;
982 break;
984 case DSPROPERTY_EAX1_DECAYTIME:
985 if(cbPropData >= sizeof(FLOAT))
987 union { void *v; FLOAT *fl; } data = { pPropData };
989 *data.fl = prim->deferred.eax.flDecayTime;
991 *pcbReturned = sizeof(FLOAT);
992 hr = DS_OK;
994 break;
996 case DSPROPERTY_EAX1_DAMPING:
997 if(cbPropData >= sizeof(FLOAT))
999 union { void *v; FLOAT *fl; } data = { pPropData };
1001 *data.fl = prim->deferred.eax1_dampening;
1003 *pcbReturned = sizeof(FLOAT);
1004 hr = DS_OK;
1006 break;
1008 default:
1009 hr = E_PROP_ID_UNSUPPORTED;
1010 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
1011 break;
1014 return hr;
1017 HRESULT EAX1Buffer_Set(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData)
1019 HRESULT hr;
1021 if(buf->filter[0] == 0)
1022 return E_PROP_ID_UNSUPPORTED;
1024 hr = DSERR_INVALIDPARAM;
1025 switch(propid)
1027 /* NOTE: DSPROPERTY_EAX1BUFFER_ALL is for EAX1BUFFER_REVERBPROPERTIES,
1028 * however that struct just contains the single ReverbMix float property.
1030 case DSPROPERTY_EAX1BUFFER_ALL:
1031 case DSPROPERTY_EAX1BUFFER_REVERBMIX:
1032 if(cbPropData >= sizeof(FLOAT))
1034 union { const void *v; const FLOAT *fl; } data = { pPropData };
1036 buf->deferred.eax.lRoom = gain_to_mB(*data.fl);
1037 buf->deferred.eax1_reverbmix = *data.fl;
1038 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_WET_PARAMS);
1040 buf->dirty.bit.wet_filter = 1;
1041 hr = DS_OK;
1043 break;
1045 default:
1046 hr = E_PROP_ID_UNSUPPORTED;
1047 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
1048 break;
1051 return hr;
1054 HRESULT EAX1Buffer_Get(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
1056 HRESULT hr;
1058 if(buf->filter[0] == 0)
1059 return E_PROP_ID_UNSUPPORTED;
1061 hr = DSERR_INVALIDPARAM;
1062 switch(propid)
1064 case DSPROPERTY_EAX1BUFFER_ALL:
1065 case DSPROPERTY_EAX1BUFFER_REVERBMIX:
1066 if(cbPropData >= sizeof(FLOAT))
1068 union { void *v; FLOAT *fl; } data = { pPropData };
1070 *data.fl = buf->deferred.eax1_reverbmix;
1071 *pcbReturned = sizeof(FLOAT);
1072 hr = DS_OK;
1074 break;
1076 default:
1077 hr = E_PROP_ID_UNSUPPORTED;
1078 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
1079 break;
1082 return hr;