Check for a minimum PipeWire version
[openal-soft.git] / al / effects / modulator.cpp
blob800b892de2535c930629b6ce71768f517ae9a564
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 "effects.h"
13 #ifdef ALSOFT_EAX
14 #include <cassert>
16 #include "alnumeric.h"
18 #include "al/eax_exception.h"
19 #include "al/eax_utils.h"
20 #endif // ALSOFT_EAX
23 namespace {
25 al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
27 switch(value)
29 case AL_RING_MODULATOR_SINUSOID: return al::make_optional(ModulatorWaveform::Sinusoid);
30 case AL_RING_MODULATOR_SAWTOOTH: return al::make_optional(ModulatorWaveform::Sawtooth);
31 case AL_RING_MODULATOR_SQUARE: return al::make_optional(ModulatorWaveform::Square);
33 return al::nullopt;
35 ALenum EnumFromWaveform(ModulatorWaveform type)
37 switch(type)
39 case ModulatorWaveform::Sinusoid: return AL_RING_MODULATOR_SINUSOID;
40 case ModulatorWaveform::Sawtooth: return AL_RING_MODULATOR_SAWTOOTH;
41 case ModulatorWaveform::Square: return AL_RING_MODULATOR_SQUARE;
43 throw std::runtime_error{"Invalid modulator waveform: " +
44 std::to_string(static_cast<int>(type))};
47 void Modulator_setParamf(EffectProps *props, ALenum param, float val)
49 switch(param)
51 case AL_RING_MODULATOR_FREQUENCY:
52 if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
53 throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
54 props->Modulator.Frequency = val;
55 break;
57 case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
58 if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
59 throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
60 props->Modulator.HighPassCutoff = val;
61 break;
63 default:
64 throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
67 void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
68 { Modulator_setParamf(props, param, vals[0]); }
69 void Modulator_setParami(EffectProps *props, ALenum param, int val)
71 switch(param)
73 case AL_RING_MODULATOR_FREQUENCY:
74 case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
75 Modulator_setParamf(props, param, static_cast<float>(val));
76 break;
78 case AL_RING_MODULATOR_WAVEFORM:
79 if(auto formopt = WaveformFromEmum(val))
80 props->Modulator.Waveform = *formopt;
81 else
82 throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
83 break;
85 default:
86 throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
87 param};
90 void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
91 { Modulator_setParami(props, param, vals[0]); }
93 void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
95 switch(param)
97 case AL_RING_MODULATOR_FREQUENCY:
98 *val = static_cast<int>(props->Modulator.Frequency);
99 break;
100 case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
101 *val = static_cast<int>(props->Modulator.HighPassCutoff);
102 break;
103 case AL_RING_MODULATOR_WAVEFORM:
104 *val = EnumFromWaveform(props->Modulator.Waveform);
105 break;
107 default:
108 throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
109 param};
112 void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals)
113 { Modulator_getParami(props, param, vals); }
114 void Modulator_getParamf(const EffectProps *props, ALenum param, float *val)
116 switch(param)
118 case AL_RING_MODULATOR_FREQUENCY:
119 *val = props->Modulator.Frequency;
120 break;
121 case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
122 *val = props->Modulator.HighPassCutoff;
123 break;
125 default:
126 throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
129 void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
130 { Modulator_getParamf(props, param, vals); }
132 EffectProps genDefaultProps() noexcept
134 EffectProps props{};
135 props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
136 props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
137 props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
138 return props;
141 } // namespace
143 DEFINE_ALEFFECT_VTABLE(Modulator);
145 const EffectProps ModulatorEffectProps{genDefaultProps()};
147 #ifdef ALSOFT_EAX
148 namespace {
150 using EaxRingModulatorEffectDirtyFlagsValue = std::uint_least8_t;
152 struct EaxRingModulatorEffectDirtyFlags
154 using EaxIsBitFieldStruct = bool;
156 EaxRingModulatorEffectDirtyFlagsValue flFrequency : 1;
157 EaxRingModulatorEffectDirtyFlagsValue flHighPassCutOff : 1;
158 EaxRingModulatorEffectDirtyFlagsValue ulWaveform : 1;
159 }; // EaxPitchShifterEffectDirtyFlags
162 class EaxRingModulatorEffect final :
163 public EaxEffect
165 public:
166 EaxRingModulatorEffect();
168 void dispatch(const EaxEaxCall& eax_call) override;
170 // [[nodiscard]]
171 bool apply_deferred() override;
173 private:
174 EAXRINGMODULATORPROPERTIES eax_{};
175 EAXRINGMODULATORPROPERTIES eax_d_{};
176 EaxRingModulatorEffectDirtyFlags eax_dirty_flags_{};
178 void set_eax_defaults();
180 void set_efx_frequency();
181 void set_efx_high_pass_cutoff();
182 void set_efx_waveform();
183 void set_efx_defaults();
185 void get(const EaxEaxCall& eax_call);
187 void validate_frequency(float flFrequency);
188 void validate_high_pass_cutoff(float flHighPassCutOff);
189 void validate_waveform(unsigned long ulWaveform);
190 void validate_all(const EAXRINGMODULATORPROPERTIES& all);
192 void defer_frequency(float flFrequency);
193 void defer_high_pass_cutoff(float flHighPassCutOff);
194 void defer_waveform(unsigned long ulWaveform);
195 void defer_all(const EAXRINGMODULATORPROPERTIES& all);
197 void defer_frequency(const EaxEaxCall& eax_call);
198 void defer_high_pass_cutoff(const EaxEaxCall& eax_call);
199 void defer_waveform(const EaxEaxCall& eax_call);
200 void defer_all(const EaxEaxCall& eax_call);
202 void set(const EaxEaxCall& eax_call);
203 }; // EaxRingModulatorEffect
206 class EaxRingModulatorEffectException :
207 public EaxException
209 public:
210 explicit EaxRingModulatorEffectException(
211 const char* message)
213 EaxException{"EAX_RING_MODULATOR_EFFECT", message}
216 }; // EaxRingModulatorEffectException
219 EaxRingModulatorEffect::EaxRingModulatorEffect()
220 : EaxEffect{AL_EFFECT_RING_MODULATOR}
222 set_eax_defaults();
223 set_efx_defaults();
226 void EaxRingModulatorEffect::dispatch(const EaxEaxCall& eax_call)
228 eax_call.is_get() ? get(eax_call) : set(eax_call);
231 void EaxRingModulatorEffect::set_eax_defaults()
233 eax_.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
234 eax_.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
235 eax_.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
237 eax_d_ = eax_;
240 void EaxRingModulatorEffect::set_efx_frequency()
242 const auto frequency = clamp(
243 eax_.flFrequency,
244 AL_RING_MODULATOR_MIN_FREQUENCY,
245 AL_RING_MODULATOR_MAX_FREQUENCY);
247 al_effect_props_.Modulator.Frequency = frequency;
250 void EaxRingModulatorEffect::set_efx_high_pass_cutoff()
252 const auto high_pass_cutoff = clamp(
253 eax_.flHighPassCutOff,
254 AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF,
255 AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF);
257 al_effect_props_.Modulator.HighPassCutoff = high_pass_cutoff;
260 void EaxRingModulatorEffect::set_efx_waveform()
262 const auto waveform = clamp(
263 static_cast<ALint>(eax_.ulWaveform),
264 AL_RING_MODULATOR_MIN_WAVEFORM,
265 AL_RING_MODULATOR_MAX_WAVEFORM);
267 const auto efx_waveform = WaveformFromEmum(waveform);
268 assert(efx_waveform.has_value());
269 al_effect_props_.Modulator.Waveform = *efx_waveform;
272 void EaxRingModulatorEffect::set_efx_defaults()
274 set_efx_frequency();
275 set_efx_high_pass_cutoff();
276 set_efx_waveform();
279 void EaxRingModulatorEffect::get(const EaxEaxCall& eax_call)
281 switch(eax_call.get_property_id())
283 case EAXRINGMODULATOR_NONE:
284 break;
286 case EAXRINGMODULATOR_ALLPARAMETERS:
287 eax_call.set_value<EaxRingModulatorEffectException>(eax_);
288 break;
290 case EAXRINGMODULATOR_FREQUENCY:
291 eax_call.set_value<EaxRingModulatorEffectException>(eax_.flFrequency);
292 break;
294 case EAXRINGMODULATOR_HIGHPASSCUTOFF:
295 eax_call.set_value<EaxRingModulatorEffectException>(eax_.flHighPassCutOff);
296 break;
298 case EAXRINGMODULATOR_WAVEFORM:
299 eax_call.set_value<EaxRingModulatorEffectException>(eax_.ulWaveform);
300 break;
302 default:
303 throw EaxRingModulatorEffectException{"Unsupported property id."};
307 void EaxRingModulatorEffect::validate_frequency(
308 float flFrequency)
310 eax_validate_range<EaxRingModulatorEffectException>(
311 "Frequency",
312 flFrequency,
313 EAXRINGMODULATOR_MINFREQUENCY,
314 EAXRINGMODULATOR_MAXFREQUENCY);
317 void EaxRingModulatorEffect::validate_high_pass_cutoff(
318 float flHighPassCutOff)
320 eax_validate_range<EaxRingModulatorEffectException>(
321 "High-Pass Cutoff",
322 flHighPassCutOff,
323 EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
324 EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
327 void EaxRingModulatorEffect::validate_waveform(
328 unsigned long ulWaveform)
330 eax_validate_range<EaxRingModulatorEffectException>(
331 "Waveform",
332 ulWaveform,
333 EAXRINGMODULATOR_MINWAVEFORM,
334 EAXRINGMODULATOR_MAXWAVEFORM);
337 void EaxRingModulatorEffect::validate_all(
338 const EAXRINGMODULATORPROPERTIES& all)
340 validate_frequency(all.flFrequency);
341 validate_high_pass_cutoff(all.flHighPassCutOff);
342 validate_waveform(all.ulWaveform);
345 void EaxRingModulatorEffect::defer_frequency(
346 float flFrequency)
348 eax_d_.flFrequency = flFrequency;
349 eax_dirty_flags_.flFrequency = (eax_.flFrequency != eax_d_.flFrequency);
352 void EaxRingModulatorEffect::defer_high_pass_cutoff(
353 float flHighPassCutOff)
355 eax_d_.flHighPassCutOff = flHighPassCutOff;
356 eax_dirty_flags_.flHighPassCutOff = (eax_.flHighPassCutOff != eax_d_.flHighPassCutOff);
359 void EaxRingModulatorEffect::defer_waveform(
360 unsigned long ulWaveform)
362 eax_d_.ulWaveform = ulWaveform;
363 eax_dirty_flags_.ulWaveform = (eax_.ulWaveform != eax_d_.ulWaveform);
366 void EaxRingModulatorEffect::defer_all(
367 const EAXRINGMODULATORPROPERTIES& all)
369 defer_frequency(all.flFrequency);
370 defer_high_pass_cutoff(all.flHighPassCutOff);
371 defer_waveform(all.ulWaveform);
374 void EaxRingModulatorEffect::defer_frequency(
375 const EaxEaxCall& eax_call)
377 const auto& frequency =
378 eax_call.get_value<
379 EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flFrequency)>();
381 validate_frequency(frequency);
382 defer_frequency(frequency);
385 void EaxRingModulatorEffect::defer_high_pass_cutoff(
386 const EaxEaxCall& eax_call)
388 const auto& high_pass_cutoff =
389 eax_call.get_value<
390 EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::flHighPassCutOff)>();
392 validate_high_pass_cutoff(high_pass_cutoff);
393 defer_high_pass_cutoff(high_pass_cutoff);
396 void EaxRingModulatorEffect::defer_waveform(
397 const EaxEaxCall& eax_call)
399 const auto& waveform =
400 eax_call.get_value<
401 EaxRingModulatorEffectException, const decltype(EAXRINGMODULATORPROPERTIES::ulWaveform)>();
403 validate_waveform(waveform);
404 defer_waveform(waveform);
407 void EaxRingModulatorEffect::defer_all(
408 const EaxEaxCall& eax_call)
410 const auto& all =
411 eax_call.get_value<EaxRingModulatorEffectException, const EAXRINGMODULATORPROPERTIES>();
413 validate_all(all);
414 defer_all(all);
417 // [[nodiscard]]
418 bool EaxRingModulatorEffect::apply_deferred()
420 if (eax_dirty_flags_ == EaxRingModulatorEffectDirtyFlags{})
422 return false;
425 eax_ = eax_d_;
427 if (eax_dirty_flags_.flFrequency)
429 set_efx_frequency();
432 if (eax_dirty_flags_.flHighPassCutOff)
434 set_efx_high_pass_cutoff();
437 if (eax_dirty_flags_.ulWaveform)
439 set_efx_waveform();
442 eax_dirty_flags_ = EaxRingModulatorEffectDirtyFlags{};
444 return true;
447 void EaxRingModulatorEffect::set(const EaxEaxCall& eax_call)
449 switch (eax_call.get_property_id())
451 case EAXRINGMODULATOR_NONE:
452 break;
454 case EAXRINGMODULATOR_ALLPARAMETERS:
455 defer_all(eax_call);
456 break;
458 case EAXRINGMODULATOR_FREQUENCY:
459 defer_frequency(eax_call);
460 break;
462 case EAXRINGMODULATOR_HIGHPASSCUTOFF:
463 defer_high_pass_cutoff(eax_call);
464 break;
466 case EAXRINGMODULATOR_WAVEFORM:
467 defer_waveform(eax_call);
468 break;
470 default:
471 throw EaxRingModulatorEffectException{"Unsupported property id."};
475 } // namespace
477 EaxEffectUPtr eax_create_eax_ring_modulator_effect()
479 return std::make_unique<EaxRingModulatorEffect>();
482 #endif // ALSOFT_EAX