Move EAX files to their own sub-directory
[openal-soft.git] / al / effects / chorus.cpp
blobb612a6c163e7a98957c448c3c1e28af4c29d2dbb
2 #include "config.h"
4 #include <stdexcept>
6 #include "AL/al.h"
7 #include "AL/efx.h"
9 #include "alc/effects/base.h"
10 #include "aloptional.h"
11 #include "core/logging.h"
12 #include "effects.h"
14 #ifdef ALSOFT_EAX
15 #include <cassert>
17 #include "alnumeric.h"
19 #include "al/eax/exception.h"
20 #include "al/eax/utils.h"
21 #endif // ALSOFT_EAX
24 namespace {
26 static_assert(ChorusMaxDelay >= AL_CHORUS_MAX_DELAY, "Chorus max delay too small");
27 static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too small");
29 static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
30 static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
32 inline al::optional<ChorusWaveform> WaveformFromEnum(ALenum type)
34 switch(type)
36 case AL_CHORUS_WAVEFORM_SINUSOID: return al::make_optional(ChorusWaveform::Sinusoid);
37 case AL_CHORUS_WAVEFORM_TRIANGLE: return al::make_optional(ChorusWaveform::Triangle);
39 return al::nullopt;
41 inline ALenum EnumFromWaveform(ChorusWaveform type)
43 switch(type)
45 case ChorusWaveform::Sinusoid: return AL_CHORUS_WAVEFORM_SINUSOID;
46 case ChorusWaveform::Triangle: return AL_CHORUS_WAVEFORM_TRIANGLE;
48 throw std::runtime_error{"Invalid chorus waveform: "+std::to_string(static_cast<int>(type))};
51 void Chorus_setParami(EffectProps *props, ALenum param, int val)
53 switch(param)
55 case AL_CHORUS_WAVEFORM:
56 if(auto formopt = WaveformFromEnum(val))
57 props->Chorus.Waveform = *formopt;
58 else
59 throw effect_exception{AL_INVALID_VALUE, "Invalid chorus waveform: 0x%04x", val};
60 break;
62 case AL_CHORUS_PHASE:
63 if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
64 throw effect_exception{AL_INVALID_VALUE, "Chorus phase out of range: %d", val};
65 props->Chorus.Phase = val;
66 break;
68 default:
69 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
72 void Chorus_setParamiv(EffectProps *props, ALenum param, const int *vals)
73 { Chorus_setParami(props, param, vals[0]); }
74 void Chorus_setParamf(EffectProps *props, ALenum param, float val)
76 switch(param)
78 case AL_CHORUS_RATE:
79 if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
80 throw effect_exception{AL_INVALID_VALUE, "Chorus rate out of range: %f", val};
81 props->Chorus.Rate = val;
82 break;
84 case AL_CHORUS_DEPTH:
85 if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
86 throw effect_exception{AL_INVALID_VALUE, "Chorus depth out of range: %f", val};
87 props->Chorus.Depth = val;
88 break;
90 case AL_CHORUS_FEEDBACK:
91 if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
92 throw effect_exception{AL_INVALID_VALUE, "Chorus feedback out of range: %f", val};
93 props->Chorus.Feedback = val;
94 break;
96 case AL_CHORUS_DELAY:
97 if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
98 throw effect_exception{AL_INVALID_VALUE, "Chorus delay out of range: %f", val};
99 props->Chorus.Delay = val;
100 break;
102 default:
103 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
106 void Chorus_setParamfv(EffectProps *props, ALenum param, const float *vals)
107 { Chorus_setParamf(props, param, vals[0]); }
109 void Chorus_getParami(const EffectProps *props, ALenum param, int *val)
111 switch(param)
113 case AL_CHORUS_WAVEFORM:
114 *val = EnumFromWaveform(props->Chorus.Waveform);
115 break;
117 case AL_CHORUS_PHASE:
118 *val = props->Chorus.Phase;
119 break;
121 default:
122 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus integer property 0x%04x", param};
125 void Chorus_getParamiv(const EffectProps *props, ALenum param, int *vals)
126 { Chorus_getParami(props, param, vals); }
127 void Chorus_getParamf(const EffectProps *props, ALenum param, float *val)
129 switch(param)
131 case AL_CHORUS_RATE:
132 *val = props->Chorus.Rate;
133 break;
135 case AL_CHORUS_DEPTH:
136 *val = props->Chorus.Depth;
137 break;
139 case AL_CHORUS_FEEDBACK:
140 *val = props->Chorus.Feedback;
141 break;
143 case AL_CHORUS_DELAY:
144 *val = props->Chorus.Delay;
145 break;
147 default:
148 throw effect_exception{AL_INVALID_ENUM, "Invalid chorus float property 0x%04x", param};
151 void Chorus_getParamfv(const EffectProps *props, ALenum param, float *vals)
152 { Chorus_getParamf(props, param, vals); }
154 const EffectProps genDefaultChorusProps() noexcept
156 EffectProps props{};
157 props.Chorus.Waveform = *WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM);
158 props.Chorus.Phase = AL_CHORUS_DEFAULT_PHASE;
159 props.Chorus.Rate = AL_CHORUS_DEFAULT_RATE;
160 props.Chorus.Depth = AL_CHORUS_DEFAULT_DEPTH;
161 props.Chorus.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
162 props.Chorus.Delay = AL_CHORUS_DEFAULT_DELAY;
163 return props;
167 void Flanger_setParami(EffectProps *props, ALenum param, int val)
169 switch(param)
171 case AL_FLANGER_WAVEFORM:
172 if(auto formopt = WaveformFromEnum(val))
173 props->Chorus.Waveform = *formopt;
174 else
175 throw effect_exception{AL_INVALID_VALUE, "Invalid flanger waveform: 0x%04x", val};
176 break;
178 case AL_FLANGER_PHASE:
179 if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
180 throw effect_exception{AL_INVALID_VALUE, "Flanger phase out of range: %d", val};
181 props->Chorus.Phase = val;
182 break;
184 default:
185 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
188 void Flanger_setParamiv(EffectProps *props, ALenum param, const int *vals)
189 { Flanger_setParami(props, param, vals[0]); }
190 void Flanger_setParamf(EffectProps *props, ALenum param, float val)
192 switch(param)
194 case AL_FLANGER_RATE:
195 if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
196 throw effect_exception{AL_INVALID_VALUE, "Flanger rate out of range: %f", val};
197 props->Chorus.Rate = val;
198 break;
200 case AL_FLANGER_DEPTH:
201 if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
202 throw effect_exception{AL_INVALID_VALUE, "Flanger depth out of range: %f", val};
203 props->Chorus.Depth = val;
204 break;
206 case AL_FLANGER_FEEDBACK:
207 if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
208 throw effect_exception{AL_INVALID_VALUE, "Flanger feedback out of range: %f", val};
209 props->Chorus.Feedback = val;
210 break;
212 case AL_FLANGER_DELAY:
213 if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
214 throw effect_exception{AL_INVALID_VALUE, "Flanger delay out of range: %f", val};
215 props->Chorus.Delay = val;
216 break;
218 default:
219 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
222 void Flanger_setParamfv(EffectProps *props, ALenum param, const float *vals)
223 { Flanger_setParamf(props, param, vals[0]); }
225 void Flanger_getParami(const EffectProps *props, ALenum param, int *val)
227 switch(param)
229 case AL_FLANGER_WAVEFORM:
230 *val = EnumFromWaveform(props->Chorus.Waveform);
231 break;
233 case AL_FLANGER_PHASE:
234 *val = props->Chorus.Phase;
235 break;
237 default:
238 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger integer property 0x%04x", param};
241 void Flanger_getParamiv(const EffectProps *props, ALenum param, int *vals)
242 { Flanger_getParami(props, param, vals); }
243 void Flanger_getParamf(const EffectProps *props, ALenum param, float *val)
245 switch(param)
247 case AL_FLANGER_RATE:
248 *val = props->Chorus.Rate;
249 break;
251 case AL_FLANGER_DEPTH:
252 *val = props->Chorus.Depth;
253 break;
255 case AL_FLANGER_FEEDBACK:
256 *val = props->Chorus.Feedback;
257 break;
259 case AL_FLANGER_DELAY:
260 *val = props->Chorus.Delay;
261 break;
263 default:
264 throw effect_exception{AL_INVALID_ENUM, "Invalid flanger float property 0x%04x", param};
267 void Flanger_getParamfv(const EffectProps *props, ALenum param, float *vals)
268 { Flanger_getParamf(props, param, vals); }
270 EffectProps genDefaultFlangerProps() noexcept
272 EffectProps props{};
273 props.Chorus.Waveform = *WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM);
274 props.Chorus.Phase = AL_FLANGER_DEFAULT_PHASE;
275 props.Chorus.Rate = AL_FLANGER_DEFAULT_RATE;
276 props.Chorus.Depth = AL_FLANGER_DEFAULT_DEPTH;
277 props.Chorus.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
278 props.Chorus.Delay = AL_FLANGER_DEFAULT_DELAY;
279 return props;
282 } // namespace
284 DEFINE_ALEFFECT_VTABLE(Chorus);
286 const EffectProps ChorusEffectProps{genDefaultChorusProps()};
288 DEFINE_ALEFFECT_VTABLE(Flanger);
290 const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
293 #ifdef ALSOFT_EAX
294 namespace {
296 void eax_set_efx_waveform(
297 ALenum waveform,
298 EffectProps& al_effect_props)
300 const auto efx_waveform = WaveformFromEnum(waveform);
301 assert(efx_waveform.has_value());
302 al_effect_props.Chorus.Waveform = *efx_waveform;
305 void eax_set_efx_phase(
306 ALint phase,
307 EffectProps& al_effect_props)
309 al_effect_props.Chorus.Phase = phase;
312 void eax_set_efx_rate(
313 ALfloat rate,
314 EffectProps& al_effect_props)
316 al_effect_props.Chorus.Rate = rate;
319 void eax_set_efx_depth(
320 ALfloat depth,
321 EffectProps& al_effect_props)
323 al_effect_props.Chorus.Depth = depth;
326 void eax_set_efx_feedback(
327 ALfloat feedback,
328 EffectProps& al_effect_props)
330 al_effect_props.Chorus.Feedback = feedback;
333 void eax_set_efx_delay(
334 ALfloat delay,
335 EffectProps& al_effect_props)
337 al_effect_props.Chorus.Delay = delay;
341 using EaxChorusEffectDirtyFlagsValue = std::uint_least8_t;
343 struct EaxChorusEffectDirtyFlags
345 using EaxIsBitFieldStruct = bool;
347 EaxChorusEffectDirtyFlagsValue ulWaveform : 1;
348 EaxChorusEffectDirtyFlagsValue lPhase : 1;
349 EaxChorusEffectDirtyFlagsValue flRate : 1;
350 EaxChorusEffectDirtyFlagsValue flDepth : 1;
351 EaxChorusEffectDirtyFlagsValue flFeedback : 1;
352 EaxChorusEffectDirtyFlagsValue flDelay : 1;
353 }; // EaxChorusEffectDirtyFlags
356 class EaxChorusEffect final :
357 public EaxEffect
359 public:
360 EaxChorusEffect();
362 void dispatch(const EaxEaxCall& eax_call) override;
364 // [[nodiscard]]
365 bool apply_deferred() override;
367 private:
368 EAXCHORUSPROPERTIES eax_{};
369 EAXCHORUSPROPERTIES eax_d_{};
370 EaxChorusEffectDirtyFlags eax_dirty_flags_{};
372 void set_eax_defaults() noexcept;
374 void set_efx_waveform();
375 void set_efx_phase();
376 void set_efx_rate();
377 void set_efx_depth();
378 void set_efx_feedback();
379 void set_efx_delay();
380 void set_efx_defaults();
382 void get(const EaxEaxCall& eax_call);
384 void validate_waveform(unsigned long ulWaveform);
385 void validate_phase(long lPhase);
386 void validate_rate(float flRate);
387 void validate_depth(float flDepth);
388 void validate_feedback(float flFeedback);
389 void validate_delay(float flDelay);
390 void validate_all(const EAXCHORUSPROPERTIES& eax_all);
392 void defer_waveform(unsigned long ulWaveform);
393 void defer_phase(long lPhase);
394 void defer_rate(float flRate);
395 void defer_depth(float flDepth);
396 void defer_feedback(float flFeedback);
397 void defer_delay(float flDelay);
398 void defer_all(const EAXCHORUSPROPERTIES& eax_all);
400 void defer_waveform(const EaxEaxCall& eax_call);
401 void defer_phase(const EaxEaxCall& eax_call);
402 void defer_rate(const EaxEaxCall& eax_call);
403 void defer_depth(const EaxEaxCall& eax_call);
404 void defer_feedback(const EaxEaxCall& eax_call);
405 void defer_delay(const EaxEaxCall& eax_call);
406 void defer_all(const EaxEaxCall& eax_call);
408 void set(const EaxEaxCall& eax_call);
409 }; // EaxChorusEffect
412 class EaxChorusEffectException :
413 public EaxException
415 public:
416 explicit EaxChorusEffectException(
417 const char* message)
419 EaxException{"EAX_CHORUS_EFFECT", message}
422 }; // EaxChorusEffectException
425 EaxChorusEffect::EaxChorusEffect()
426 : EaxEffect{AL_EFFECT_CHORUS}
428 set_eax_defaults();
429 set_efx_defaults();
432 void EaxChorusEffect::dispatch(const EaxEaxCall& eax_call)
434 eax_call.is_get() ? get(eax_call) : set(eax_call);
437 void EaxChorusEffect::set_eax_defaults() noexcept
439 eax_.ulWaveform = EAXCHORUS_DEFAULTWAVEFORM;
440 eax_.lPhase = EAXCHORUS_DEFAULTPHASE;
441 eax_.flRate = EAXCHORUS_DEFAULTRATE;
442 eax_.flDepth = EAXCHORUS_DEFAULTDEPTH;
443 eax_.flFeedback = EAXCHORUS_DEFAULTFEEDBACK;
444 eax_.flDelay = EAXCHORUS_DEFAULTDELAY;
446 eax_d_ = eax_;
449 void EaxChorusEffect::set_efx_waveform()
451 const auto waveform = clamp(
452 static_cast<ALint>(eax_.ulWaveform),
453 AL_CHORUS_MIN_WAVEFORM,
454 AL_CHORUS_MAX_WAVEFORM);
456 eax_set_efx_waveform(waveform, al_effect_props_);
459 void EaxChorusEffect::set_efx_phase()
461 const auto phase = clamp(
462 static_cast<ALint>(eax_.lPhase),
463 AL_CHORUS_MIN_PHASE,
464 AL_CHORUS_MAX_PHASE);
466 eax_set_efx_phase(phase, al_effect_props_);
469 void EaxChorusEffect::set_efx_rate()
471 const auto rate = clamp(
472 eax_.flRate,
473 AL_CHORUS_MIN_RATE,
474 AL_CHORUS_MAX_RATE);
476 eax_set_efx_rate(rate, al_effect_props_);
479 void EaxChorusEffect::set_efx_depth()
481 const auto depth = clamp(
482 eax_.flDepth,
483 AL_CHORUS_MIN_DEPTH,
484 AL_CHORUS_MAX_DEPTH);
486 eax_set_efx_depth(depth, al_effect_props_);
489 void EaxChorusEffect::set_efx_feedback()
491 const auto feedback = clamp(
492 eax_.flFeedback,
493 AL_CHORUS_MIN_FEEDBACK,
494 AL_CHORUS_MAX_FEEDBACK);
496 eax_set_efx_feedback(feedback, al_effect_props_);
499 void EaxChorusEffect::set_efx_delay()
501 const auto delay = clamp(
502 eax_.flDelay,
503 AL_CHORUS_MIN_DELAY,
504 AL_CHORUS_MAX_DELAY);
506 eax_set_efx_delay(delay, al_effect_props_);
509 void EaxChorusEffect::set_efx_defaults()
511 set_efx_waveform();
512 set_efx_phase();
513 set_efx_rate();
514 set_efx_depth();
515 set_efx_feedback();
516 set_efx_delay();
519 void EaxChorusEffect::get(const EaxEaxCall& eax_call)
521 switch(eax_call.get_property_id())
523 case EAXCHORUS_NONE:
524 break;
526 case EAXCHORUS_ALLPARAMETERS:
527 eax_call.set_value<EaxChorusEffectException>(eax_);
528 break;
530 case EAXCHORUS_WAVEFORM:
531 eax_call.set_value<EaxChorusEffectException>(eax_.ulWaveform);
532 break;
534 case EAXCHORUS_PHASE:
535 eax_call.set_value<EaxChorusEffectException>(eax_.lPhase);
536 break;
538 case EAXCHORUS_RATE:
539 eax_call.set_value<EaxChorusEffectException>(eax_.flRate);
540 break;
542 case EAXCHORUS_DEPTH:
543 eax_call.set_value<EaxChorusEffectException>(eax_.flDepth);
544 break;
546 case EAXCHORUS_FEEDBACK:
547 eax_call.set_value<EaxChorusEffectException>(eax_.flFeedback);
548 break;
550 case EAXCHORUS_DELAY:
551 eax_call.set_value<EaxChorusEffectException>(eax_.flDelay);
552 break;
554 default:
555 throw EaxChorusEffectException{"Unsupported property id."};
559 void EaxChorusEffect::validate_waveform(
560 unsigned long ulWaveform)
562 eax_validate_range<EaxChorusEffectException>(
563 "Waveform",
564 ulWaveform,
565 EAXCHORUS_MINWAVEFORM,
566 EAXCHORUS_MAXWAVEFORM);
569 void EaxChorusEffect::validate_phase(
570 long lPhase)
572 eax_validate_range<EaxChorusEffectException>(
573 "Phase",
574 lPhase,
575 EAXCHORUS_MINPHASE,
576 EAXCHORUS_MAXPHASE);
579 void EaxChorusEffect::validate_rate(
580 float flRate)
582 eax_validate_range<EaxChorusEffectException>(
583 "Rate",
584 flRate,
585 EAXCHORUS_MINRATE,
586 EAXCHORUS_MAXRATE);
589 void EaxChorusEffect::validate_depth(
590 float flDepth)
592 eax_validate_range<EaxChorusEffectException>(
593 "Depth",
594 flDepth,
595 EAXCHORUS_MINDEPTH,
596 EAXCHORUS_MAXDEPTH);
599 void EaxChorusEffect::validate_feedback(
600 float flFeedback)
602 eax_validate_range<EaxChorusEffectException>(
603 "Feedback",
604 flFeedback,
605 EAXCHORUS_MINFEEDBACK,
606 EAXCHORUS_MAXFEEDBACK);
609 void EaxChorusEffect::validate_delay(
610 float flDelay)
612 eax_validate_range<EaxChorusEffectException>(
613 "Delay",
614 flDelay,
615 EAXCHORUS_MINDELAY,
616 EAXCHORUS_MAXDELAY);
619 void EaxChorusEffect::validate_all(
620 const EAXCHORUSPROPERTIES& eax_all)
622 validate_waveform(eax_all.ulWaveform);
623 validate_phase(eax_all.lPhase);
624 validate_rate(eax_all.flRate);
625 validate_depth(eax_all.flDepth);
626 validate_feedback(eax_all.flFeedback);
627 validate_delay(eax_all.flDelay);
630 void EaxChorusEffect::defer_waveform(
631 unsigned long ulWaveform)
633 eax_d_.ulWaveform = ulWaveform;
634 eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
637 void EaxChorusEffect::defer_phase(
638 long lPhase)
640 eax_d_.lPhase = lPhase;
641 eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase);
644 void EaxChorusEffect::defer_rate(
645 float flRate)
647 eax_d_.flRate = flRate;
648 eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate);
651 void EaxChorusEffect::defer_depth(
652 float flDepth)
654 eax_d_.flDepth = flDepth;
655 eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth);
658 void EaxChorusEffect::defer_feedback(
659 float flFeedback)
661 eax_d_.flFeedback = flFeedback;
662 eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
665 void EaxChorusEffect::defer_delay(
666 float flDelay)
668 eax_d_.flDelay = flDelay;
669 eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
672 void EaxChorusEffect::defer_all(
673 const EAXCHORUSPROPERTIES& eax_all)
675 defer_waveform(eax_all.ulWaveform);
676 defer_phase(eax_all.lPhase);
677 defer_rate(eax_all.flRate);
678 defer_depth(eax_all.flDepth);
679 defer_feedback(eax_all.flFeedback);
680 defer_delay(eax_all.flDelay);
683 void EaxChorusEffect::defer_waveform(
684 const EaxEaxCall& eax_call)
686 const auto& waveform =
687 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::ulWaveform)>();
689 validate_waveform(waveform);
690 defer_waveform(waveform);
693 void EaxChorusEffect::defer_phase(
694 const EaxEaxCall& eax_call)
696 const auto& phase =
697 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::lPhase)>();
699 validate_phase(phase);
700 defer_phase(phase);
703 void EaxChorusEffect::defer_rate(
704 const EaxEaxCall& eax_call)
706 const auto& rate =
707 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flRate)>();
709 validate_rate(rate);
710 defer_rate(rate);
713 void EaxChorusEffect::defer_depth(
714 const EaxEaxCall& eax_call)
716 const auto& depth =
717 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flDepth)>();
719 validate_depth(depth);
720 defer_depth(depth);
723 void EaxChorusEffect::defer_feedback(
724 const EaxEaxCall& eax_call)
726 const auto& feedback =
727 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flFeedback)>();
729 validate_feedback(feedback);
730 defer_feedback(feedback);
733 void EaxChorusEffect::defer_delay(
734 const EaxEaxCall& eax_call)
736 const auto& delay =
737 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flDelay)>();
739 validate_delay(delay);
740 defer_delay(delay);
743 void EaxChorusEffect::defer_all(
744 const EaxEaxCall& eax_call)
746 const auto& all =
747 eax_call.get_value<EaxChorusEffectException, const EAXCHORUSPROPERTIES>();
749 validate_all(all);
750 defer_all(all);
753 // [[nodiscard]]
754 bool EaxChorusEffect::apply_deferred()
756 if (eax_dirty_flags_ == EaxChorusEffectDirtyFlags{})
758 return false;
761 eax_ = eax_d_;
763 if (eax_dirty_flags_.ulWaveform)
765 set_efx_waveform();
768 if (eax_dirty_flags_.lPhase)
770 set_efx_phase();
773 if (eax_dirty_flags_.flRate)
775 set_efx_rate();
778 if (eax_dirty_flags_.flDepth)
780 set_efx_depth();
783 if (eax_dirty_flags_.flFeedback)
785 set_efx_feedback();
788 if (eax_dirty_flags_.flDelay)
790 set_efx_delay();
793 eax_dirty_flags_ = EaxChorusEffectDirtyFlags{};
795 return true;
798 void EaxChorusEffect::set(const EaxEaxCall& eax_call)
800 switch(eax_call.get_property_id())
802 case EAXCHORUS_NONE:
803 break;
805 case EAXCHORUS_ALLPARAMETERS:
806 defer_all(eax_call);
807 break;
809 case EAXCHORUS_WAVEFORM:
810 defer_waveform(eax_call);
811 break;
813 case EAXCHORUS_PHASE:
814 defer_phase(eax_call);
815 break;
817 case EAXCHORUS_RATE:
818 defer_rate(eax_call);
819 break;
821 case EAXCHORUS_DEPTH:
822 defer_depth(eax_call);
823 break;
825 case EAXCHORUS_FEEDBACK:
826 defer_feedback(eax_call);
827 break;
829 case EAXCHORUS_DELAY:
830 defer_delay(eax_call);
831 break;
833 default:
834 throw EaxChorusEffectException{"Unsupported property id."};
839 } // namespace
842 EaxEffectUPtr eax_create_eax_chorus_effect()
844 return std::make_unique<::EaxChorusEffect>();
848 namespace
852 using EaxFlangerEffectDirtyFlagsValue = std::uint_least8_t;
854 struct EaxFlangerEffectDirtyFlags
856 using EaxIsBitFieldStruct = bool;
858 EaxFlangerEffectDirtyFlagsValue ulWaveform : 1;
859 EaxFlangerEffectDirtyFlagsValue lPhase : 1;
860 EaxFlangerEffectDirtyFlagsValue flRate : 1;
861 EaxFlangerEffectDirtyFlagsValue flDepth : 1;
862 EaxFlangerEffectDirtyFlagsValue flFeedback : 1;
863 EaxFlangerEffectDirtyFlagsValue flDelay : 1;
864 }; // EaxFlangerEffectDirtyFlags
867 class EaxFlangerEffect final :
868 public EaxEffect
870 public:
871 EaxFlangerEffect();
874 void dispatch(const EaxEaxCall& eax_call) override;
876 // [[nodiscard]]
877 bool apply_deferred() override;
879 private:
880 EAXFLANGERPROPERTIES eax_{};
881 EAXFLANGERPROPERTIES eax_d_{};
882 EaxFlangerEffectDirtyFlags eax_dirty_flags_{};
884 void set_eax_defaults();
886 void set_efx_waveform();
887 void set_efx_phase();
888 void set_efx_rate();
889 void set_efx_depth();
890 void set_efx_feedback();
891 void set_efx_delay();
892 void set_efx_defaults();
894 void get(const EaxEaxCall& eax_call);
896 void validate_waveform(unsigned long ulWaveform);
897 void validate_phase(long lPhase);
898 void validate_rate(float flRate);
899 void validate_depth(float flDepth);
900 void validate_feedback(float flFeedback);
901 void validate_delay(float flDelay);
902 void validate_all(const EAXFLANGERPROPERTIES& all);
904 void defer_waveform(unsigned long ulWaveform);
905 void defer_phase(long lPhase);
906 void defer_rate(float flRate);
907 void defer_depth(float flDepth);
908 void defer_feedback(float flFeedback);
909 void defer_delay(float flDelay);
910 void defer_all(const EAXFLANGERPROPERTIES& all);
912 void defer_waveform(const EaxEaxCall& eax_call);
913 void defer_phase(const EaxEaxCall& eax_call);
914 void defer_rate(const EaxEaxCall& eax_call);
915 void defer_depth(const EaxEaxCall& eax_call);
916 void defer_feedback(const EaxEaxCall& eax_call);
917 void defer_delay(const EaxEaxCall& eax_call);
918 void defer_all(const EaxEaxCall& eax_call);
920 void set(const EaxEaxCall& eax_call);
921 }; // EaxFlangerEffect
924 class EaxFlangerEffectException :
925 public EaxException
927 public:
928 explicit EaxFlangerEffectException(
929 const char* message)
931 EaxException{"EAX_FLANGER_EFFECT", message}
934 }; // EaxFlangerEffectException
937 EaxFlangerEffect::EaxFlangerEffect()
938 : EaxEffect{AL_EFFECT_FLANGER}
940 set_eax_defaults();
941 set_efx_defaults();
944 void EaxFlangerEffect::dispatch(const EaxEaxCall& eax_call)
946 eax_call.is_get() ? get(eax_call) : set(eax_call);
949 void EaxFlangerEffect::set_eax_defaults()
951 eax_.ulWaveform = EAXFLANGER_DEFAULTWAVEFORM;
952 eax_.lPhase = EAXFLANGER_DEFAULTPHASE;
953 eax_.flRate = EAXFLANGER_DEFAULTRATE;
954 eax_.flDepth = EAXFLANGER_DEFAULTDEPTH;
955 eax_.flFeedback = EAXFLANGER_DEFAULTFEEDBACK;
956 eax_.flDelay = EAXFLANGER_DEFAULTDELAY;
958 eax_d_ = eax_;
961 void EaxFlangerEffect::set_efx_waveform()
963 const auto waveform = clamp(
964 static_cast<ALint>(eax_.ulWaveform),
965 AL_FLANGER_MIN_WAVEFORM,
966 AL_FLANGER_MAX_WAVEFORM);
968 eax_set_efx_waveform(waveform, al_effect_props_);
971 void EaxFlangerEffect::set_efx_phase()
973 const auto phase = clamp(
974 static_cast<ALint>(eax_.lPhase),
975 AL_FLANGER_MIN_PHASE,
976 AL_FLANGER_MAX_PHASE);
978 eax_set_efx_phase(phase, al_effect_props_);
981 void EaxFlangerEffect::set_efx_rate()
983 const auto rate = clamp(
984 eax_.flRate,
985 AL_FLANGER_MIN_RATE,
986 AL_FLANGER_MAX_RATE);
988 eax_set_efx_rate(rate, al_effect_props_);
991 void EaxFlangerEffect::set_efx_depth()
993 const auto depth = clamp(
994 eax_.flDepth,
995 AL_FLANGER_MIN_DEPTH,
996 AL_FLANGER_MAX_DEPTH);
998 eax_set_efx_depth(depth, al_effect_props_);
1001 void EaxFlangerEffect::set_efx_feedback()
1003 const auto feedback = clamp(
1004 eax_.flFeedback,
1005 AL_FLANGER_MIN_FEEDBACK,
1006 AL_FLANGER_MAX_FEEDBACK);
1008 eax_set_efx_feedback(feedback, al_effect_props_);
1011 void EaxFlangerEffect::set_efx_delay()
1013 const auto delay = clamp(
1014 eax_.flDelay,
1015 AL_FLANGER_MIN_DELAY,
1016 AL_FLANGER_MAX_DELAY);
1018 eax_set_efx_delay(delay, al_effect_props_);
1021 void EaxFlangerEffect::set_efx_defaults()
1023 set_efx_waveform();
1024 set_efx_phase();
1025 set_efx_rate();
1026 set_efx_depth();
1027 set_efx_feedback();
1028 set_efx_delay();
1031 void EaxFlangerEffect::get(const EaxEaxCall& eax_call)
1033 switch(eax_call.get_property_id())
1035 case EAXFLANGER_NONE:
1036 break;
1038 case EAXFLANGER_ALLPARAMETERS:
1039 eax_call.set_value<EaxFlangerEffectException>(eax_);
1040 break;
1042 case EAXFLANGER_WAVEFORM:
1043 eax_call.set_value<EaxFlangerEffectException>(eax_.ulWaveform);
1044 break;
1046 case EAXFLANGER_PHASE:
1047 eax_call.set_value<EaxFlangerEffectException>(eax_.lPhase);
1048 break;
1050 case EAXFLANGER_RATE:
1051 eax_call.set_value<EaxFlangerEffectException>(eax_.flRate);
1052 break;
1054 case EAXFLANGER_DEPTH:
1055 eax_call.set_value<EaxFlangerEffectException>(eax_.flDepth);
1056 break;
1058 case EAXFLANGER_FEEDBACK:
1059 eax_call.set_value<EaxFlangerEffectException>(eax_.flFeedback);
1060 break;
1062 case EAXFLANGER_DELAY:
1063 eax_call.set_value<EaxFlangerEffectException>(eax_.flDelay);
1064 break;
1066 default:
1067 throw EaxFlangerEffectException{"Unsupported property id."};
1071 void EaxFlangerEffect::validate_waveform(
1072 unsigned long ulWaveform)
1074 eax_validate_range<EaxFlangerEffectException>(
1075 "Waveform",
1076 ulWaveform,
1077 EAXFLANGER_MINWAVEFORM,
1078 EAXFLANGER_MAXWAVEFORM);
1081 void EaxFlangerEffect::validate_phase(
1082 long lPhase)
1084 eax_validate_range<EaxFlangerEffectException>(
1085 "Phase",
1086 lPhase,
1087 EAXFLANGER_MINPHASE,
1088 EAXFLANGER_MAXPHASE);
1091 void EaxFlangerEffect::validate_rate(
1092 float flRate)
1094 eax_validate_range<EaxFlangerEffectException>(
1095 "Rate",
1096 flRate,
1097 EAXFLANGER_MINRATE,
1098 EAXFLANGER_MAXRATE);
1101 void EaxFlangerEffect::validate_depth(
1102 float flDepth)
1104 eax_validate_range<EaxFlangerEffectException>(
1105 "Depth",
1106 flDepth,
1107 EAXFLANGER_MINDEPTH,
1108 EAXFLANGER_MAXDEPTH);
1111 void EaxFlangerEffect::validate_feedback(
1112 float flFeedback)
1114 eax_validate_range<EaxFlangerEffectException>(
1115 "Feedback",
1116 flFeedback,
1117 EAXFLANGER_MINFEEDBACK,
1118 EAXFLANGER_MAXFEEDBACK);
1121 void EaxFlangerEffect::validate_delay(
1122 float flDelay)
1124 eax_validate_range<EaxFlangerEffectException>(
1125 "Delay",
1126 flDelay,
1127 EAXFLANGER_MINDELAY,
1128 EAXFLANGER_MAXDELAY);
1131 void EaxFlangerEffect::validate_all(
1132 const EAXFLANGERPROPERTIES& all)
1134 validate_waveform(all.ulWaveform);
1135 validate_phase(all.lPhase);
1136 validate_rate(all.flRate);
1137 validate_depth(all.flDepth);
1138 validate_feedback(all.flDelay);
1139 validate_delay(all.flDelay);
1142 void EaxFlangerEffect::defer_waveform(
1143 unsigned long ulWaveform)
1145 eax_d_.ulWaveform = ulWaveform;
1146 eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
1149 void EaxFlangerEffect::defer_phase(
1150 long lPhase)
1152 eax_d_.lPhase = lPhase;
1153 eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase);
1156 void EaxFlangerEffect::defer_rate(
1157 float flRate)
1159 eax_d_.flRate = flRate;
1160 eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate);
1163 void EaxFlangerEffect::defer_depth(
1164 float flDepth)
1166 eax_d_.flDepth = flDepth;
1167 eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth);
1170 void EaxFlangerEffect::defer_feedback(
1171 float flFeedback)
1173 eax_d_.flFeedback = flFeedback;
1174 eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
1177 void EaxFlangerEffect::defer_delay(
1178 float flDelay)
1180 eax_d_.flDelay = flDelay;
1181 eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
1184 void EaxFlangerEffect::defer_all(
1185 const EAXFLANGERPROPERTIES& all)
1187 defer_waveform(all.ulWaveform);
1188 defer_phase(all.lPhase);
1189 defer_rate(all.flRate);
1190 defer_depth(all.flDepth);
1191 defer_feedback(all.flDelay);
1192 defer_delay(all.flDelay);
1195 void EaxFlangerEffect::defer_waveform(
1196 const EaxEaxCall& eax_call)
1198 const auto& waveform =
1199 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::ulWaveform)>();
1201 validate_waveform(waveform);
1202 defer_waveform(waveform);
1205 void EaxFlangerEffect::defer_phase(
1206 const EaxEaxCall& eax_call)
1208 const auto& phase =
1209 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::lPhase)>();
1211 validate_phase(phase);
1212 defer_phase(phase);
1215 void EaxFlangerEffect::defer_rate(
1216 const EaxEaxCall& eax_call)
1218 const auto& rate =
1219 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flRate)>();
1221 validate_rate(rate);
1222 defer_rate(rate);
1225 void EaxFlangerEffect::defer_depth(
1226 const EaxEaxCall& eax_call)
1228 const auto& depth =
1229 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flDepth)>();
1231 validate_depth(depth);
1232 defer_depth(depth);
1235 void EaxFlangerEffect::defer_feedback(
1236 const EaxEaxCall& eax_call)
1238 const auto& feedback =
1239 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flFeedback)>();
1241 validate_feedback(feedback);
1242 defer_feedback(feedback);
1245 void EaxFlangerEffect::defer_delay(
1246 const EaxEaxCall& eax_call)
1248 const auto& delay =
1249 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flDelay)>();
1251 validate_delay(delay);
1252 defer_delay(delay);
1255 void EaxFlangerEffect::defer_all(
1256 const EaxEaxCall& eax_call)
1258 const auto& all =
1259 eax_call.get_value<EaxFlangerEffectException, const EAXFLANGERPROPERTIES>();
1261 validate_all(all);
1262 defer_all(all);
1265 // [[nodiscard]]
1266 bool EaxFlangerEffect::apply_deferred()
1268 if (eax_dirty_flags_ == EaxFlangerEffectDirtyFlags{})
1270 return false;
1273 eax_ = eax_d_;
1275 if (eax_dirty_flags_.ulWaveform)
1277 set_efx_waveform();
1280 if (eax_dirty_flags_.lPhase)
1282 set_efx_phase();
1285 if (eax_dirty_flags_.flRate)
1287 set_efx_rate();
1290 if (eax_dirty_flags_.flDepth)
1292 set_efx_depth();
1295 if (eax_dirty_flags_.flFeedback)
1297 set_efx_feedback();
1300 if (eax_dirty_flags_.flDelay)
1302 set_efx_delay();
1305 eax_dirty_flags_ = EaxFlangerEffectDirtyFlags{};
1307 return true;
1310 void EaxFlangerEffect::set(const EaxEaxCall& eax_call)
1312 switch(eax_call.get_property_id())
1314 case EAXFLANGER_NONE:
1315 break;
1317 case EAXFLANGER_ALLPARAMETERS:
1318 defer_all(eax_call);
1319 break;
1321 case EAXFLANGER_WAVEFORM:
1322 defer_waveform(eax_call);
1323 break;
1325 case EAXFLANGER_PHASE:
1326 defer_phase(eax_call);
1327 break;
1329 case EAXFLANGER_RATE:
1330 defer_rate(eax_call);
1331 break;
1333 case EAXFLANGER_DEPTH:
1334 defer_depth(eax_call);
1335 break;
1337 case EAXFLANGER_FEEDBACK:
1338 defer_feedback(eax_call);
1339 break;
1341 case EAXFLANGER_DELAY:
1342 defer_delay(eax_call);
1343 break;
1345 default:
1346 throw EaxFlangerEffectException{"Unsupported property id."};
1350 } // namespace
1352 EaxEffectUPtr eax_create_eax_flanger_effect()
1354 return std::make_unique<EaxFlangerEffect>();
1357 #endif // ALSOFT_EAX