🛠️🐜 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.

548 lines
13 KiB

  1. #include "config.h"
  2. #include <cmath>
  3. #include <cstdlib>
  4. #include <algorithm>
  5. #include "AL/efx.h"
  6. #include "alc/effects/base.h"
  7. #include "effects.h"
  8. #ifdef ALSOFT_EAX
  9. #include "alnumeric.h"
  10. #include "al/eax_exception.h"
  11. #include "al/eax_utils.h"
  12. #endif // ALSOFT_EAX
  13. namespace {
  14. void Autowah_setParamf(EffectProps *props, ALenum param, float val)
  15. {
  16. switch(param)
  17. {
  18. case AL_AUTOWAH_ATTACK_TIME:
  19. if(!(val >= AL_AUTOWAH_MIN_ATTACK_TIME && val <= AL_AUTOWAH_MAX_ATTACK_TIME))
  20. throw effect_exception{AL_INVALID_VALUE, "Autowah attack time out of range"};
  21. props->Autowah.AttackTime = val;
  22. break;
  23. case AL_AUTOWAH_RELEASE_TIME:
  24. if(!(val >= AL_AUTOWAH_MIN_RELEASE_TIME && val <= AL_AUTOWAH_MAX_RELEASE_TIME))
  25. throw effect_exception{AL_INVALID_VALUE, "Autowah release time out of range"};
  26. props->Autowah.ReleaseTime = val;
  27. break;
  28. case AL_AUTOWAH_RESONANCE:
  29. if(!(val >= AL_AUTOWAH_MIN_RESONANCE && val <= AL_AUTOWAH_MAX_RESONANCE))
  30. throw effect_exception{AL_INVALID_VALUE, "Autowah resonance out of range"};
  31. props->Autowah.Resonance = val;
  32. break;
  33. case AL_AUTOWAH_PEAK_GAIN:
  34. if(!(val >= AL_AUTOWAH_MIN_PEAK_GAIN && val <= AL_AUTOWAH_MAX_PEAK_GAIN))
  35. throw effect_exception{AL_INVALID_VALUE, "Autowah peak gain out of range"};
  36. props->Autowah.PeakGain = val;
  37. break;
  38. default:
  39. throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
  40. }
  41. }
  42. void Autowah_setParamfv(EffectProps *props, ALenum param, const float *vals)
  43. { Autowah_setParamf(props, param, vals[0]); }
  44. void Autowah_setParami(EffectProps*, ALenum param, int)
  45. { throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
  46. void Autowah_setParamiv(EffectProps*, ALenum param, const int*)
  47. {
  48. throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
  49. param};
  50. }
  51. void Autowah_getParamf(const EffectProps *props, ALenum param, float *val)
  52. {
  53. switch(param)
  54. {
  55. case AL_AUTOWAH_ATTACK_TIME:
  56. *val = props->Autowah.AttackTime;
  57. break;
  58. case AL_AUTOWAH_RELEASE_TIME:
  59. *val = props->Autowah.ReleaseTime;
  60. break;
  61. case AL_AUTOWAH_RESONANCE:
  62. *val = props->Autowah.Resonance;
  63. break;
  64. case AL_AUTOWAH_PEAK_GAIN:
  65. *val = props->Autowah.PeakGain;
  66. break;
  67. default:
  68. throw effect_exception{AL_INVALID_ENUM, "Invalid autowah float property 0x%04x", param};
  69. }
  70. }
  71. void Autowah_getParamfv(const EffectProps *props, ALenum param, float *vals)
  72. { Autowah_getParamf(props, param, vals); }
  73. void Autowah_getParami(const EffectProps*, ALenum param, int*)
  74. { throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer property 0x%04x", param}; }
  75. void Autowah_getParamiv(const EffectProps*, ALenum param, int*)
  76. {
  77. throw effect_exception{AL_INVALID_ENUM, "Invalid autowah integer vector property 0x%04x",
  78. param};
  79. }
  80. EffectProps genDefaultProps() noexcept
  81. {
  82. EffectProps props{};
  83. props.Autowah.AttackTime = AL_AUTOWAH_DEFAULT_ATTACK_TIME;
  84. props.Autowah.ReleaseTime = AL_AUTOWAH_DEFAULT_RELEASE_TIME;
  85. props.Autowah.Resonance = AL_AUTOWAH_DEFAULT_RESONANCE;
  86. props.Autowah.PeakGain = AL_AUTOWAH_DEFAULT_PEAK_GAIN;
  87. return props;
  88. }
  89. } // namespace
  90. DEFINE_ALEFFECT_VTABLE(Autowah);
  91. const EffectProps AutowahEffectProps{genDefaultProps()};
  92. #ifdef ALSOFT_EAX
  93. namespace {
  94. using EaxAutoWahEffectDirtyFlagsValue = std::uint_least8_t;
  95. struct EaxAutoWahEffectDirtyFlags
  96. {
  97. using EaxIsBitFieldStruct = bool;
  98. EaxAutoWahEffectDirtyFlagsValue flAttackTime : 1;
  99. EaxAutoWahEffectDirtyFlagsValue flReleaseTime : 1;
  100. EaxAutoWahEffectDirtyFlagsValue lResonance : 1;
  101. EaxAutoWahEffectDirtyFlagsValue lPeakLevel : 1;
  102. }; // EaxAutoWahEffectDirtyFlags
  103. class EaxAutoWahEffect final :
  104. public EaxEffect
  105. {
  106. public:
  107. EaxAutoWahEffect();
  108. void dispatch(const EaxEaxCall& eax_call) override;
  109. // [[nodiscard]]
  110. bool apply_deferred() override;
  111. private:
  112. EAXAUTOWAHPROPERTIES eax_{};
  113. EAXAUTOWAHPROPERTIES eax_d_{};
  114. EaxAutoWahEffectDirtyFlags eax_dirty_flags_{};
  115. void set_eax_defaults();
  116. void set_efx_attack_time();
  117. void set_efx_release_time();
  118. void set_efx_resonance();
  119. void set_efx_peak_gain();
  120. void set_efx_defaults();
  121. void get(const EaxEaxCall& eax_call);
  122. void validate_attack_time(
  123. float flAttackTime);
  124. void validate_release_time(
  125. float flReleaseTime);
  126. void validate_resonance(
  127. long lResonance);
  128. void validate_peak_level(
  129. long lPeakLevel);
  130. void validate_all(
  131. const EAXAUTOWAHPROPERTIES& eax_all);
  132. void defer_attack_time(
  133. float flAttackTime);
  134. void defer_release_time(
  135. float flReleaseTime);
  136. void defer_resonance(
  137. long lResonance);
  138. void defer_peak_level(
  139. long lPeakLevel);
  140. void defer_all(
  141. const EAXAUTOWAHPROPERTIES& eax_all);
  142. void defer_attack_time(
  143. const EaxEaxCall& eax_call);
  144. void defer_release_time(
  145. const EaxEaxCall& eax_call);
  146. void defer_resonance(
  147. const EaxEaxCall& eax_call);
  148. void defer_peak_level(
  149. const EaxEaxCall& eax_call);
  150. void defer_all(
  151. const EaxEaxCall& eax_call);
  152. void set(const EaxEaxCall& eax_call);
  153. }; // EaxAutoWahEffect
  154. class EaxAutoWahEffectException :
  155. public EaxException
  156. {
  157. public:
  158. explicit EaxAutoWahEffectException(
  159. const char* message)
  160. :
  161. EaxException{"EAX_AUTO_WAH_EFFECT", message}
  162. {
  163. }
  164. }; // EaxAutoWahEffectException
  165. EaxAutoWahEffect::EaxAutoWahEffect()
  166. : EaxEffect{AL_EFFECT_AUTOWAH}
  167. {
  168. set_eax_defaults();
  169. set_efx_defaults();
  170. }
  171. void EaxAutoWahEffect::dispatch(const EaxEaxCall& eax_call)
  172. {
  173. eax_call.is_get() ? get(eax_call) : set(eax_call);
  174. }
  175. void EaxAutoWahEffect::set_eax_defaults()
  176. {
  177. eax_.flAttackTime = EAXAUTOWAH_DEFAULTATTACKTIME;
  178. eax_.flReleaseTime = EAXAUTOWAH_DEFAULTRELEASETIME;
  179. eax_.lResonance = EAXAUTOWAH_DEFAULTRESONANCE;
  180. eax_.lPeakLevel = EAXAUTOWAH_DEFAULTPEAKLEVEL;
  181. eax_d_ = eax_;
  182. }
  183. void EaxAutoWahEffect::set_efx_attack_time()
  184. {
  185. const auto attack_time = clamp(
  186. eax_.flAttackTime,
  187. AL_AUTOWAH_MIN_ATTACK_TIME,
  188. AL_AUTOWAH_MAX_ATTACK_TIME);
  189. al_effect_props_.Autowah.AttackTime = attack_time;
  190. }
  191. void EaxAutoWahEffect::set_efx_release_time()
  192. {
  193. const auto release_time = clamp(
  194. eax_.flReleaseTime,
  195. AL_AUTOWAH_MIN_RELEASE_TIME,
  196. AL_AUTOWAH_MAX_RELEASE_TIME);
  197. al_effect_props_.Autowah.ReleaseTime = release_time;
  198. }
  199. void EaxAutoWahEffect::set_efx_resonance()
  200. {
  201. const auto resonance = clamp(
  202. level_mb_to_gain(static_cast<float>(eax_.lResonance)),
  203. AL_AUTOWAH_MIN_RESONANCE,
  204. AL_AUTOWAH_MAX_RESONANCE);
  205. al_effect_props_.Autowah.Resonance = resonance;
  206. }
  207. void EaxAutoWahEffect::set_efx_peak_gain()
  208. {
  209. const auto peak_gain = clamp(
  210. level_mb_to_gain(static_cast<float>(eax_.lPeakLevel)),
  211. AL_AUTOWAH_MIN_PEAK_GAIN,
  212. AL_AUTOWAH_MAX_PEAK_GAIN);
  213. al_effect_props_.Autowah.PeakGain = peak_gain;
  214. }
  215. void EaxAutoWahEffect::set_efx_defaults()
  216. {
  217. set_efx_attack_time();
  218. set_efx_release_time();
  219. set_efx_resonance();
  220. set_efx_peak_gain();
  221. }
  222. void EaxAutoWahEffect::get(const EaxEaxCall& eax_call)
  223. {
  224. switch (eax_call.get_property_id())
  225. {
  226. case EAXAUTOWAH_NONE:
  227. break;
  228. case EAXAUTOWAH_ALLPARAMETERS:
  229. eax_call.set_value<EaxAutoWahEffectException>(eax_);
  230. break;
  231. case EAXAUTOWAH_ATTACKTIME:
  232. eax_call.set_value<EaxAutoWahEffectException>(eax_.flAttackTime);
  233. break;
  234. case EAXAUTOWAH_RELEASETIME:
  235. eax_call.set_value<EaxAutoWahEffectException>(eax_.flReleaseTime);
  236. break;
  237. case EAXAUTOWAH_RESONANCE:
  238. eax_call.set_value<EaxAutoWahEffectException>(eax_.lResonance);
  239. break;
  240. case EAXAUTOWAH_PEAKLEVEL:
  241. eax_call.set_value<EaxAutoWahEffectException>(eax_.lPeakLevel);
  242. break;
  243. default:
  244. throw EaxAutoWahEffectException{"Unsupported property id."};
  245. }
  246. }
  247. void EaxAutoWahEffect::validate_attack_time(
  248. float flAttackTime)
  249. {
  250. eax_validate_range<EaxAutoWahEffectException>(
  251. "Attack Time",
  252. flAttackTime,
  253. EAXAUTOWAH_MINATTACKTIME,
  254. EAXAUTOWAH_MAXATTACKTIME);
  255. }
  256. void EaxAutoWahEffect::validate_release_time(
  257. float flReleaseTime)
  258. {
  259. eax_validate_range<EaxAutoWahEffectException>(
  260. "Release Time",
  261. flReleaseTime,
  262. EAXAUTOWAH_MINRELEASETIME,
  263. EAXAUTOWAH_MAXRELEASETIME);
  264. }
  265. void EaxAutoWahEffect::validate_resonance(
  266. long lResonance)
  267. {
  268. eax_validate_range<EaxAutoWahEffectException>(
  269. "Resonance",
  270. lResonance,
  271. EAXAUTOWAH_MINRESONANCE,
  272. EAXAUTOWAH_MAXRESONANCE);
  273. }
  274. void EaxAutoWahEffect::validate_peak_level(
  275. long lPeakLevel)
  276. {
  277. eax_validate_range<EaxAutoWahEffectException>(
  278. "Peak Level",
  279. lPeakLevel,
  280. EAXAUTOWAH_MINPEAKLEVEL,
  281. EAXAUTOWAH_MAXPEAKLEVEL);
  282. }
  283. void EaxAutoWahEffect::validate_all(
  284. const EAXAUTOWAHPROPERTIES& eax_all)
  285. {
  286. validate_attack_time(eax_all.flAttackTime);
  287. validate_release_time(eax_all.flReleaseTime);
  288. validate_resonance(eax_all.lResonance);
  289. validate_peak_level(eax_all.lPeakLevel);
  290. }
  291. void EaxAutoWahEffect::defer_attack_time(
  292. float flAttackTime)
  293. {
  294. eax_d_.flAttackTime = flAttackTime;
  295. eax_dirty_flags_.flAttackTime = (eax_.flAttackTime != eax_d_.flAttackTime);
  296. }
  297. void EaxAutoWahEffect::defer_release_time(
  298. float flReleaseTime)
  299. {
  300. eax_d_.flReleaseTime = flReleaseTime;
  301. eax_dirty_flags_.flReleaseTime = (eax_.flReleaseTime != eax_d_.flReleaseTime);
  302. }
  303. void EaxAutoWahEffect::defer_resonance(
  304. long lResonance)
  305. {
  306. eax_d_.lResonance = lResonance;
  307. eax_dirty_flags_.lResonance = (eax_.lResonance != eax_d_.lResonance);
  308. }
  309. void EaxAutoWahEffect::defer_peak_level(
  310. long lPeakLevel)
  311. {
  312. eax_d_.lPeakLevel = lPeakLevel;
  313. eax_dirty_flags_.lPeakLevel = (eax_.lPeakLevel != eax_d_.lPeakLevel);
  314. }
  315. void EaxAutoWahEffect::defer_all(
  316. const EAXAUTOWAHPROPERTIES& eax_all)
  317. {
  318. validate_all(eax_all);
  319. defer_attack_time(eax_all.flAttackTime);
  320. defer_release_time(eax_all.flReleaseTime);
  321. defer_resonance(eax_all.lResonance);
  322. defer_peak_level(eax_all.lPeakLevel);
  323. }
  324. void EaxAutoWahEffect::defer_attack_time(
  325. const EaxEaxCall& eax_call)
  326. {
  327. const auto& attack_time =
  328. eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flAttackTime)>();
  329. validate_attack_time(attack_time);
  330. defer_attack_time(attack_time);
  331. }
  332. void EaxAutoWahEffect::defer_release_time(
  333. const EaxEaxCall& eax_call)
  334. {
  335. const auto& release_time =
  336. eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::flReleaseTime)>();
  337. validate_release_time(release_time);
  338. defer_release_time(release_time);
  339. }
  340. void EaxAutoWahEffect::defer_resonance(
  341. const EaxEaxCall& eax_call)
  342. {
  343. const auto& resonance =
  344. eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lResonance)>();
  345. validate_resonance(resonance);
  346. defer_resonance(resonance);
  347. }
  348. void EaxAutoWahEffect::defer_peak_level(
  349. const EaxEaxCall& eax_call)
  350. {
  351. const auto& peak_level =
  352. eax_call.get_value<EaxAutoWahEffectException, const decltype(EAXAUTOWAHPROPERTIES::lPeakLevel)>();
  353. validate_peak_level(peak_level);
  354. defer_peak_level(peak_level);
  355. }
  356. void EaxAutoWahEffect::defer_all(
  357. const EaxEaxCall& eax_call)
  358. {
  359. const auto& all =
  360. eax_call.get_value<EaxAutoWahEffectException, const EAXAUTOWAHPROPERTIES>();
  361. validate_all(all);
  362. defer_all(all);
  363. }
  364. // [[nodiscard]]
  365. bool EaxAutoWahEffect::apply_deferred()
  366. {
  367. if (eax_dirty_flags_ == EaxAutoWahEffectDirtyFlags{})
  368. {
  369. return false;
  370. }
  371. eax_ = eax_d_;
  372. if (eax_dirty_flags_.flAttackTime)
  373. {
  374. set_efx_attack_time();
  375. }
  376. if (eax_dirty_flags_.flReleaseTime)
  377. {
  378. set_efx_release_time();
  379. }
  380. if (eax_dirty_flags_.lResonance)
  381. {
  382. set_efx_resonance();
  383. }
  384. if (eax_dirty_flags_.lPeakLevel)
  385. {
  386. set_efx_peak_gain();
  387. }
  388. eax_dirty_flags_ = EaxAutoWahEffectDirtyFlags{};
  389. return true;
  390. }
  391. void EaxAutoWahEffect::set(const EaxEaxCall& eax_call)
  392. {
  393. switch (eax_call.get_property_id())
  394. {
  395. case EAXAUTOWAH_NONE:
  396. break;
  397. case EAXAUTOWAH_ALLPARAMETERS:
  398. defer_all(eax_call);
  399. break;
  400. case EAXAUTOWAH_ATTACKTIME:
  401. defer_attack_time(eax_call);
  402. break;
  403. case EAXAUTOWAH_RELEASETIME:
  404. defer_release_time(eax_call);
  405. break;
  406. case EAXAUTOWAH_RESONANCE:
  407. defer_resonance(eax_call);
  408. break;
  409. case EAXAUTOWAH_PEAKLEVEL:
  410. defer_peak_level(eax_call);
  411. break;
  412. default:
  413. throw EaxAutoWahEffectException{"Unsupported property id."};
  414. }
  415. }
  416. } // namespace
  417. EaxEffectUPtr eax_create_eax_auto_wah_effect()
  418. {
  419. return std::make_unique<::EaxAutoWahEffect>();
  420. }
  421. #endif // ALSOFT_EAX