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

569 lines
14 KiB

  1. #include "config.h"
  2. #include "AL/al.h"
  3. #include "AL/efx.h"
  4. #include "alc/effects/base.h"
  5. #include "effects.h"
  6. #ifdef ALSOFT_EAX
  7. #include "alnumeric.h"
  8. #include "al/eax_exception.h"
  9. #include "al/eax_utils.h"
  10. #endif // ALSOFT_EAX
  11. namespace {
  12. static_assert(EchoMaxDelay >= AL_ECHO_MAX_DELAY, "Echo max delay too short");
  13. static_assert(EchoMaxLRDelay >= AL_ECHO_MAX_LRDELAY, "Echo max left-right delay too short");
  14. void Echo_setParami(EffectProps*, ALenum param, int)
  15. { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
  16. void Echo_setParamiv(EffectProps*, ALenum param, const int*)
  17. { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
  18. void Echo_setParamf(EffectProps *props, ALenum param, float val)
  19. {
  20. switch(param)
  21. {
  22. case AL_ECHO_DELAY:
  23. if(!(val >= AL_ECHO_MIN_DELAY && val <= AL_ECHO_MAX_DELAY))
  24. throw effect_exception{AL_INVALID_VALUE, "Echo delay out of range"};
  25. props->Echo.Delay = val;
  26. break;
  27. case AL_ECHO_LRDELAY:
  28. if(!(val >= AL_ECHO_MIN_LRDELAY && val <= AL_ECHO_MAX_LRDELAY))
  29. throw effect_exception{AL_INVALID_VALUE, "Echo LR delay out of range"};
  30. props->Echo.LRDelay = val;
  31. break;
  32. case AL_ECHO_DAMPING:
  33. if(!(val >= AL_ECHO_MIN_DAMPING && val <= AL_ECHO_MAX_DAMPING))
  34. throw effect_exception{AL_INVALID_VALUE, "Echo damping out of range"};
  35. props->Echo.Damping = val;
  36. break;
  37. case AL_ECHO_FEEDBACK:
  38. if(!(val >= AL_ECHO_MIN_FEEDBACK && val <= AL_ECHO_MAX_FEEDBACK))
  39. throw effect_exception{AL_INVALID_VALUE, "Echo feedback out of range"};
  40. props->Echo.Feedback = val;
  41. break;
  42. case AL_ECHO_SPREAD:
  43. if(!(val >= AL_ECHO_MIN_SPREAD && val <= AL_ECHO_MAX_SPREAD))
  44. throw effect_exception{AL_INVALID_VALUE, "Echo spread out of range"};
  45. props->Echo.Spread = val;
  46. break;
  47. default:
  48. throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
  49. }
  50. }
  51. void Echo_setParamfv(EffectProps *props, ALenum param, const float *vals)
  52. { Echo_setParamf(props, param, vals[0]); }
  53. void Echo_getParami(const EffectProps*, ALenum param, int*)
  54. { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer property 0x%04x", param}; }
  55. void Echo_getParamiv(const EffectProps*, ALenum param, int*)
  56. { throw effect_exception{AL_INVALID_ENUM, "Invalid echo integer-vector property 0x%04x", param}; }
  57. void Echo_getParamf(const EffectProps *props, ALenum param, float *val)
  58. {
  59. switch(param)
  60. {
  61. case AL_ECHO_DELAY:
  62. *val = props->Echo.Delay;
  63. break;
  64. case AL_ECHO_LRDELAY:
  65. *val = props->Echo.LRDelay;
  66. break;
  67. case AL_ECHO_DAMPING:
  68. *val = props->Echo.Damping;
  69. break;
  70. case AL_ECHO_FEEDBACK:
  71. *val = props->Echo.Feedback;
  72. break;
  73. case AL_ECHO_SPREAD:
  74. *val = props->Echo.Spread;
  75. break;
  76. default:
  77. throw effect_exception{AL_INVALID_ENUM, "Invalid echo float property 0x%04x", param};
  78. }
  79. }
  80. void Echo_getParamfv(const EffectProps *props, ALenum param, float *vals)
  81. { Echo_getParamf(props, param, vals); }
  82. EffectProps genDefaultProps() noexcept
  83. {
  84. EffectProps props{};
  85. props.Echo.Delay = AL_ECHO_DEFAULT_DELAY;
  86. props.Echo.LRDelay = AL_ECHO_DEFAULT_LRDELAY;
  87. props.Echo.Damping = AL_ECHO_DEFAULT_DAMPING;
  88. props.Echo.Feedback = AL_ECHO_DEFAULT_FEEDBACK;
  89. props.Echo.Spread = AL_ECHO_DEFAULT_SPREAD;
  90. return props;
  91. }
  92. } // namespace
  93. DEFINE_ALEFFECT_VTABLE(Echo);
  94. const EffectProps EchoEffectProps{genDefaultProps()};
  95. #ifdef ALSOFT_EAX
  96. namespace {
  97. using EaxEchoEffectDirtyFlagsValue = std::uint_least8_t;
  98. struct EaxEchoEffectDirtyFlags
  99. {
  100. using EaxIsBitFieldStruct = bool;
  101. EaxEchoEffectDirtyFlagsValue flDelay : 1;
  102. EaxEchoEffectDirtyFlagsValue flLRDelay : 1;
  103. EaxEchoEffectDirtyFlagsValue flDamping : 1;
  104. EaxEchoEffectDirtyFlagsValue flFeedback : 1;
  105. EaxEchoEffectDirtyFlagsValue flSpread : 1;
  106. }; // EaxEchoEffectDirtyFlags
  107. class EaxEchoEffect final :
  108. public EaxEffect
  109. {
  110. public:
  111. EaxEchoEffect();
  112. void dispatch(const EaxEaxCall& eax_call) override;
  113. // [[nodiscard]]
  114. bool apply_deferred() override;
  115. private:
  116. EAXECHOPROPERTIES eax_{};
  117. EAXECHOPROPERTIES eax_d_{};
  118. EaxEchoEffectDirtyFlags eax_dirty_flags_{};
  119. void set_eax_defaults();
  120. void set_efx_delay();
  121. void set_efx_lr_delay();
  122. void set_efx_damping();
  123. void set_efx_feedback();
  124. void set_efx_spread();
  125. void set_efx_defaults();
  126. void get(const EaxEaxCall& eax_call);
  127. void validate_delay(float flDelay);
  128. void validate_lr_delay(float flLRDelay);
  129. void validate_damping(float flDamping);
  130. void validate_feedback(float flFeedback);
  131. void validate_spread(float flSpread);
  132. void validate_all(const EAXECHOPROPERTIES& all);
  133. void defer_delay(float flDelay);
  134. void defer_lr_delay(float flLRDelay);
  135. void defer_damping(float flDamping);
  136. void defer_feedback(float flFeedback);
  137. void defer_spread(float flSpread);
  138. void defer_all(const EAXECHOPROPERTIES& all);
  139. void defer_delay(const EaxEaxCall& eax_call);
  140. void defer_lr_delay(const EaxEaxCall& eax_call);
  141. void defer_damping(const EaxEaxCall& eax_call);
  142. void defer_feedback(const EaxEaxCall& eax_call);
  143. void defer_spread(const EaxEaxCall& eax_call);
  144. void defer_all(const EaxEaxCall& eax_call);
  145. void set(const EaxEaxCall& eax_call);
  146. }; // EaxEchoEffect
  147. class EaxEchoEffectException :
  148. public EaxException
  149. {
  150. public:
  151. explicit EaxEchoEffectException(
  152. const char* message)
  153. :
  154. EaxException{"EAX_ECHO_EFFECT", message}
  155. {
  156. }
  157. }; // EaxEchoEffectException
  158. EaxEchoEffect::EaxEchoEffect()
  159. : EaxEffect{AL_EFFECT_ECHO}
  160. {
  161. set_eax_defaults();
  162. set_efx_defaults();
  163. }
  164. void EaxEchoEffect::dispatch(
  165. const EaxEaxCall& eax_call)
  166. {
  167. eax_call.is_get() ? get(eax_call) : set(eax_call);
  168. }
  169. void EaxEchoEffect::set_eax_defaults()
  170. {
  171. eax_.flDelay = EAXECHO_DEFAULTDELAY;
  172. eax_.flLRDelay = EAXECHO_DEFAULTLRDELAY;
  173. eax_.flDamping = EAXECHO_DEFAULTDAMPING;
  174. eax_.flFeedback = EAXECHO_DEFAULTFEEDBACK;
  175. eax_.flSpread = EAXECHO_DEFAULTSPREAD;
  176. eax_d_ = eax_;
  177. }
  178. void EaxEchoEffect::set_efx_delay()
  179. {
  180. const auto delay = clamp(
  181. eax_.flDelay,
  182. AL_ECHO_MIN_DELAY,
  183. AL_ECHO_MAX_DELAY);
  184. al_effect_props_.Echo.Delay = delay;
  185. }
  186. void EaxEchoEffect::set_efx_lr_delay()
  187. {
  188. const auto lr_delay = clamp(
  189. eax_.flLRDelay,
  190. AL_ECHO_MIN_LRDELAY,
  191. AL_ECHO_MAX_LRDELAY);
  192. al_effect_props_.Echo.LRDelay = lr_delay;
  193. }
  194. void EaxEchoEffect::set_efx_damping()
  195. {
  196. const auto damping = clamp(
  197. eax_.flDamping,
  198. AL_ECHO_MIN_DAMPING,
  199. AL_ECHO_MAX_DAMPING);
  200. al_effect_props_.Echo.Damping = damping;
  201. }
  202. void EaxEchoEffect::set_efx_feedback()
  203. {
  204. const auto feedback = clamp(
  205. eax_.flFeedback,
  206. AL_ECHO_MIN_FEEDBACK,
  207. AL_ECHO_MAX_FEEDBACK);
  208. al_effect_props_.Echo.Feedback = feedback;
  209. }
  210. void EaxEchoEffect::set_efx_spread()
  211. {
  212. const auto spread = clamp(
  213. eax_.flSpread,
  214. AL_ECHO_MIN_SPREAD,
  215. AL_ECHO_MAX_SPREAD);
  216. al_effect_props_.Echo.Spread = spread;
  217. }
  218. void EaxEchoEffect::set_efx_defaults()
  219. {
  220. set_efx_delay();
  221. set_efx_lr_delay();
  222. set_efx_damping();
  223. set_efx_feedback();
  224. set_efx_spread();
  225. }
  226. void EaxEchoEffect::get(const EaxEaxCall& eax_call)
  227. {
  228. switch(eax_call.get_property_id())
  229. {
  230. case EAXECHO_NONE:
  231. break;
  232. case EAXECHO_ALLPARAMETERS:
  233. eax_call.set_value<EaxEchoEffectException>(eax_);
  234. break;
  235. case EAXECHO_DELAY:
  236. eax_call.set_value<EaxEchoEffectException>(eax_.flDelay);
  237. break;
  238. case EAXECHO_LRDELAY:
  239. eax_call.set_value<EaxEchoEffectException>(eax_.flLRDelay);
  240. break;
  241. case EAXECHO_DAMPING:
  242. eax_call.set_value<EaxEchoEffectException>(eax_.flDamping);
  243. break;
  244. case EAXECHO_FEEDBACK:
  245. eax_call.set_value<EaxEchoEffectException>(eax_.flFeedback);
  246. break;
  247. case EAXECHO_SPREAD:
  248. eax_call.set_value<EaxEchoEffectException>(eax_.flSpread);
  249. break;
  250. default:
  251. throw EaxEchoEffectException{"Unsupported property id."};
  252. }
  253. }
  254. void EaxEchoEffect::validate_delay(
  255. float flDelay)
  256. {
  257. eax_validate_range<EaxEchoEffectException>(
  258. "Delay",
  259. flDelay,
  260. EAXECHO_MINDELAY,
  261. EAXECHO_MAXDELAY);
  262. }
  263. void EaxEchoEffect::validate_lr_delay(
  264. float flLRDelay)
  265. {
  266. eax_validate_range<EaxEchoEffectException>(
  267. "LR Delay",
  268. flLRDelay,
  269. EAXECHO_MINLRDELAY,
  270. EAXECHO_MAXLRDELAY);
  271. }
  272. void EaxEchoEffect::validate_damping(
  273. float flDamping)
  274. {
  275. eax_validate_range<EaxEchoEffectException>(
  276. "Damping",
  277. flDamping,
  278. EAXECHO_MINDAMPING,
  279. EAXECHO_MAXDAMPING);
  280. }
  281. void EaxEchoEffect::validate_feedback(
  282. float flFeedback)
  283. {
  284. eax_validate_range<EaxEchoEffectException>(
  285. "Feedback",
  286. flFeedback,
  287. EAXECHO_MINFEEDBACK,
  288. EAXECHO_MAXFEEDBACK);
  289. }
  290. void EaxEchoEffect::validate_spread(
  291. float flSpread)
  292. {
  293. eax_validate_range<EaxEchoEffectException>(
  294. "Spread",
  295. flSpread,
  296. EAXECHO_MINSPREAD,
  297. EAXECHO_MAXSPREAD);
  298. }
  299. void EaxEchoEffect::validate_all(
  300. const EAXECHOPROPERTIES& all)
  301. {
  302. validate_delay(all.flDelay);
  303. validate_lr_delay(all.flLRDelay);
  304. validate_damping(all.flDamping);
  305. validate_feedback(all.flFeedback);
  306. validate_spread(all.flSpread);
  307. }
  308. void EaxEchoEffect::defer_delay(
  309. float flDelay)
  310. {
  311. eax_d_.flDelay = flDelay;
  312. eax_dirty_flags_.flDelay = (eax_.flDelay != eax_d_.flDelay);
  313. }
  314. void EaxEchoEffect::defer_lr_delay(
  315. float flLRDelay)
  316. {
  317. eax_d_.flLRDelay = flLRDelay;
  318. eax_dirty_flags_.flLRDelay = (eax_.flLRDelay != eax_d_.flLRDelay);
  319. }
  320. void EaxEchoEffect::defer_damping(
  321. float flDamping)
  322. {
  323. eax_d_.flDamping = flDamping;
  324. eax_dirty_flags_.flDamping = (eax_.flDamping != eax_d_.flDamping);
  325. }
  326. void EaxEchoEffect::defer_feedback(
  327. float flFeedback)
  328. {
  329. eax_d_.flFeedback = flFeedback;
  330. eax_dirty_flags_.flFeedback = (eax_.flFeedback != eax_d_.flFeedback);
  331. }
  332. void EaxEchoEffect::defer_spread(
  333. float flSpread)
  334. {
  335. eax_d_.flSpread = flSpread;
  336. eax_dirty_flags_.flSpread = (eax_.flSpread != eax_d_.flSpread);
  337. }
  338. void EaxEchoEffect::defer_all(
  339. const EAXECHOPROPERTIES& all)
  340. {
  341. defer_delay(all.flDelay);
  342. defer_lr_delay(all.flLRDelay);
  343. defer_damping(all.flDamping);
  344. defer_feedback(all.flFeedback);
  345. defer_spread(all.flSpread);
  346. }
  347. void EaxEchoEffect::defer_delay(
  348. const EaxEaxCall& eax_call)
  349. {
  350. const auto& delay =
  351. eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDelay)>();
  352. validate_delay(delay);
  353. defer_delay(delay);
  354. }
  355. void EaxEchoEffect::defer_lr_delay(
  356. const EaxEaxCall& eax_call)
  357. {
  358. const auto& lr_delay =
  359. eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flLRDelay)>();
  360. validate_lr_delay(lr_delay);
  361. defer_lr_delay(lr_delay);
  362. }
  363. void EaxEchoEffect::defer_damping(
  364. const EaxEaxCall& eax_call)
  365. {
  366. const auto& damping =
  367. eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flDamping)>();
  368. validate_damping(damping);
  369. defer_damping(damping);
  370. }
  371. void EaxEchoEffect::defer_feedback(
  372. const EaxEaxCall& eax_call)
  373. {
  374. const auto& feedback =
  375. eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flFeedback)>();
  376. validate_feedback(feedback);
  377. defer_feedback(feedback);
  378. }
  379. void EaxEchoEffect::defer_spread(
  380. const EaxEaxCall& eax_call)
  381. {
  382. const auto& spread =
  383. eax_call.get_value<EaxEchoEffectException, const decltype(EAXECHOPROPERTIES::flSpread)>();
  384. validate_spread(spread);
  385. defer_spread(spread);
  386. }
  387. void EaxEchoEffect::defer_all(
  388. const EaxEaxCall& eax_call)
  389. {
  390. const auto& all =
  391. eax_call.get_value<EaxEchoEffectException, const EAXECHOPROPERTIES>();
  392. validate_all(all);
  393. defer_all(all);
  394. }
  395. // [[nodiscard]]
  396. bool EaxEchoEffect::apply_deferred()
  397. {
  398. if (eax_dirty_flags_ == EaxEchoEffectDirtyFlags{})
  399. {
  400. return false;
  401. }
  402. eax_ = eax_d_;
  403. if (eax_dirty_flags_.flDelay)
  404. {
  405. set_efx_delay();
  406. }
  407. if (eax_dirty_flags_.flLRDelay)
  408. {
  409. set_efx_lr_delay();
  410. }
  411. if (eax_dirty_flags_.flDamping)
  412. {
  413. set_efx_damping();
  414. }
  415. if (eax_dirty_flags_.flFeedback)
  416. {
  417. set_efx_feedback();
  418. }
  419. if (eax_dirty_flags_.flSpread)
  420. {
  421. set_efx_spread();
  422. }
  423. eax_dirty_flags_ = EaxEchoEffectDirtyFlags{};
  424. return true;
  425. }
  426. void EaxEchoEffect::set(const EaxEaxCall& eax_call)
  427. {
  428. switch(eax_call.get_property_id())
  429. {
  430. case EAXECHO_NONE:
  431. break;
  432. case EAXECHO_ALLPARAMETERS:
  433. defer_all(eax_call);
  434. break;
  435. case EAXECHO_DELAY:
  436. defer_delay(eax_call);
  437. break;
  438. case EAXECHO_LRDELAY:
  439. defer_lr_delay(eax_call);
  440. break;
  441. case EAXECHO_DAMPING:
  442. defer_damping(eax_call);
  443. break;
  444. case EAXECHO_FEEDBACK:
  445. defer_feedback(eax_call);
  446. break;
  447. case EAXECHO_SPREAD:
  448. defer_spread(eax_call);
  449. break;
  450. default:
  451. throw EaxEchoEffectException{"Unsupported property id."};
  452. }
  453. }
  454. } // namespace
  455. EaxEffectUPtr eax_create_eax_echo_effect()
  456. {
  457. return std::make_unique<EaxEchoEffect>();
  458. }
  459. #endif // ALSOFT_EAX