Make some local constexpr variables static
[openal-soft.git] / al / effects / chorus.cpp
blob15b9d63524292108c6bce43c7c24dcbfacdeff86
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();
363 // [[nodiscard]]
364 bool dispatch(
365 const EaxEaxCall& eax_call) override;
368 private:
369 EAXCHORUSPROPERTIES eax_{};
370 EAXCHORUSPROPERTIES eax_d_{};
371 EaxChorusEffectDirtyFlags eax_dirty_flags_{};
374 void set_eax_defaults() noexcept;
377 void set_efx_waveform();
379 void set_efx_phase();
381 void set_efx_rate();
383 void set_efx_depth();
385 void set_efx_feedback();
387 void set_efx_delay();
389 void set_efx_defaults();
392 // [[nodiscard]]
393 bool get(
394 const EaxEaxCall& eax_call);
397 void validate_waveform(
398 unsigned long ulWaveform);
400 void validate_phase(
401 long lPhase);
403 void validate_rate(
404 float flRate);
406 void validate_depth(
407 float flDepth);
409 void validate_feedback(
410 float flFeedback);
412 void validate_delay(
413 float flDelay);
415 void validate_all(
416 const EAXCHORUSPROPERTIES& eax_all);
419 void defer_waveform(
420 unsigned long ulWaveform);
422 void defer_phase(
423 long lPhase);
425 void defer_rate(
426 float flRate);
428 void defer_depth(
429 float flDepth);
431 void defer_feedback(
432 float flFeedback);
434 void defer_delay(
435 float flDelay);
437 void defer_all(
438 const EAXCHORUSPROPERTIES& eax_all);
441 void defer_waveform(
442 const EaxEaxCall& eax_call);
444 void defer_phase(
445 const EaxEaxCall& eax_call);
447 void defer_rate(
448 const EaxEaxCall& eax_call);
450 void defer_depth(
451 const EaxEaxCall& eax_call);
453 void defer_feedback(
454 const EaxEaxCall& eax_call);
456 void defer_delay(
457 const EaxEaxCall& eax_call);
459 void defer_all(
460 const EaxEaxCall& eax_call);
463 // [[nodiscard]]
464 bool apply_deferred();
466 // [[nodiscard]]
467 bool set(
468 const EaxEaxCall& eax_call);
469 }; // EaxChorusEffect
472 class EaxChorusEffectException :
473 public EaxException
475 public:
476 explicit EaxChorusEffectException(
477 const char* message)
479 EaxException{"EAX_CHORUS_EFFECT", message}
482 }; // EaxChorusEffectException
485 EaxChorusEffect::EaxChorusEffect()
486 : EaxEffect{AL_EFFECT_CHORUS}
488 set_eax_defaults();
489 set_efx_defaults();
492 // [[nodiscard]]
493 bool EaxChorusEffect::dispatch(
494 const EaxEaxCall& eax_call)
496 return eax_call.is_get() ? get(eax_call) : set(eax_call);
499 void EaxChorusEffect::set_eax_defaults() noexcept
501 eax_.ulWaveform = EAXCHORUS_DEFAULTWAVEFORM;
502 eax_.lPhase = EAXCHORUS_DEFAULTPHASE;
503 eax_.flRate = EAXCHORUS_DEFAULTRATE;
504 eax_.flDepth = EAXCHORUS_DEFAULTDEPTH;
505 eax_.flFeedback = EAXCHORUS_DEFAULTFEEDBACK;
506 eax_.flDelay = EAXCHORUS_DEFAULTDELAY;
508 eax_d_ = eax_;
511 void EaxChorusEffect::set_efx_waveform()
513 const auto waveform = clamp(
514 static_cast<ALint>(eax_.ulWaveform),
515 AL_CHORUS_MIN_WAVEFORM,
516 AL_CHORUS_MAX_WAVEFORM);
518 eax_set_efx_waveform(waveform, al_effect_props_);
521 void EaxChorusEffect::set_efx_phase()
523 const auto phase = clamp(
524 static_cast<ALint>(eax_.lPhase),
525 AL_CHORUS_MIN_PHASE,
526 AL_CHORUS_MAX_PHASE);
528 eax_set_efx_phase(phase, al_effect_props_);
531 void EaxChorusEffect::set_efx_rate()
533 const auto rate = clamp(
534 eax_.flRate,
535 AL_CHORUS_MIN_RATE,
536 AL_CHORUS_MAX_RATE);
538 eax_set_efx_rate(rate, al_effect_props_);
541 void EaxChorusEffect::set_efx_depth()
543 const auto depth = clamp(
544 eax_.flDepth,
545 AL_CHORUS_MIN_DEPTH,
546 AL_CHORUS_MAX_DEPTH);
548 eax_set_efx_depth(depth, al_effect_props_);
551 void EaxChorusEffect::set_efx_feedback()
553 const auto feedback = clamp(
554 eax_.flFeedback,
555 AL_CHORUS_MIN_FEEDBACK,
556 AL_CHORUS_MAX_FEEDBACK);
558 eax_set_efx_feedback(feedback, al_effect_props_);
561 void EaxChorusEffect::set_efx_delay()
563 const auto delay = clamp(
564 eax_.flDelay,
565 AL_CHORUS_MIN_DELAY,
566 AL_CHORUS_MAX_DELAY);
568 eax_set_efx_delay(delay, al_effect_props_);
571 void EaxChorusEffect::set_efx_defaults()
573 set_efx_waveform();
574 set_efx_phase();
575 set_efx_rate();
576 set_efx_depth();
577 set_efx_feedback();
578 set_efx_delay();
581 bool EaxChorusEffect::get(
582 const EaxEaxCall& eax_call)
584 switch (eax_call.get_property_id())
586 case EAXCHORUS_NONE:
587 break;
589 case EAXCHORUS_ALLPARAMETERS:
590 eax_call.set_value<EaxChorusEffectException>(eax_);
591 break;
593 case EAXCHORUS_WAVEFORM:
594 eax_call.set_value<EaxChorusEffectException>(eax_.ulWaveform);
595 break;
597 case EAXCHORUS_PHASE:
598 eax_call.set_value<EaxChorusEffectException>(eax_.lPhase);
599 break;
601 case EAXCHORUS_RATE:
602 eax_call.set_value<EaxChorusEffectException>(eax_.flRate);
603 break;
605 case EAXCHORUS_DEPTH:
606 eax_call.set_value<EaxChorusEffectException>(eax_.flDepth);
607 break;
609 case EAXCHORUS_FEEDBACK:
610 eax_call.set_value<EaxChorusEffectException>(eax_.flFeedback);
611 break;
613 case EAXCHORUS_DELAY:
614 eax_call.set_value<EaxChorusEffectException>(eax_.flDelay);
615 break;
617 default:
618 throw EaxChorusEffectException{"Unsupported property id."};
621 return false;
624 void EaxChorusEffect::validate_waveform(
625 unsigned long ulWaveform)
627 eax_validate_range<EaxChorusEffectException>(
628 "Waveform",
629 ulWaveform,
630 EAXCHORUS_MINWAVEFORM,
631 EAXCHORUS_MAXWAVEFORM);
634 void EaxChorusEffect::validate_phase(
635 long lPhase)
637 eax_validate_range<EaxChorusEffectException>(
638 "Phase",
639 lPhase,
640 EAXCHORUS_MINPHASE,
641 EAXCHORUS_MAXPHASE);
644 void EaxChorusEffect::validate_rate(
645 float flRate)
647 eax_validate_range<EaxChorusEffectException>(
648 "Rate",
649 flRate,
650 EAXCHORUS_MINRATE,
651 EAXCHORUS_MAXRATE);
654 void EaxChorusEffect::validate_depth(
655 float flDepth)
657 eax_validate_range<EaxChorusEffectException>(
658 "Depth",
659 flDepth,
660 EAXCHORUS_MINDEPTH,
661 EAXCHORUS_MAXDEPTH);
664 void EaxChorusEffect::validate_feedback(
665 float flFeedback)
667 eax_validate_range<EaxChorusEffectException>(
668 "Feedback",
669 flFeedback,
670 EAXCHORUS_MINFEEDBACK,
671 EAXCHORUS_MAXFEEDBACK);
674 void EaxChorusEffect::validate_delay(
675 float flDelay)
677 eax_validate_range<EaxChorusEffectException>(
678 "Delay",
679 flDelay,
680 EAXCHORUS_MINDELAY,
681 EAXCHORUS_MAXDELAY);
684 void EaxChorusEffect::validate_all(
685 const EAXCHORUSPROPERTIES& eax_all)
687 validate_waveform(eax_all.ulWaveform);
688 validate_phase(eax_all.lPhase);
689 validate_rate(eax_all.flRate);
690 validate_depth(eax_all.flDepth);
691 validate_feedback(eax_all.flFeedback);
692 validate_delay(eax_all.flDelay);
695 void EaxChorusEffect::defer_waveform(
696 unsigned long ulWaveform)
698 eax_d_.ulWaveform = ulWaveform;
699 eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
702 void EaxChorusEffect::defer_phase(
703 long lPhase)
705 eax_d_.lPhase = lPhase;
706 eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase);
709 void EaxChorusEffect::defer_rate(
710 float flRate)
712 eax_d_.flRate = flRate;
713 eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate);
716 void EaxChorusEffect::defer_depth(
717 float flDepth)
719 eax_d_.flDepth = flDepth;
720 eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth);
723 void EaxChorusEffect::defer_feedback(
724 float flFeedback)
726 eax_d_.flFeedback = flFeedback;
727 eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
730 void EaxChorusEffect::defer_delay(
731 float flDelay)
733 eax_d_.flDelay = flDelay;
734 eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
737 void EaxChorusEffect::defer_all(
738 const EAXCHORUSPROPERTIES& eax_all)
740 defer_waveform(eax_all.ulWaveform);
741 defer_phase(eax_all.lPhase);
742 defer_rate(eax_all.flRate);
743 defer_depth(eax_all.flDepth);
744 defer_feedback(eax_all.flFeedback);
745 defer_delay(eax_all.flDelay);
748 void EaxChorusEffect::defer_waveform(
749 const EaxEaxCall& eax_call)
751 const auto& waveform =
752 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::ulWaveform)>();
754 validate_waveform(waveform);
755 defer_waveform(waveform);
758 void EaxChorusEffect::defer_phase(
759 const EaxEaxCall& eax_call)
761 const auto& phase =
762 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::lPhase)>();
764 validate_phase(phase);
765 defer_phase(phase);
768 void EaxChorusEffect::defer_rate(
769 const EaxEaxCall& eax_call)
771 const auto& rate =
772 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flRate)>();
774 validate_rate(rate);
775 defer_rate(rate);
778 void EaxChorusEffect::defer_depth(
779 const EaxEaxCall& eax_call)
781 const auto& depth =
782 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flDepth)>();
784 validate_depth(depth);
785 defer_depth(depth);
788 void EaxChorusEffect::defer_feedback(
789 const EaxEaxCall& eax_call)
791 const auto& feedback =
792 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flFeedback)>();
794 validate_feedback(feedback);
795 defer_feedback(feedback);
798 void EaxChorusEffect::defer_delay(
799 const EaxEaxCall& eax_call)
801 const auto& delay =
802 eax_call.get_value<EaxChorusEffectException, const decltype(EAXCHORUSPROPERTIES::flDelay)>();
804 validate_delay(delay);
805 defer_delay(delay);
808 void EaxChorusEffect::defer_all(
809 const EaxEaxCall& eax_call)
811 const auto& all =
812 eax_call.get_value<EaxChorusEffectException, const EAXCHORUSPROPERTIES>();
814 validate_all(all);
815 defer_all(all);
818 // [[nodiscard]]
819 bool EaxChorusEffect::apply_deferred()
821 if (eax_dirty_flags_ == EaxChorusEffectDirtyFlags{})
823 return false;
826 eax_ = eax_d_;
828 if (eax_dirty_flags_.ulWaveform)
830 set_efx_waveform();
833 if (eax_dirty_flags_.lPhase)
835 set_efx_phase();
838 if (eax_dirty_flags_.flRate)
840 set_efx_rate();
843 if (eax_dirty_flags_.flDepth)
845 set_efx_depth();
848 if (eax_dirty_flags_.flFeedback)
850 set_efx_feedback();
853 if (eax_dirty_flags_.flDelay)
855 set_efx_delay();
858 eax_dirty_flags_ = EaxChorusEffectDirtyFlags{};
860 return true;
863 // [[nodiscard]]
864 bool EaxChorusEffect::set(
865 const EaxEaxCall& eax_call)
867 switch (eax_call.get_property_id())
869 case EAXCHORUS_NONE:
870 break;
872 case EAXCHORUS_ALLPARAMETERS:
873 defer_all(eax_call);
874 break;
876 case EAXCHORUS_WAVEFORM:
877 defer_waveform(eax_call);
878 break;
880 case EAXCHORUS_PHASE:
881 defer_phase(eax_call);
882 break;
884 case EAXCHORUS_RATE:
885 defer_rate(eax_call);
886 break;
888 case EAXCHORUS_DEPTH:
889 defer_depth(eax_call);
890 break;
892 case EAXCHORUS_FEEDBACK:
893 defer_feedback(eax_call);
894 break;
896 case EAXCHORUS_DELAY:
897 defer_delay(eax_call);
898 break;
900 default:
901 throw EaxChorusEffectException{"Unsupported property id."};
904 if (!eax_call.is_deferred())
906 return apply_deferred();
909 return false;
913 } // namespace
916 EaxEffectUPtr eax_create_eax_chorus_effect()
918 return std::make_unique<::EaxChorusEffect>();
922 namespace
926 using EaxFlangerEffectDirtyFlagsValue = std::uint_least8_t;
928 struct EaxFlangerEffectDirtyFlags
930 using EaxIsBitFieldStruct = bool;
932 EaxFlangerEffectDirtyFlagsValue ulWaveform : 1;
933 EaxFlangerEffectDirtyFlagsValue lPhase : 1;
934 EaxFlangerEffectDirtyFlagsValue flRate : 1;
935 EaxFlangerEffectDirtyFlagsValue flDepth : 1;
936 EaxFlangerEffectDirtyFlagsValue flFeedback : 1;
937 EaxFlangerEffectDirtyFlagsValue flDelay : 1;
938 }; // EaxFlangerEffectDirtyFlags
941 class EaxFlangerEffect final :
942 public EaxEffect
944 public:
945 EaxFlangerEffect();
948 // [[nodiscard]]
949 bool dispatch(
950 const EaxEaxCall& eax_call) override;
953 private:
954 EAXFLANGERPROPERTIES eax_{};
955 EAXFLANGERPROPERTIES eax_d_{};
956 EaxFlangerEffectDirtyFlags eax_dirty_flags_{};
959 void set_eax_defaults();
962 void set_efx_waveform();
964 void set_efx_phase();
966 void set_efx_rate();
968 void set_efx_depth();
970 void set_efx_feedback();
972 void set_efx_delay();
974 void set_efx_defaults();
977 // [[nodiscard]]
978 bool get(
979 const EaxEaxCall& eax_call);
982 void validate_waveform(
983 unsigned long ulWaveform);
985 void validate_phase(
986 long lPhase);
988 void validate_rate(
989 float flRate);
991 void validate_depth(
992 float flDepth);
994 void validate_feedback(
995 float flFeedback);
997 void validate_delay(
998 float flDelay);
1000 void validate_all(
1001 const EAXFLANGERPROPERTIES& all);
1004 void defer_waveform(
1005 unsigned long ulWaveform);
1007 void defer_phase(
1008 long lPhase);
1010 void defer_rate(
1011 float flRate);
1013 void defer_depth(
1014 float flDepth);
1016 void defer_feedback(
1017 float flFeedback);
1019 void defer_delay(
1020 float flDelay);
1022 void defer_all(
1023 const EAXFLANGERPROPERTIES& all);
1026 void defer_waveform(
1027 const EaxEaxCall& eax_call);
1029 void defer_phase(
1030 const EaxEaxCall& eax_call);
1032 void defer_rate(
1033 const EaxEaxCall& eax_call);
1035 void defer_depth(
1036 const EaxEaxCall& eax_call);
1038 void defer_feedback(
1039 const EaxEaxCall& eax_call);
1041 void defer_delay(
1042 const EaxEaxCall& eax_call);
1044 void defer_all(
1045 const EaxEaxCall& eax_call);
1048 // [[nodiscard]]
1049 bool apply_deferred();
1051 // [[nodiscard]]
1052 bool set(
1053 const EaxEaxCall& eax_call);
1054 }; // EaxFlangerEffect
1057 class EaxFlangerEffectException :
1058 public EaxException
1060 public:
1061 explicit EaxFlangerEffectException(
1062 const char* message)
1064 EaxException{"EAX_FLANGER_EFFECT", message}
1067 }; // EaxFlangerEffectException
1070 EaxFlangerEffect::EaxFlangerEffect()
1071 : EaxEffect{AL_EFFECT_FLANGER}
1073 set_eax_defaults();
1074 set_efx_defaults();
1077 // [[nodiscard]]
1078 bool EaxFlangerEffect::dispatch(
1079 const EaxEaxCall& eax_call)
1081 return eax_call.is_get() ? get(eax_call) : set(eax_call);
1084 void EaxFlangerEffect::set_eax_defaults()
1086 eax_.ulWaveform = EAXFLANGER_DEFAULTWAVEFORM;
1087 eax_.lPhase = EAXFLANGER_DEFAULTPHASE;
1088 eax_.flRate = EAXFLANGER_DEFAULTRATE;
1089 eax_.flDepth = EAXFLANGER_DEFAULTDEPTH;
1090 eax_.flFeedback = EAXFLANGER_DEFAULTFEEDBACK;
1091 eax_.flDelay = EAXFLANGER_DEFAULTDELAY;
1093 eax_d_ = eax_;
1096 void EaxFlangerEffect::set_efx_waveform()
1098 const auto waveform = clamp(
1099 static_cast<ALint>(eax_.ulWaveform),
1100 AL_FLANGER_MIN_WAVEFORM,
1101 AL_FLANGER_MAX_WAVEFORM);
1103 eax_set_efx_waveform(waveform, al_effect_props_);
1106 void EaxFlangerEffect::set_efx_phase()
1108 const auto phase = clamp(
1109 static_cast<ALint>(eax_.lPhase),
1110 AL_FLANGER_MIN_PHASE,
1111 AL_FLANGER_MAX_PHASE);
1113 eax_set_efx_phase(phase, al_effect_props_);
1116 void EaxFlangerEffect::set_efx_rate()
1118 const auto rate = clamp(
1119 eax_.flRate,
1120 AL_FLANGER_MIN_RATE,
1121 AL_FLANGER_MAX_RATE);
1123 eax_set_efx_rate(rate, al_effect_props_);
1126 void EaxFlangerEffect::set_efx_depth()
1128 const auto depth = clamp(
1129 eax_.flDepth,
1130 AL_FLANGER_MIN_DEPTH,
1131 AL_FLANGER_MAX_DEPTH);
1133 eax_set_efx_depth(depth, al_effect_props_);
1136 void EaxFlangerEffect::set_efx_feedback()
1138 const auto feedback = clamp(
1139 eax_.flFeedback,
1140 AL_FLANGER_MIN_FEEDBACK,
1141 AL_FLANGER_MAX_FEEDBACK);
1143 eax_set_efx_feedback(feedback, al_effect_props_);
1146 void EaxFlangerEffect::set_efx_delay()
1148 const auto delay = clamp(
1149 eax_.flDelay,
1150 AL_FLANGER_MIN_DELAY,
1151 AL_FLANGER_MAX_DELAY);
1153 eax_set_efx_delay(delay, al_effect_props_);
1156 void EaxFlangerEffect::set_efx_defaults()
1158 set_efx_waveform();
1159 set_efx_phase();
1160 set_efx_rate();
1161 set_efx_depth();
1162 set_efx_feedback();
1163 set_efx_delay();
1166 // [[nodiscard]]
1167 bool EaxFlangerEffect::get(
1168 const EaxEaxCall& eax_call)
1170 switch (eax_call.get_property_id())
1172 case EAXFLANGER_NONE:
1173 break;
1175 case EAXFLANGER_ALLPARAMETERS:
1176 eax_call.set_value<EaxFlangerEffectException>(eax_);
1177 break;
1179 case EAXFLANGER_WAVEFORM:
1180 eax_call.set_value<EaxFlangerEffectException>(eax_.ulWaveform);
1181 break;
1183 case EAXFLANGER_PHASE:
1184 eax_call.set_value<EaxFlangerEffectException>(eax_.lPhase);
1185 break;
1187 case EAXFLANGER_RATE:
1188 eax_call.set_value<EaxFlangerEffectException>(eax_.flRate);
1189 break;
1191 case EAXFLANGER_DEPTH:
1192 eax_call.set_value<EaxFlangerEffectException>(eax_.flDepth);
1193 break;
1195 case EAXFLANGER_FEEDBACK:
1196 eax_call.set_value<EaxFlangerEffectException>(eax_.flFeedback);
1197 break;
1199 case EAXFLANGER_DELAY:
1200 eax_call.set_value<EaxFlangerEffectException>(eax_.flDelay);
1201 break;
1203 default:
1204 throw EaxFlangerEffectException{"Unsupported property id."};
1207 return false;
1210 void EaxFlangerEffect::validate_waveform(
1211 unsigned long ulWaveform)
1213 eax_validate_range<EaxFlangerEffectException>(
1214 "Waveform",
1215 ulWaveform,
1216 EAXFLANGER_MINWAVEFORM,
1217 EAXFLANGER_MAXWAVEFORM);
1220 void EaxFlangerEffect::validate_phase(
1221 long lPhase)
1223 eax_validate_range<EaxFlangerEffectException>(
1224 "Phase",
1225 lPhase,
1226 EAXFLANGER_MINPHASE,
1227 EAXFLANGER_MAXPHASE);
1230 void EaxFlangerEffect::validate_rate(
1231 float flRate)
1233 eax_validate_range<EaxFlangerEffectException>(
1234 "Rate",
1235 flRate,
1236 EAXFLANGER_MINRATE,
1237 EAXFLANGER_MAXRATE);
1240 void EaxFlangerEffect::validate_depth(
1241 float flDepth)
1243 eax_validate_range<EaxFlangerEffectException>(
1244 "Depth",
1245 flDepth,
1246 EAXFLANGER_MINDEPTH,
1247 EAXFLANGER_MAXDEPTH);
1250 void EaxFlangerEffect::validate_feedback(
1251 float flFeedback)
1253 eax_validate_range<EaxFlangerEffectException>(
1254 "Feedback",
1255 flFeedback,
1256 EAXFLANGER_MINFEEDBACK,
1257 EAXFLANGER_MAXFEEDBACK);
1260 void EaxFlangerEffect::validate_delay(
1261 float flDelay)
1263 eax_validate_range<EaxFlangerEffectException>(
1264 "Delay",
1265 flDelay,
1266 EAXFLANGER_MINDELAY,
1267 EAXFLANGER_MAXDELAY);
1270 void EaxFlangerEffect::validate_all(
1271 const EAXFLANGERPROPERTIES& all)
1273 validate_waveform(all.ulWaveform);
1274 validate_phase(all.lPhase);
1275 validate_rate(all.flRate);
1276 validate_depth(all.flDepth);
1277 validate_feedback(all.flDelay);
1278 validate_delay(all.flDelay);
1281 void EaxFlangerEffect::defer_waveform(
1282 unsigned long ulWaveform)
1284 eax_d_.ulWaveform = ulWaveform;
1285 eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
1288 void EaxFlangerEffect::defer_phase(
1289 long lPhase)
1291 eax_d_.lPhase = lPhase;
1292 eax_dirty_flags_.lPhase = (eax_.lPhase != eax_d_.lPhase);
1295 void EaxFlangerEffect::defer_rate(
1296 float flRate)
1298 eax_d_.flRate = flRate;
1299 eax_dirty_flags_.flRate = (eax_.flRate != eax_d_.flRate);
1302 void EaxFlangerEffect::defer_depth(
1303 float flDepth)
1305 eax_d_.flDepth = flDepth;
1306 eax_dirty_flags_.flDepth = (eax_.flDepth != eax_d_.flDepth);
1309 void EaxFlangerEffect::defer_feedback(
1310 float flFeedback)
1312 eax_d_.flFeedback = flFeedback;
1313 eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
1316 void EaxFlangerEffect::defer_delay(
1317 float flDelay)
1319 eax_d_.flDelay = flDelay;
1320 eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
1323 void EaxFlangerEffect::defer_all(
1324 const EAXFLANGERPROPERTIES& all)
1326 defer_waveform(all.ulWaveform);
1327 defer_phase(all.lPhase);
1328 defer_rate(all.flRate);
1329 defer_depth(all.flDepth);
1330 defer_feedback(all.flDelay);
1331 defer_delay(all.flDelay);
1334 void EaxFlangerEffect::defer_waveform(
1335 const EaxEaxCall& eax_call)
1337 const auto& waveform =
1338 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::ulWaveform)>();
1340 validate_waveform(waveform);
1341 defer_waveform(waveform);
1344 void EaxFlangerEffect::defer_phase(
1345 const EaxEaxCall& eax_call)
1347 const auto& phase =
1348 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::lPhase)>();
1350 validate_phase(phase);
1351 defer_phase(phase);
1354 void EaxFlangerEffect::defer_rate(
1355 const EaxEaxCall& eax_call)
1357 const auto& rate =
1358 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flRate)>();
1360 validate_rate(rate);
1361 defer_rate(rate);
1364 void EaxFlangerEffect::defer_depth(
1365 const EaxEaxCall& eax_call)
1367 const auto& depth =
1368 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flDepth)>();
1370 validate_depth(depth);
1371 defer_depth(depth);
1374 void EaxFlangerEffect::defer_feedback(
1375 const EaxEaxCall& eax_call)
1377 const auto& feedback =
1378 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flFeedback)>();
1380 validate_feedback(feedback);
1381 defer_feedback(feedback);
1384 void EaxFlangerEffect::defer_delay(
1385 const EaxEaxCall& eax_call)
1387 const auto& delay =
1388 eax_call.get_value<EaxFlangerEffectException, const decltype(EAXFLANGERPROPERTIES::flDelay)>();
1390 validate_delay(delay);
1391 defer_delay(delay);
1394 void EaxFlangerEffect::defer_all(
1395 const EaxEaxCall& eax_call)
1397 const auto& all =
1398 eax_call.get_value<EaxFlangerEffectException, const EAXFLANGERPROPERTIES>();
1400 validate_all(all);
1401 defer_all(all);
1404 // [[nodiscard]]
1405 bool EaxFlangerEffect::apply_deferred()
1407 if (eax_dirty_flags_ == EaxFlangerEffectDirtyFlags{})
1409 return false;
1412 eax_ = eax_d_;
1414 if (eax_dirty_flags_.ulWaveform)
1416 set_efx_waveform();
1419 if (eax_dirty_flags_.lPhase)
1421 set_efx_phase();
1424 if (eax_dirty_flags_.flRate)
1426 set_efx_rate();
1429 if (eax_dirty_flags_.flDepth)
1431 set_efx_depth();
1434 if (eax_dirty_flags_.flFeedback)
1436 set_efx_feedback();
1439 if (eax_dirty_flags_.flDelay)
1441 set_efx_delay();
1444 eax_dirty_flags_ = EaxFlangerEffectDirtyFlags{};
1446 return true;
1449 // [[nodiscard]]
1450 bool EaxFlangerEffect::set(
1451 const EaxEaxCall& eax_call)
1453 switch (eax_call.get_property_id())
1455 case EAXFLANGER_NONE:
1456 break;
1458 case EAXFLANGER_ALLPARAMETERS:
1459 defer_all(eax_call);
1460 break;
1462 case EAXFLANGER_WAVEFORM:
1463 defer_waveform(eax_call);
1464 break;
1466 case EAXFLANGER_PHASE:
1467 defer_phase(eax_call);
1468 break;
1470 case EAXFLANGER_RATE:
1471 defer_rate(eax_call);
1472 break;
1474 case EAXFLANGER_DEPTH:
1475 defer_depth(eax_call);
1476 break;
1478 case EAXFLANGER_FEEDBACK:
1479 defer_feedback(eax_call);
1480 break;
1482 case EAXFLANGER_DELAY:
1483 defer_delay(eax_call);
1484 break;
1486 default:
1487 throw EaxFlangerEffectException{"Unsupported property id."};
1490 if (!eax_call.is_deferred())
1492 return apply_deferred();
1495 return false;
1498 } // namespace
1500 EaxEffectUPtr eax_create_eax_flanger_effect()
1502 return std::make_unique<EaxFlangerEffect>();
1505 #endif // ALSOFT_EAX