🛠️🐜 Antkeeper superbuild with dependencies included https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

319 lines
9.7 KiB

  1. #include "config.h"
  2. #include <stdexcept>
  3. #include "AL/al.h"
  4. #include "AL/efx.h"
  5. #include "alc/effects/base.h"
  6. #include "aloptional.h"
  7. #include "effects.h"
  8. #ifdef ALSOFT_EAX
  9. #include <cassert>
  10. #include "alnumeric.h"
  11. #include "al/eax/exception.h"
  12. #include "al/eax/utils.h"
  13. #endif // ALSOFT_EAX
  14. namespace {
  15. al::optional<ModulatorWaveform> WaveformFromEmum(ALenum value)
  16. {
  17. switch(value)
  18. {
  19. case AL_RING_MODULATOR_SINUSOID: return al::make_optional(ModulatorWaveform::Sinusoid);
  20. case AL_RING_MODULATOR_SAWTOOTH: return al::make_optional(ModulatorWaveform::Sawtooth);
  21. case AL_RING_MODULATOR_SQUARE: return al::make_optional(ModulatorWaveform::Square);
  22. }
  23. return al::nullopt;
  24. }
  25. ALenum EnumFromWaveform(ModulatorWaveform type)
  26. {
  27. switch(type)
  28. {
  29. case ModulatorWaveform::Sinusoid: return AL_RING_MODULATOR_SINUSOID;
  30. case ModulatorWaveform::Sawtooth: return AL_RING_MODULATOR_SAWTOOTH;
  31. case ModulatorWaveform::Square: return AL_RING_MODULATOR_SQUARE;
  32. }
  33. throw std::runtime_error{"Invalid modulator waveform: " +
  34. std::to_string(static_cast<int>(type))};
  35. }
  36. void Modulator_setParamf(EffectProps *props, ALenum param, float val)
  37. {
  38. switch(param)
  39. {
  40. case AL_RING_MODULATOR_FREQUENCY:
  41. if(!(val >= AL_RING_MODULATOR_MIN_FREQUENCY && val <= AL_RING_MODULATOR_MAX_FREQUENCY))
  42. throw effect_exception{AL_INVALID_VALUE, "Modulator frequency out of range: %f", val};
  43. props->Modulator.Frequency = val;
  44. break;
  45. case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
  46. if(!(val >= AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF && val <= AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF))
  47. throw effect_exception{AL_INVALID_VALUE, "Modulator high-pass cutoff out of range: %f", val};
  48. props->Modulator.HighPassCutoff = val;
  49. break;
  50. default:
  51. throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
  52. }
  53. }
  54. void Modulator_setParamfv(EffectProps *props, ALenum param, const float *vals)
  55. { Modulator_setParamf(props, param, vals[0]); }
  56. void Modulator_setParami(EffectProps *props, ALenum param, int val)
  57. {
  58. switch(param)
  59. {
  60. case AL_RING_MODULATOR_FREQUENCY:
  61. case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
  62. Modulator_setParamf(props, param, static_cast<float>(val));
  63. break;
  64. case AL_RING_MODULATOR_WAVEFORM:
  65. if(auto formopt = WaveformFromEmum(val))
  66. props->Modulator.Waveform = *formopt;
  67. else
  68. throw effect_exception{AL_INVALID_VALUE, "Invalid modulator waveform: 0x%04x", val};
  69. break;
  70. default:
  71. throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
  72. param};
  73. }
  74. }
  75. void Modulator_setParamiv(EffectProps *props, ALenum param, const int *vals)
  76. { Modulator_setParami(props, param, vals[0]); }
  77. void Modulator_getParami(const EffectProps *props, ALenum param, int *val)
  78. {
  79. switch(param)
  80. {
  81. case AL_RING_MODULATOR_FREQUENCY:
  82. *val = static_cast<int>(props->Modulator.Frequency);
  83. break;
  84. case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
  85. *val = static_cast<int>(props->Modulator.HighPassCutoff);
  86. break;
  87. case AL_RING_MODULATOR_WAVEFORM:
  88. *val = EnumFromWaveform(props->Modulator.Waveform);
  89. break;
  90. default:
  91. throw effect_exception{AL_INVALID_ENUM, "Invalid modulator integer property 0x%04x",
  92. param};
  93. }
  94. }
  95. void Modulator_getParamiv(const EffectProps *props, ALenum param, int *vals)
  96. { Modulator_getParami(props, param, vals); }
  97. void Modulator_getParamf(const EffectProps *props, ALenum param, float *val)
  98. {
  99. switch(param)
  100. {
  101. case AL_RING_MODULATOR_FREQUENCY:
  102. *val = props->Modulator.Frequency;
  103. break;
  104. case AL_RING_MODULATOR_HIGHPASS_CUTOFF:
  105. *val = props->Modulator.HighPassCutoff;
  106. break;
  107. default:
  108. throw effect_exception{AL_INVALID_ENUM, "Invalid modulator float property 0x%04x", param};
  109. }
  110. }
  111. void Modulator_getParamfv(const EffectProps *props, ALenum param, float *vals)
  112. { Modulator_getParamf(props, param, vals); }
  113. EffectProps genDefaultProps() noexcept
  114. {
  115. EffectProps props{};
  116. props.Modulator.Frequency = AL_RING_MODULATOR_DEFAULT_FREQUENCY;
  117. props.Modulator.HighPassCutoff = AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF;
  118. props.Modulator.Waveform = *WaveformFromEmum(AL_RING_MODULATOR_DEFAULT_WAVEFORM);
  119. return props;
  120. }
  121. } // namespace
  122. DEFINE_ALEFFECT_VTABLE(Modulator);
  123. const EffectProps ModulatorEffectProps{genDefaultProps()};
  124. #ifdef ALSOFT_EAX
  125. namespace {
  126. class EaxRingModulatorEffectException : public EaxException
  127. {
  128. public:
  129. explicit EaxRingModulatorEffectException(const char* message)
  130. : EaxException{"EAX_RING_MODULATOR_EFFECT", message}
  131. {}
  132. }; // EaxRingModulatorEffectException
  133. class EaxRingModulatorEffect final : public EaxEffect4<EaxRingModulatorEffectException, EAXRINGMODULATORPROPERTIES>
  134. {
  135. public:
  136. EaxRingModulatorEffect(const EaxCall& call);
  137. private:
  138. struct FrequencyValidator {
  139. void operator()(float flFrequency) const
  140. {
  141. eax_validate_range<EaxRingModulatorEffectException>(
  142. "Frequency",
  143. flFrequency,
  144. EAXRINGMODULATOR_MINFREQUENCY,
  145. EAXRINGMODULATOR_MAXFREQUENCY);
  146. }
  147. }; // FrequencyValidator
  148. struct HighPassCutOffValidator {
  149. void operator()(float flHighPassCutOff) const
  150. {
  151. eax_validate_range<EaxRingModulatorEffectException>(
  152. "High-Pass Cutoff",
  153. flHighPassCutOff,
  154. EAXRINGMODULATOR_MINHIGHPASSCUTOFF,
  155. EAXRINGMODULATOR_MAXHIGHPASSCUTOFF);
  156. }
  157. }; // HighPassCutOffValidator
  158. struct WaveformValidator {
  159. void operator()(unsigned long ulWaveform) const
  160. {
  161. eax_validate_range<EaxRingModulatorEffectException>(
  162. "Waveform",
  163. ulWaveform,
  164. EAXRINGMODULATOR_MINWAVEFORM,
  165. EAXRINGMODULATOR_MAXWAVEFORM);
  166. }
  167. }; // WaveformValidator
  168. struct AllValidator {
  169. void operator()(const Props& all) const
  170. {
  171. FrequencyValidator{}(all.flFrequency);
  172. HighPassCutOffValidator{}(all.flHighPassCutOff);
  173. WaveformValidator{}(all.ulWaveform);
  174. }
  175. }; // AllValidator
  176. void set_defaults(Props& props) override;
  177. void set_efx_frequency() noexcept;
  178. void set_efx_high_pass_cutoff() noexcept;
  179. void set_efx_waveform();
  180. void set_efx_defaults() override;
  181. void get(const EaxCall& call, const Props& props) override;
  182. void set(const EaxCall& call, Props& props) override;
  183. bool commit_props(const Props& props) override;
  184. }; // EaxRingModulatorEffect
  185. EaxRingModulatorEffect::EaxRingModulatorEffect(const EaxCall& call)
  186. : EaxEffect4{AL_EFFECT_RING_MODULATOR, call}
  187. {}
  188. void EaxRingModulatorEffect::set_defaults(Props& props)
  189. {
  190. props.flFrequency = EAXRINGMODULATOR_DEFAULTFREQUENCY;
  191. props.flHighPassCutOff = EAXRINGMODULATOR_DEFAULTHIGHPASSCUTOFF;
  192. props.ulWaveform = EAXRINGMODULATOR_DEFAULTWAVEFORM;
  193. }
  194. void EaxRingModulatorEffect::set_efx_frequency() noexcept
  195. {
  196. al_effect_props_.Modulator.Frequency = clamp(
  197. props_.flFrequency,
  198. AL_RING_MODULATOR_MIN_FREQUENCY,
  199. AL_RING_MODULATOR_MAX_FREQUENCY);
  200. }
  201. void EaxRingModulatorEffect::set_efx_high_pass_cutoff() noexcept
  202. {
  203. al_effect_props_.Modulator.HighPassCutoff = clamp(
  204. props_.flHighPassCutOff,
  205. AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF,
  206. AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF);
  207. }
  208. void EaxRingModulatorEffect::set_efx_waveform()
  209. {
  210. const auto waveform = clamp(
  211. static_cast<ALint>(props_.ulWaveform),
  212. AL_RING_MODULATOR_MIN_WAVEFORM,
  213. AL_RING_MODULATOR_MAX_WAVEFORM);
  214. const auto efx_waveform = WaveformFromEmum(waveform);
  215. assert(efx_waveform.has_value());
  216. al_effect_props_.Modulator.Waveform = *efx_waveform;
  217. }
  218. void EaxRingModulatorEffect::set_efx_defaults()
  219. {
  220. set_efx_frequency();
  221. set_efx_high_pass_cutoff();
  222. set_efx_waveform();
  223. }
  224. void EaxRingModulatorEffect::get(const EaxCall& call, const Props& props)
  225. {
  226. switch(call.get_property_id())
  227. {
  228. case EAXRINGMODULATOR_NONE: break;
  229. case EAXRINGMODULATOR_ALLPARAMETERS: call.set_value<Exception>(props); break;
  230. case EAXRINGMODULATOR_FREQUENCY: call.set_value<Exception>(props.flFrequency); break;
  231. case EAXRINGMODULATOR_HIGHPASSCUTOFF: call.set_value<Exception>(props.flHighPassCutOff); break;
  232. case EAXRINGMODULATOR_WAVEFORM: call.set_value<Exception>(props.ulWaveform); break;
  233. default: fail_unknown_property_id();
  234. }
  235. }
  236. void EaxRingModulatorEffect::set(const EaxCall& call, Props& props)
  237. {
  238. switch (call.get_property_id())
  239. {
  240. case EAXRINGMODULATOR_NONE: break;
  241. case EAXRINGMODULATOR_ALLPARAMETERS: defer<AllValidator>(call, props); break;
  242. case EAXRINGMODULATOR_FREQUENCY: defer<FrequencyValidator>(call, props.flFrequency); break;
  243. case EAXRINGMODULATOR_HIGHPASSCUTOFF: defer<HighPassCutOffValidator>(call, props.flHighPassCutOff); break;
  244. case EAXRINGMODULATOR_WAVEFORM: defer<WaveformValidator>(call, props.ulWaveform); break;
  245. default: fail_unknown_property_id();
  246. }
  247. }
  248. bool EaxRingModulatorEffect::commit_props(const Props& props)
  249. {
  250. auto is_dirty = false;
  251. if (props_.flFrequency != props.flFrequency)
  252. {
  253. is_dirty = true;
  254. set_efx_frequency();
  255. }
  256. if (props_.flHighPassCutOff != props.flHighPassCutOff)
  257. {
  258. is_dirty = true;
  259. set_efx_high_pass_cutoff();
  260. }
  261. if (props_.ulWaveform != props.ulWaveform)
  262. {
  263. is_dirty = true;
  264. set_efx_waveform();
  265. }
  266. return is_dirty;
  267. }
  268. } // namespace
  269. EaxEffectUPtr eax_create_eax_ring_modulator_effect(const EaxCall& call)
  270. {
  271. return eax_create_eax4_effect<EaxRingModulatorEffect>(call);
  272. }
  273. #endif // ALSOFT_EAX