Use the EAXReverb effect instead of standard reverb
[dsound-openal.git] / eax.c
blob597fe85475f184d5e438fc2febc582afcb378b49
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_EAXREVERB_DENSITY,
60 clampF(powf(props->flEnvironmentSize, 3.0f) / 16.0f, 0.0f, 1.0f)
62 alEffectf(prim->effect, AL_EAXREVERB_DIFFUSION, props->flEnvironmentDiffusion);
64 alEffectf(prim->effect, AL_EAXREVERB_GAIN, mB_to_gain(props->lRoom));
65 alEffectf(prim->effect, AL_EAXREVERB_GAINHF, mB_to_gain(props->lRoomHF));
66 alEffectf(prim->effect, AL_EAXREVERB_GAINLF, mB_to_gain(props->lRoomLF));
68 alEffectf(prim->effect, AL_EAXREVERB_DECAY_TIME, props->flDecayTime);
69 alEffectf(prim->effect, AL_EAXREVERB_DECAY_HFRATIO, props->flDecayHFRatio);
70 alEffectf(prim->effect, AL_EAXREVERB_DECAY_LFRATIO, props->flDecayLFRatio);
72 alEffectf(prim->effect, AL_EAXREVERB_REFLECTIONS_GAIN, mB_to_gain(props->lReflections));
73 alEffectf(prim->effect, AL_EAXREVERB_REFLECTIONS_DELAY, props->flReflectionsDelay);
74 alEffectfv(prim->effect, AL_EAXREVERB_REFLECTIONS_PAN, &props->vReflectionsPan.x);
76 alEffectf(prim->effect, AL_EAXREVERB_LATE_REVERB_GAIN, mB_to_gain(props->lReverb));
77 alEffectf(prim->effect, AL_EAXREVERB_LATE_REVERB_DELAY, props->flReverbDelay);
78 alEffectfv(prim->effect, AL_EAXREVERB_LATE_REVERB_PAN, &props->vReverbPan.x);
80 alEffectf(prim->effect, AL_EAXREVERB_ECHO_TIME, props->flEchoTime);
81 alEffectf(prim->effect, AL_EAXREVERB_ECHO_DEPTH, props->flEchoDepth);
83 alEffectf(prim->effect, AL_EAXREVERB_MODULATION_TIME, props->flModulationTime);
84 alEffectf(prim->effect, AL_EAXREVERB_MODULATION_DEPTH, props->flModulationDepth);
86 alEffectf(prim->effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF,
87 mB_to_gain(props->flAirAbsorptionHF));
89 alEffectf(prim->effect, AL_EAXREVERB_HFREFERENCE, props->flHFReference);
90 alEffectf(prim->effect, AL_EAXREVERB_LFREFERENCE, props->flLFReference);
92 alEffectf(prim->effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR, props->flRoomRolloffFactor);
94 alEffecti(prim->effect, AL_EAXREVERB_DECAY_HFLIMIT,
95 (props->dwFlags&EAX30LISTENERFLAGS_DECAYHFLIMIT) ?
96 AL_TRUE : AL_FALSE);
98 checkALError();
100 prim->dirty.bit.effect = 1;
103 #define APPLY_DRY_PARAMS 1
104 #define APPLY_WET_PARAMS 2
105 static void ApplyFilterParams(DS8Buffer *buf, const EAX20BUFFERPROPERTIES *props, int apply)
107 /* The LFRatio properties determine how much the given level applies to low
108 * frequencies as well as high frequencies. Given that the high frequency
109 * levels are specified relative to the low, they should increase as the
110 * low frequency levels reduce.
112 FLOAT occl = props->lOcclusion * props->flOcclusionLFRatio;
113 FLOAT occlhf = props->lOcclusion * (1.0f-props->flOcclusionLFRatio);
115 if((apply&APPLY_DRY_PARAMS))
117 FLOAT obstr = props->lObstruction * props->flObstructionLFRatio;
118 FLOAT obstrhf = props->lObstruction * (1.0f-props->flObstructionLFRatio);
119 FLOAT mb = props->lDirect + obstr + occl;
120 FLOAT mbhf = props->lDirectHF + obstrhf + occlhf;
122 alFilterf(buf->filter[0], AL_LOWPASS_GAIN, mB_to_gain(mb));
123 alFilterf(buf->filter[0], AL_LOWPASS_GAINHF, mB_to_gain(mbhf));
125 if((apply&APPLY_WET_PARAMS))
127 FLOAT occlroom = props->flOcclusionRoomRatio;
128 FLOAT mb = props->lRoom + occlroom*occl;
129 FLOAT mbhf = props->lRoomHF + occlroom*occlhf;
131 alFilterf(buf->filter[1], AL_LOWPASS_GAIN, mB_to_gain(mb));
132 alFilterf(buf->filter[1], AL_LOWPASS_GAINHF, mB_to_gain(mbhf));
134 checkALError();
138 HRESULT EAX2_Set(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData)
140 HRESULT hr;
142 if(prim->effect == 0)
143 return E_PROP_ID_UNSUPPORTED;
145 hr = DSERR_INVALIDPARAM;
146 switch(propid)
148 case DSPROPERTY_EAX20LISTENER_NONE: /* not setting any property, just applying */
149 hr = DS_OK;
150 break;
152 case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS:
153 if(cbPropData >= sizeof(EAX20LISTENERPROPERTIES))
155 union {
156 const void *v;
157 const EAX20LISTENERPROPERTIES *props;
158 } data = { pPropData };
159 EAX30LISTENERPROPERTIES props3 = REVERB_PRESET_GENERIC;
160 if(data.props->dwEnvironment < EAX_ENVIRONMENT_COUNT)
162 props3 = EnvironmentDefaults[data.props->dwEnvironment];
163 props3.dwEnvironment = data.props->dwEnvironment;
165 props3.flEnvironmentSize = data.props->flEnvironmentSize;
166 props3.flEnvironmentDiffusion = data.props->flEnvironmentDiffusion;
167 props3.lRoom = data.props->lRoom;
168 props3.lRoomHF = data.props->lRoomHF;
169 props3.flDecayTime = data.props->flDecayTime;
170 props3.flDecayHFRatio = data.props->flDecayHFRatio;
171 props3.lReflections = data.props->lReflections;
172 props3.flReflectionsDelay = data.props->flReflectionsDelay;
173 props3.lReverb = data.props->lReverb;
174 props3.flReverbDelay = data.props->flReverbDelay;
175 props3.flAirAbsorptionHF = data.props->flAirAbsorptionHF;
176 props3.flRoomRolloffFactor = data.props->flRoomRolloffFactor;
177 props3.dwFlags = data.props->dwFlags;
179 ApplyReverbParams(prim, &props3);
180 hr = DS_OK;
182 break;
184 case DSPROPERTY_EAX20LISTENER_ROOM:
185 if(cbPropData >= sizeof(LONG))
187 union { const void *v; const LONG *l; } data = { pPropData };
189 prim->deferred.eax.lRoom = *data.l;
190 alEffectf(prim->effect, AL_EAXREVERB_GAIN,
191 mB_to_gain(prim->deferred.eax.lRoom));
192 checkALError();
194 prim->dirty.bit.effect = 1;
195 hr = DS_OK;
197 break;
198 case DSPROPERTY_EAX20LISTENER_ROOMHF:
199 if(cbPropData >= sizeof(LONG))
201 union { const void *v; const LONG *l; } data = { pPropData };
203 prim->deferred.eax.lRoomHF = *data.l;
204 alEffectf(prim->effect, AL_EAXREVERB_GAINHF,
205 mB_to_gain(prim->deferred.eax.lRoomHF));
206 checkALError();
208 prim->dirty.bit.effect = 1;
209 hr = DS_OK;
211 break;
213 case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR:
214 if(cbPropData >= sizeof(FLOAT))
216 union { const void *v; const FLOAT *fl; } data = { pPropData };
218 prim->deferred.eax.flRoomRolloffFactor = *data.fl;
219 alEffectf(prim->effect, AL_EAXREVERB_ROOM_ROLLOFF_FACTOR,
220 prim->deferred.eax.flRoomRolloffFactor);
221 checkALError();
223 prim->dirty.bit.effect = 1;
224 hr = DS_OK;
226 break;
228 case DSPROPERTY_EAX20LISTENER_DECAYTIME:
229 if(cbPropData >= sizeof(FLOAT))
231 union { const void *v; const FLOAT *fl; } data = { pPropData };
233 prim->deferred.eax.flDecayTime = *data.fl;
234 alEffectf(prim->effect, AL_EAXREVERB_DECAY_TIME,
235 prim->deferred.eax.flDecayTime);
236 checkALError();
238 prim->dirty.bit.effect = 1;
239 hr = DS_OK;
241 break;
242 case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO:
243 if(cbPropData >= sizeof(FLOAT))
245 union { const void *v; const FLOAT *fl; } data = { pPropData };
247 prim->deferred.eax.flDecayHFRatio = *data.fl;
248 alEffectf(prim->effect, AL_EAXREVERB_DECAY_HFRATIO,
249 prim->deferred.eax.flDecayHFRatio);
250 checkALError();
252 prim->dirty.bit.effect = 1;
253 hr = DS_OK;
255 break;
257 case DSPROPERTY_EAX20LISTENER_REFLECTIONS:
258 if(cbPropData >= sizeof(LONG))
260 union { const void *v; const LONG *l; } data = { pPropData };
262 prim->deferred.eax.lReflections = *data.l;
263 alEffectf(prim->effect, AL_EAXREVERB_REFLECTIONS_GAIN,
264 mB_to_gain(prim->deferred.eax.lReflections));
265 checkALError();
267 prim->dirty.bit.effect = 1;
268 hr = DS_OK;
270 break;
271 case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY:
272 if(cbPropData >= sizeof(FLOAT))
274 union { const void *v; const FLOAT *fl; } data = { pPropData };
276 prim->deferred.eax.flReflectionsDelay = *data.fl;
277 alEffectf(prim->effect, AL_EAXREVERB_REFLECTIONS_DELAY,
278 prim->deferred.eax.flReflectionsDelay);
279 checkALError();
281 prim->dirty.bit.effect = 1;
282 hr = DS_OK;
284 break;
286 case DSPROPERTY_EAX20LISTENER_REVERB:
287 if(cbPropData >= sizeof(LONG))
289 union { const void *v; const LONG *l; } data = { pPropData };
291 prim->deferred.eax.lReverb = *data.l;
292 alEffectf(prim->effect, AL_EAXREVERB_LATE_REVERB_GAIN,
293 mB_to_gain(prim->deferred.eax.lReverb));
294 checkALError();
296 prim->dirty.bit.effect = 1;
297 hr = DS_OK;
299 break;
300 case DSPROPERTY_EAX20LISTENER_REVERBDELAY:
301 if(cbPropData >= sizeof(FLOAT))
303 union { const void *v; const FLOAT *fl; } data = { pPropData };
305 prim->deferred.eax.flReverbDelay = *data.fl;
306 alEffectf(prim->effect, AL_EAXREVERB_LATE_REVERB_DELAY,
307 prim->deferred.eax.flReverbDelay);
308 checkALError();
310 prim->dirty.bit.effect = 1;
311 hr = DS_OK;
313 break;
315 case DSPROPERTY_EAX20LISTENER_ENVIRONMENT:
316 if(cbPropData >= sizeof(DWORD))
318 union { const void *v; const DWORD *dw; } data = { pPropData };
319 if(*data.dw < EAX_ENVIRONMENT_COUNT)
321 ApplyReverbParams(prim, &EnvironmentDefaults[*data.dw]);
322 hr = DS_OK;
325 break;
327 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE:
328 if(cbPropData >= sizeof(FLOAT))
330 union { const void *v; const FLOAT *fl; } data = { pPropData };
331 if(*data.fl >= 1.0f && *data.fl <= 100.0f)
333 float scale = (*data.fl)/prim->deferred.eax.flEnvironmentSize;
335 prim->deferred.eax.flEnvironmentSize = *data.fl;
337 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_DECAYTIMESCALE))
339 prim->deferred.eax.flDecayTime *= scale;
340 prim->deferred.eax.flDecayTime = clampF(prim->deferred.eax.flDecayTime, 0.1f, 20.0f);
342 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REFLECTIONSSCALE))
344 prim->deferred.eax.lReflections -= gain_to_mB(scale);
345 prim->deferred.eax.lReflections = clampI(prim->deferred.eax.lReflections, -10000, 1000);
347 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REFLECTIONSDELAYSCALE))
349 prim->deferred.eax.flReflectionsDelay *= scale;
350 prim->deferred.eax.flReflectionsDelay = clampF(prim->deferred.eax.flReflectionsDelay, 0.0f, 0.3f);
352 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REVERBSCALE))
354 prim->deferred.eax.lReverb -= gain_to_mB(scale);
355 prim->deferred.eax.lReverb = clampI(prim->deferred.eax.lReverb, -10000, 2000);
357 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_REVERBDELAYSCALE))
359 prim->deferred.eax.flReverbDelay *= scale;
360 prim->deferred.eax.flReverbDelay = clampF(prim->deferred.eax.flReverbDelay, 0.0f, 0.1f);
362 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_ECHOTIMESCALE))
364 prim->deferred.eax.flEchoTime *= scale;
365 prim->deferred.eax.flEchoTime = clampF(prim->deferred.eax.flEchoTime, 0.075f, 0.25f);
367 if((prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_MODTIMESCALE))
369 prim->deferred.eax.flModulationTime *= scale;
370 prim->deferred.eax.flModulationTime = clampF(prim->deferred.eax.flModulationTime, 0.04f, 4.0f);
373 ApplyReverbParams(prim, &prim->deferred.eax);
374 hr = DS_OK;
377 break;
378 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION:
379 if(cbPropData >= sizeof(FLOAT))
381 union { const void *v; const FLOAT *fl; } data = { pPropData };
383 prim->deferred.eax.flEnvironmentDiffusion = *data.fl;
384 alEffectf(prim->effect, AL_EAXREVERB_DIFFUSION,
385 prim->deferred.eax.flEnvironmentDiffusion);
386 checkALError();
388 prim->dirty.bit.effect = 1;
389 hr = DS_OK;
391 break;
393 case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF:
394 if(cbPropData >= sizeof(FLOAT))
396 union { const void *v; const FLOAT *fl; } data = { pPropData };
398 prim->deferred.eax.flAirAbsorptionHF = *data.fl;
399 alEffectf(prim->effect, AL_EAXREVERB_AIR_ABSORPTION_GAINHF,
400 mB_to_gain(prim->deferred.eax.flAirAbsorptionHF));
401 checkALError();
403 prim->dirty.bit.effect = 1;
404 hr = DS_OK;
406 break;
408 case DSPROPERTY_EAX20LISTENER_FLAGS:
409 if(cbPropData >= sizeof(DWORD))
411 union { const void *v; const DWORD *dw; } data = { pPropData };
413 prim->deferred.eax.dwFlags = *data.dw;
414 alEffecti(prim->effect, AL_EAXREVERB_DECAY_HFLIMIT,
415 (prim->deferred.eax.dwFlags&EAX30LISTENERFLAGS_DECAYHFLIMIT) ?
416 AL_TRUE : AL_FALSE);
417 checkALError();
419 prim->dirty.bit.effect = 1;
420 hr = DS_OK;
422 break;
424 default:
425 hr = E_PROP_ID_UNSUPPORTED;
426 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
427 break;
430 return hr;
433 HRESULT EAX2_Get(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
435 HRESULT hr;
437 if(prim->effect == 0)
438 return E_PROP_ID_UNSUPPORTED;
440 #define GET_PROP(src, T) do { \
441 if(cbPropData >= sizeof(T)) \
443 union { void *v; T *props; } data = { pPropData }; \
444 *data.props = src; \
445 *pcbReturned = sizeof(T); \
446 hr = DS_OK; \
448 } while(0)
449 hr = DSERR_INVALIDPARAM;
450 switch(propid)
452 case DSPROPERTY_EAX20LISTENER_NONE:
453 *pcbReturned = 0;
454 hr = DS_OK;
455 break;
457 case DSPROPERTY_EAX20LISTENER_ALLPARAMETERS:
458 GET_PROP(EAX3To2(&prim->deferred.eax), EAX20LISTENERPROPERTIES);
459 break;
461 case DSPROPERTY_EAX20LISTENER_ROOM:
462 GET_PROP(prim->deferred.eax.lRoom, LONG);
463 break;
464 case DSPROPERTY_EAX20LISTENER_ROOMHF:
465 GET_PROP(prim->deferred.eax.lRoomHF, LONG);
466 break;
468 case DSPROPERTY_EAX20LISTENER_ROOMROLLOFFFACTOR:
469 GET_PROP(prim->deferred.eax.flRoomRolloffFactor, FLOAT);
470 break;
472 case DSPROPERTY_EAX20LISTENER_DECAYTIME:
473 GET_PROP(prim->deferred.eax.flDecayTime, FLOAT);
474 break;
475 case DSPROPERTY_EAX20LISTENER_DECAYHFRATIO:
476 GET_PROP(prim->deferred.eax.flDecayHFRatio, FLOAT);
477 break;
479 case DSPROPERTY_EAX20LISTENER_REFLECTIONS:
480 GET_PROP(prim->deferred.eax.lReflections, LONG);
481 break;
482 case DSPROPERTY_EAX20LISTENER_REFLECTIONSDELAY:
483 GET_PROP(prim->deferred.eax.flReflectionsDelay, FLOAT);
484 break;
486 case DSPROPERTY_EAX20LISTENER_REVERB:
487 GET_PROP(prim->deferred.eax.lReverb, LONG);
488 break;
489 case DSPROPERTY_EAX20LISTENER_REVERBDELAY:
490 GET_PROP(prim->deferred.eax.flReverbDelay, FLOAT);
491 break;
493 case DSPROPERTY_EAX20LISTENER_ENVIRONMENT:
494 GET_PROP(prim->deferred.eax.dwEnvironment, DWORD);
495 break;
497 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTSIZE:
498 GET_PROP(prim->deferred.eax.flEnvironmentSize, FLOAT);
499 break;
500 case DSPROPERTY_EAX20LISTENER_ENVIRONMENTDIFFUSION:
501 GET_PROP(prim->deferred.eax.flEnvironmentDiffusion, FLOAT);
502 break;
504 case DSPROPERTY_EAX20LISTENER_AIRABSORPTIONHF:
505 GET_PROP(prim->deferred.eax.flAirAbsorptionHF, FLOAT);
506 break;
508 case DSPROPERTY_EAX20LISTENER_FLAGS:
509 GET_PROP(prim->deferred.eax.dwFlags&EAX2LISTENERFLAGS_MASK, DWORD);
510 break;
512 default:
513 hr = E_PROP_ID_UNSUPPORTED;
514 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
515 break;
517 #undef GET_PROP
519 return hr;
523 HRESULT EAX2Buffer_Set(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData)
525 HRESULT hr;
527 if(buf->filter[0] == 0)
528 return E_PROP_ID_UNSUPPORTED;
530 hr = DSERR_INVALIDPARAM;
531 switch(propid)
533 case DSPROPERTY_EAX20BUFFER_NONE: /* not setting any property, just applying */
534 hr = DS_OK;
535 break;
537 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS:
538 if(cbPropData >= sizeof(EAX20BUFFERPROPERTIES))
540 union {
541 const void *v;
542 const EAX20BUFFERPROPERTIES *props;
543 } data = { pPropData };
545 buf->deferred.eax = *data.props;
546 ApplyFilterParams(buf, data.props, APPLY_DRY_PARAMS|APPLY_WET_PARAMS);
548 buf->dirty.bit.dry_filter = 1;
549 buf->dirty.bit.wet_filter = 1;
550 buf->dirty.bit.room_rolloff = 1;
551 buf->dirty.bit.cone_outsidevolumehf = 1;
552 buf->dirty.bit.air_absorb = 1;
553 buf->dirty.bit.flags = 1;
554 hr = DS_OK;
556 break;
558 case DSPROPERTY_EAX20BUFFER_DIRECT:
559 if(cbPropData >= sizeof(LONG))
561 union { const void *v; const LONG *l; } data = { pPropData };
563 buf->deferred.eax.lDirect = *data.l;
564 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
566 buf->dirty.bit.dry_filter = 1;
567 hr = DS_OK;
569 break;
570 case DSPROPERTY_EAX20BUFFER_DIRECTHF:
571 if(cbPropData >= sizeof(LONG))
573 union { const void *v; const LONG *l; } data = { pPropData };
575 buf->deferred.eax.lDirectHF = *data.l;
576 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
578 buf->dirty.bit.dry_filter = 1;
579 hr = DS_OK;
581 break;
583 case DSPROPERTY_EAX20BUFFER_ROOM:
584 if(cbPropData >= sizeof(LONG))
586 union { const void *v; const LONG *l; } data = { pPropData };
588 buf->deferred.eax.lRoom = *data.l;
589 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_WET_PARAMS);
591 buf->dirty.bit.wet_filter = 1;
592 hr = DS_OK;
594 break;
595 case DSPROPERTY_EAX20BUFFER_ROOMHF:
596 if(cbPropData >= sizeof(LONG))
598 union { const void *v; const LONG *l; } data = { pPropData };
600 buf->deferred.eax.lRoomHF = *data.l;
601 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_WET_PARAMS);
603 buf->dirty.bit.wet_filter = 1;
604 hr = DS_OK;
606 break;
608 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR:
609 if(cbPropData >= sizeof(FLOAT))
611 union { const void *v; const FLOAT *fl; } data = { pPropData };
613 buf->deferred.eax.flRoomRolloffFactor = *data.fl;
615 buf->dirty.bit.room_rolloff = 1;
616 hr = DS_OK;
618 break;
620 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION:
621 if(cbPropData >= sizeof(LONG))
623 union { const void *v; const LONG *l; } data = { pPropData };
625 buf->deferred.eax.lObstruction = *data.l;
626 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
628 buf->dirty.bit.dry_filter = 1;
629 hr = DS_OK;
631 break;
632 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO:
633 if(cbPropData >= sizeof(FLOAT))
635 union { const void *v; const FLOAT *fl; } data = { pPropData };
637 buf->deferred.eax.flObstructionLFRatio = *data.fl;
638 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS);
640 buf->dirty.bit.dry_filter = 1;
641 hr = DS_OK;
643 break;
645 case DSPROPERTY_EAX20BUFFER_OCCLUSION:
646 if(cbPropData >= sizeof(LONG))
648 union { const void *v; const LONG *l; } data = { pPropData };
650 buf->deferred.eax.lOcclusion = *data.l;
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_OCCLUSIONLFRATIO:
659 if(cbPropData >= sizeof(FLOAT))
661 union { const void *v; const FLOAT *fl; } data = { pPropData };
663 buf->deferred.eax.flOcclusionLFRatio = *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;
671 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO:
672 if(cbPropData >= sizeof(FLOAT))
674 union { const void *v; const FLOAT *fl; } data = { pPropData };
676 buf->deferred.eax.flOcclusionRoomRatio = *data.fl;
677 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_DRY_PARAMS|APPLY_WET_PARAMS);
679 buf->dirty.bit.dry_filter = 1;
680 buf->dirty.bit.wet_filter = 1;
681 hr = DS_OK;
683 break;
685 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF:
686 if(cbPropData >= sizeof(LONG))
688 union { const void *v; const LONG *l; } data = { pPropData };
690 buf->deferred.eax.lOutsideVolumeHF = *data.l;
692 buf->dirty.bit.cone_outsidevolumehf = 1;
693 hr = DS_OK;
695 break;
697 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR:
698 if(cbPropData >= sizeof(FLOAT))
700 union { const void *v; const FLOAT *fl; } data = { pPropData };
702 buf->deferred.eax.flAirAbsorptionFactor = *data.fl;
704 buf->dirty.bit.air_absorb = 1;
705 hr = DS_OK;
707 break;
709 case DSPROPERTY_EAX20BUFFER_FLAGS:
710 if(cbPropData >= sizeof(DWORD))
712 union { const void *v; const DWORD *dw; } data = { pPropData };
714 buf->deferred.eax.dwFlags = *data.dw;
716 buf->dirty.bit.flags = 1;
717 hr = DS_OK;
719 break;
721 default:
722 hr = E_PROP_ID_UNSUPPORTED;
723 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
724 break;
727 return hr;
730 HRESULT EAX2Buffer_Get(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
732 HRESULT hr;
734 if(buf->filter[0] == 0)
735 return E_PROP_ID_UNSUPPORTED;
737 #define GET_PROP(src, T) do { \
738 if(cbPropData >= sizeof(T)) \
740 union { void *v; T *props; } data = { pPropData }; \
741 *data.props = src; \
742 *pcbReturned = sizeof(T); \
743 hr = DS_OK; \
745 } while(0)
746 hr = DSERR_INVALIDPARAM;
747 switch(propid)
749 case DSPROPERTY_EAX20BUFFER_NONE:
750 *pcbReturned = 0;
751 hr = DS_OK;
752 break;
754 case DSPROPERTY_EAX20BUFFER_ALLPARAMETERS:
755 GET_PROP(buf->deferred.eax, EAX20BUFFERPROPERTIES);
756 break;
758 case DSPROPERTY_EAX20BUFFER_DIRECT:
759 GET_PROP(buf->deferred.eax.lDirect, LONG);
760 break;
761 case DSPROPERTY_EAX20BUFFER_DIRECTHF:
762 GET_PROP(buf->deferred.eax.lDirectHF, LONG);
763 break;
765 case DSPROPERTY_EAX20BUFFER_ROOM:
766 GET_PROP(buf->deferred.eax.lRoom, LONG);
767 break;
768 case DSPROPERTY_EAX20BUFFER_ROOMHF:
769 GET_PROP(buf->deferred.eax.lRoomHF, LONG);
770 break;
772 case DSPROPERTY_EAX20BUFFER_ROOMROLLOFFFACTOR:
773 GET_PROP(buf->deferred.eax.flRoomRolloffFactor, FLOAT);
774 break;
776 case DSPROPERTY_EAX20BUFFER_OBSTRUCTION:
777 GET_PROP(buf->deferred.eax.lObstruction, LONG);
778 break;
779 case DSPROPERTY_EAX20BUFFER_OBSTRUCTIONLFRATIO:
780 GET_PROP(buf->deferred.eax.flObstructionLFRatio, FLOAT);
781 break;
783 case DSPROPERTY_EAX20BUFFER_OCCLUSION:
784 GET_PROP(buf->deferred.eax.lOcclusion, LONG);
785 break;
786 case DSPROPERTY_EAX20BUFFER_OCCLUSIONLFRATIO:
787 GET_PROP(buf->deferred.eax.flOcclusionLFRatio, FLOAT);
788 break;
789 case DSPROPERTY_EAX20BUFFER_OCCLUSIONROOMRATIO:
790 GET_PROP(buf->deferred.eax.flOcclusionRoomRatio, FLOAT);
791 break;
793 case DSPROPERTY_EAX20BUFFER_OUTSIDEVOLUMEHF:
794 GET_PROP(buf->deferred.eax.lOutsideVolumeHF, LONG);
795 break;
797 case DSPROPERTY_EAX20BUFFER_AIRABSORPTIONFACTOR:
798 GET_PROP(buf->deferred.eax.flAirAbsorptionFactor, FLOAT);
799 break;
801 case DSPROPERTY_EAX20BUFFER_FLAGS:
802 GET_PROP(buf->deferred.eax.dwFlags, DWORD);
803 break;
805 default:
806 hr = E_PROP_ID_UNSUPPORTED;
807 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
808 break;
810 #undef GET_PROP
812 return hr;
816 HRESULT EAX1_Set(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData)
818 static const float eax1_env_volume[EAX_ENVIRONMENT_COUNT] = {
819 0.5f, 0.25f, 0.417f, 0.653f, 0.208f, 0.5f, 0.403f, 0.5f, 0.5f,
820 0.361f, 0.5f, 0.153f, 0.361f, 0.444f, 0.25f, 0.111f, 0.111f,
821 0.194f, 1.0f, 0.097f, 0.208f, 0.652f, 1.0f, 0.875f, 0.139f, 0.486f
823 static const float eax1_env_dampening[EAX_ENVIRONMENT_COUNT] = {
824 0.5f, 0.0f, 0.666f, 0.166f, 0.0f, 0.888f, 0.5f, 0.5f, 1.304f,
825 0.332f, 0.3f, 2.0f, 0.0f, 0.638f, 0.776f, 0.472f, 0.224f, 0.472f,
826 0.5f, 0.224f, 1.5f, 0.25f, 0.0f, 1.388f, 0.666f, 0.806f
828 HRESULT hr;
830 if(prim->effect == 0)
831 return E_PROP_ID_UNSUPPORTED;
833 hr = DSERR_INVALIDPARAM;
834 switch(propid)
836 case DSPROPERTY_EAX1_ALL:
837 if(cbPropData >= sizeof(EAX1_REVERBPROPERTIES))
839 union {
840 const void *v;
841 const EAX1_REVERBPROPERTIES *props;
842 } data = { pPropData };
844 if(data.props->dwEnvironment < EAX_ENVIRONMENT_COUNT)
846 /* NOTE: I'm not quite sure how to handle the volume. It's
847 * important to deal with since it can have a notable impact on
848 * the output levels, but given the default EAX1 environment
849 * volumes, they don't align with the gain/room volume for
850 * EAX2+ environments. Presuming the default volumes are
851 * correct, it's possible the reverb implementation was
852 * different and relied on different gains to get the intended
853 * output levels.
855 * Rather than just blindly applying the volume, we take the
856 * difference from the EAX1 environment's default volume and
857 * apply that as an offset to the EAX2 environment's volume.
859 EAX30LISTENERPROPERTIES env = EnvironmentDefaults[data.props->dwEnvironment];
860 LONG db_vol = clampI(
861 gain_to_mB(data.props->fVolume / eax1_env_volume[data.props->dwEnvironment]),
862 -10000, 10000
864 env.lRoom = clampI(env.lRoom + db_vol, -10000, 0);
865 env.flDecayTime = data.props->fDecayTime;
866 prim->deferred.eax1_volume = data.props->fVolume;
867 prim->deferred.eax1_dampening = data.props->fDamping;
868 ApplyReverbParams(prim, &env);
869 hr = DS_OK;
872 break;
874 case DSPROPERTY_EAX1_ENVIRONMENT:
875 if(cbPropData >= sizeof(DWORD))
877 union { const void *v; const DWORD *dw; } data = { pPropData };
879 if(*data.dw < EAX_ENVIRONMENT_COUNT)
881 prim->deferred.eax1_volume = eax1_env_volume[*data.dw];
882 prim->deferred.eax1_dampening = eax1_env_dampening[*data.dw];
883 ApplyReverbParams(prim, &EnvironmentDefaults[*data.dw]);
884 hr = DS_OK;
887 break;
889 case DSPROPERTY_EAX1_VOLUME:
890 if(cbPropData >= sizeof(FLOAT))
892 union { const void *v; const FLOAT *fl; } data = { pPropData };
893 LONG db_vol = clampI(
894 gain_to_mB(*data.fl / eax1_env_volume[prim->deferred.eax.dwEnvironment]),
895 -10000, 10000
897 LONG room_vol = clampI(
898 EnvironmentDefaults[prim->deferred.eax.dwEnvironment].lRoom + db_vol,
899 -10000, 0
902 prim->deferred.eax.lRoom = room_vol;
903 prim->deferred.eax1_volume = *data.fl;
904 alEffectf(prim->effect, AL_EAXREVERB_GAIN, mB_to_gain(room_vol));
905 checkALError();
907 prim->dirty.bit.effect = 1;
908 hr = DS_OK;
910 break;
911 case DSPROPERTY_EAX1_DECAYTIME:
912 if(cbPropData >= sizeof(FLOAT))
914 union { const void *v; const FLOAT *fl; } data = { pPropData };
916 prim->deferred.eax.flDecayTime = *data.fl;
917 alEffectf(prim->effect, AL_EAXREVERB_DECAY_TIME,
918 prim->deferred.eax.flDecayTime);
919 checkALError();
921 prim->dirty.bit.effect = 1;
922 hr = DS_OK;
924 break;
925 case DSPROPERTY_EAX1_DAMPING:
926 if(cbPropData >= sizeof(FLOAT))
928 union { const void *v; const FLOAT *fl; } data = { pPropData };
930 prim->deferred.eax1_dampening = *data.fl;
932 hr = DS_OK;
934 break;
936 default:
937 hr = E_PROP_ID_UNSUPPORTED;
938 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
939 break;
942 return hr;
945 HRESULT EAX1_Get(DS8Primary *prim, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
947 HRESULT hr;
949 if(prim->effect == 0)
950 return E_PROP_ID_UNSUPPORTED;
952 hr = DSERR_INVALIDPARAM;
953 switch(propid)
955 case DSPROPERTY_EAX1_ALL:
956 if(cbPropData >= sizeof(EAX1_REVERBPROPERTIES))
958 union {
959 void *v;
960 EAX1_REVERBPROPERTIES *props;
961 } data = { pPropData };
963 data.props->dwEnvironment = prim->deferred.eax.dwEnvironment;
964 data.props->fVolume = prim->deferred.eax1_volume;
965 data.props->fDecayTime = prim->deferred.eax.flDecayTime;
966 data.props->fDamping = prim->deferred.eax1_dampening;
968 *pcbReturned = sizeof(EAX1_REVERBPROPERTIES);
969 hr = DS_OK;
971 break;
973 case DSPROPERTY_EAX1_ENVIRONMENT:
974 if(cbPropData >= sizeof(DWORD))
976 union { void *v; DWORD *dw; } data = { pPropData };
978 *data.dw = prim->deferred.eax.dwEnvironment;
980 *pcbReturned = sizeof(DWORD);
981 hr = DS_OK;
983 break;
985 case DSPROPERTY_EAX1_VOLUME:
986 if(cbPropData >= sizeof(FLOAT))
988 union { void *v; FLOAT *fl; } data = { pPropData };
990 *data.fl = prim->deferred.eax1_volume;
992 *pcbReturned = sizeof(FLOAT);
993 hr = DS_OK;
995 break;
997 case DSPROPERTY_EAX1_DECAYTIME:
998 if(cbPropData >= sizeof(FLOAT))
1000 union { void *v; FLOAT *fl; } data = { pPropData };
1002 *data.fl = prim->deferred.eax.flDecayTime;
1004 *pcbReturned = sizeof(FLOAT);
1005 hr = DS_OK;
1007 break;
1009 case DSPROPERTY_EAX1_DAMPING:
1010 if(cbPropData >= sizeof(FLOAT))
1012 union { void *v; FLOAT *fl; } data = { pPropData };
1014 *data.fl = prim->deferred.eax1_dampening;
1016 *pcbReturned = sizeof(FLOAT);
1017 hr = DS_OK;
1019 break;
1021 default:
1022 hr = E_PROP_ID_UNSUPPORTED;
1023 FIXME("Unhandled listener propid: 0x%08lx\n", propid);
1024 break;
1027 return hr;
1030 HRESULT EAX1Buffer_Set(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData)
1032 HRESULT hr;
1034 if(buf->filter[0] == 0)
1035 return E_PROP_ID_UNSUPPORTED;
1037 hr = DSERR_INVALIDPARAM;
1038 switch(propid)
1040 /* NOTE: DSPROPERTY_EAX1BUFFER_ALL is for EAX1BUFFER_REVERBPROPERTIES,
1041 * however that struct just contains the single ReverbMix float property.
1043 case DSPROPERTY_EAX1BUFFER_ALL:
1044 case DSPROPERTY_EAX1BUFFER_REVERBMIX:
1045 if(cbPropData >= sizeof(FLOAT))
1047 union { const void *v; const FLOAT *fl; } data = { pPropData };
1049 buf->deferred.eax.lRoom = gain_to_mB(*data.fl);
1050 buf->deferred.eax1_reverbmix = *data.fl;
1051 ApplyFilterParams(buf, &buf->deferred.eax, APPLY_WET_PARAMS);
1053 buf->dirty.bit.wet_filter = 1;
1054 hr = DS_OK;
1056 break;
1058 default:
1059 hr = E_PROP_ID_UNSUPPORTED;
1060 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
1061 break;
1064 return hr;
1067 HRESULT EAX1Buffer_Get(DS8Buffer *buf, DWORD propid, void *pPropData, ULONG cbPropData, ULONG *pcbReturned)
1069 HRESULT hr;
1071 if(buf->filter[0] == 0)
1072 return E_PROP_ID_UNSUPPORTED;
1074 hr = DSERR_INVALIDPARAM;
1075 switch(propid)
1077 case DSPROPERTY_EAX1BUFFER_ALL:
1078 case DSPROPERTY_EAX1BUFFER_REVERBMIX:
1079 if(cbPropData >= sizeof(FLOAT))
1081 union { void *v; FLOAT *fl; } data = { pPropData };
1083 *data.fl = buf->deferred.eax1_reverbmix;
1084 *pcbReturned = sizeof(FLOAT);
1085 hr = DS_OK;
1087 break;
1089 default:
1090 hr = E_PROP_ID_UNSUPPORTED;
1091 FIXME("Unhandled buffer propid: 0x%08lx\n", propid);
1092 break;
1095 return hr;