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

571 lines
15 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. void Distortion_setParami(EffectProps*, ALenum param, int)
  13. { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
  14. void Distortion_setParamiv(EffectProps*, ALenum param, const int*)
  15. {
  16. throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
  17. param};
  18. }
  19. void Distortion_setParamf(EffectProps *props, ALenum param, float val)
  20. {
  21. switch(param)
  22. {
  23. case AL_DISTORTION_EDGE:
  24. if(!(val >= AL_DISTORTION_MIN_EDGE && val <= AL_DISTORTION_MAX_EDGE))
  25. throw effect_exception{AL_INVALID_VALUE, "Distortion edge out of range"};
  26. props->Distortion.Edge = val;
  27. break;
  28. case AL_DISTORTION_GAIN:
  29. if(!(val >= AL_DISTORTION_MIN_GAIN && val <= AL_DISTORTION_MAX_GAIN))
  30. throw effect_exception{AL_INVALID_VALUE, "Distortion gain out of range"};
  31. props->Distortion.Gain = val;
  32. break;
  33. case AL_DISTORTION_LOWPASS_CUTOFF:
  34. if(!(val >= AL_DISTORTION_MIN_LOWPASS_CUTOFF && val <= AL_DISTORTION_MAX_LOWPASS_CUTOFF))
  35. throw effect_exception{AL_INVALID_VALUE, "Distortion low-pass cutoff out of range"};
  36. props->Distortion.LowpassCutoff = val;
  37. break;
  38. case AL_DISTORTION_EQCENTER:
  39. if(!(val >= AL_DISTORTION_MIN_EQCENTER && val <= AL_DISTORTION_MAX_EQCENTER))
  40. throw effect_exception{AL_INVALID_VALUE, "Distortion EQ center out of range"};
  41. props->Distortion.EQCenter = val;
  42. break;
  43. case AL_DISTORTION_EQBANDWIDTH:
  44. if(!(val >= AL_DISTORTION_MIN_EQBANDWIDTH && val <= AL_DISTORTION_MAX_EQBANDWIDTH))
  45. throw effect_exception{AL_INVALID_VALUE, "Distortion EQ bandwidth out of range"};
  46. props->Distortion.EQBandwidth = val;
  47. break;
  48. default:
  49. throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
  50. }
  51. }
  52. void Distortion_setParamfv(EffectProps *props, ALenum param, const float *vals)
  53. { Distortion_setParamf(props, param, vals[0]); }
  54. void Distortion_getParami(const EffectProps*, ALenum param, int*)
  55. { throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer property 0x%04x", param}; }
  56. void Distortion_getParamiv(const EffectProps*, ALenum param, int*)
  57. {
  58. throw effect_exception{AL_INVALID_ENUM, "Invalid distortion integer-vector property 0x%04x",
  59. param};
  60. }
  61. void Distortion_getParamf(const EffectProps *props, ALenum param, float *val)
  62. {
  63. switch(param)
  64. {
  65. case AL_DISTORTION_EDGE:
  66. *val = props->Distortion.Edge;
  67. break;
  68. case AL_DISTORTION_GAIN:
  69. *val = props->Distortion.Gain;
  70. break;
  71. case AL_DISTORTION_LOWPASS_CUTOFF:
  72. *val = props->Distortion.LowpassCutoff;
  73. break;
  74. case AL_DISTORTION_EQCENTER:
  75. *val = props->Distortion.EQCenter;
  76. break;
  77. case AL_DISTORTION_EQBANDWIDTH:
  78. *val = props->Distortion.EQBandwidth;
  79. break;
  80. default:
  81. throw effect_exception{AL_INVALID_ENUM, "Invalid distortion float property 0x%04x", param};
  82. }
  83. }
  84. void Distortion_getParamfv(const EffectProps *props, ALenum param, float *vals)
  85. { Distortion_getParamf(props, param, vals); }
  86. EffectProps genDefaultProps() noexcept
  87. {
  88. EffectProps props{};
  89. props.Distortion.Edge = AL_DISTORTION_DEFAULT_EDGE;
  90. props.Distortion.Gain = AL_DISTORTION_DEFAULT_GAIN;
  91. props.Distortion.LowpassCutoff = AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF;
  92. props.Distortion.EQCenter = AL_DISTORTION_DEFAULT_EQCENTER;
  93. props.Distortion.EQBandwidth = AL_DISTORTION_DEFAULT_EQBANDWIDTH;
  94. return props;
  95. }
  96. } // namespace
  97. DEFINE_ALEFFECT_VTABLE(Distortion);
  98. const EffectProps DistortionEffectProps{genDefaultProps()};
  99. #ifdef ALSOFT_EAX
  100. namespace {
  101. using EaxDistortionEffectDirtyFlagsValue = std::uint_least8_t;
  102. struct EaxDistortionEffectDirtyFlags
  103. {
  104. using EaxIsBitFieldStruct = bool;
  105. EaxDistortionEffectDirtyFlagsValue flEdge : 1;
  106. EaxDistortionEffectDirtyFlagsValue lGain : 1;
  107. EaxDistortionEffectDirtyFlagsValue flLowPassCutOff : 1;
  108. EaxDistortionEffectDirtyFlagsValue flEQCenter : 1;
  109. EaxDistortionEffectDirtyFlagsValue flEQBandwidth : 1;
  110. }; // EaxDistortionEffectDirtyFlags
  111. class EaxDistortionEffect final :
  112. public EaxEffect
  113. {
  114. public:
  115. EaxDistortionEffect();
  116. void dispatch(const EaxEaxCall& eax_call) override;
  117. // [[nodiscard]]
  118. bool apply_deferred() override;
  119. private:
  120. EAXDISTORTIONPROPERTIES eax_{};
  121. EAXDISTORTIONPROPERTIES eax_d_{};
  122. EaxDistortionEffectDirtyFlags eax_dirty_flags_{};
  123. void set_eax_defaults();
  124. void set_efx_edge();
  125. void set_efx_gain();
  126. void set_efx_lowpass_cutoff();
  127. void set_efx_eq_center();
  128. void set_efx_eq_bandwidth();
  129. void set_efx_defaults();
  130. void get(const EaxEaxCall& eax_call);
  131. void validate_edge(float flEdge);
  132. void validate_gain(long lGain);
  133. void validate_lowpass_cutoff(float flLowPassCutOff);
  134. void validate_eq_center(float flEQCenter);
  135. void validate_eq_bandwidth(float flEQBandwidth);
  136. void validate_all(const EAXDISTORTIONPROPERTIES& eax_all);
  137. void defer_edge(float flEdge);
  138. void defer_gain(long lGain);
  139. void defer_low_pass_cutoff(float flLowPassCutOff);
  140. void defer_eq_center(float flEQCenter);
  141. void defer_eq_bandwidth(float flEQBandwidth);
  142. void defer_all(const EAXDISTORTIONPROPERTIES& eax_all);
  143. void defer_edge(const EaxEaxCall& eax_call);
  144. void defer_gain(const EaxEaxCall& eax_call);
  145. void defer_low_pass_cutoff(const EaxEaxCall& eax_call);
  146. void defer_eq_center(const EaxEaxCall& eax_call);
  147. void defer_eq_bandwidth(const EaxEaxCall& eax_call);
  148. void defer_all(const EaxEaxCall& eax_call);
  149. void set(const EaxEaxCall& eax_call);
  150. }; // EaxDistortionEffect
  151. class EaxDistortionEffectException :
  152. public EaxException
  153. {
  154. public:
  155. explicit EaxDistortionEffectException(
  156. const char* message)
  157. :
  158. EaxException{"EAX_DISTORTION_EFFECT", message}
  159. {
  160. }
  161. }; // EaxDistortionEffectException
  162. EaxDistortionEffect::EaxDistortionEffect()
  163. : EaxEffect{AL_EFFECT_DISTORTION}
  164. {
  165. set_eax_defaults();
  166. set_efx_defaults();
  167. }
  168. void EaxDistortionEffect::dispatch(const EaxEaxCall& eax_call)
  169. {
  170. eax_call.is_get() ? get(eax_call) : set(eax_call);
  171. }
  172. void EaxDistortionEffect::set_eax_defaults()
  173. {
  174. eax_.flEdge = EAXDISTORTION_DEFAULTEDGE;
  175. eax_.lGain = EAXDISTORTION_DEFAULTGAIN;
  176. eax_.flLowPassCutOff = EAXDISTORTION_DEFAULTLOWPASSCUTOFF;
  177. eax_.flEQCenter = EAXDISTORTION_DEFAULTEQCENTER;
  178. eax_.flEQBandwidth = EAXDISTORTION_DEFAULTEQBANDWIDTH;
  179. eax_d_ = eax_;
  180. }
  181. void EaxDistortionEffect::set_efx_edge()
  182. {
  183. const auto edge = clamp(
  184. eax_.flEdge,
  185. AL_DISTORTION_MIN_EDGE,
  186. AL_DISTORTION_MAX_EDGE);
  187. al_effect_props_.Distortion.Edge = edge;
  188. }
  189. void EaxDistortionEffect::set_efx_gain()
  190. {
  191. const auto gain = clamp(
  192. level_mb_to_gain(static_cast<float>(eax_.lGain)),
  193. AL_DISTORTION_MIN_GAIN,
  194. AL_DISTORTION_MAX_GAIN);
  195. al_effect_props_.Distortion.Gain = gain;
  196. }
  197. void EaxDistortionEffect::set_efx_lowpass_cutoff()
  198. {
  199. const auto lowpass_cutoff = clamp(
  200. eax_.flLowPassCutOff,
  201. AL_DISTORTION_MIN_LOWPASS_CUTOFF,
  202. AL_DISTORTION_MAX_LOWPASS_CUTOFF);
  203. al_effect_props_.Distortion.LowpassCutoff = lowpass_cutoff;
  204. }
  205. void EaxDistortionEffect::set_efx_eq_center()
  206. {
  207. const auto eq_center = clamp(
  208. eax_.flEQCenter,
  209. AL_DISTORTION_MIN_EQCENTER,
  210. AL_DISTORTION_MAX_EQCENTER);
  211. al_effect_props_.Distortion.EQCenter = eq_center;
  212. }
  213. void EaxDistortionEffect::set_efx_eq_bandwidth()
  214. {
  215. const auto eq_bandwidth = clamp(
  216. eax_.flEdge,
  217. AL_DISTORTION_MIN_EQBANDWIDTH,
  218. AL_DISTORTION_MAX_EQBANDWIDTH);
  219. al_effect_props_.Distortion.EQBandwidth = eq_bandwidth;
  220. }
  221. void EaxDistortionEffect::set_efx_defaults()
  222. {
  223. set_efx_edge();
  224. set_efx_gain();
  225. set_efx_lowpass_cutoff();
  226. set_efx_eq_center();
  227. set_efx_eq_bandwidth();
  228. }
  229. void EaxDistortionEffect::get(const EaxEaxCall& eax_call)
  230. {
  231. switch(eax_call.get_property_id())
  232. {
  233. case EAXDISTORTION_NONE:
  234. break;
  235. case EAXDISTORTION_ALLPARAMETERS:
  236. eax_call.set_value<EaxDistortionEffectException>(eax_);
  237. break;
  238. case EAXDISTORTION_EDGE:
  239. eax_call.set_value<EaxDistortionEffectException>(eax_.flEdge);
  240. break;
  241. case EAXDISTORTION_GAIN:
  242. eax_call.set_value<EaxDistortionEffectException>(eax_.lGain);
  243. break;
  244. case EAXDISTORTION_LOWPASSCUTOFF:
  245. eax_call.set_value<EaxDistortionEffectException>(eax_.flLowPassCutOff);
  246. break;
  247. case EAXDISTORTION_EQCENTER:
  248. eax_call.set_value<EaxDistortionEffectException>(eax_.flEQCenter);
  249. break;
  250. case EAXDISTORTION_EQBANDWIDTH:
  251. eax_call.set_value<EaxDistortionEffectException>(eax_.flEQBandwidth);
  252. break;
  253. default:
  254. throw EaxDistortionEffectException{"Unsupported property id."};
  255. }
  256. }
  257. void EaxDistortionEffect::validate_edge(
  258. float flEdge)
  259. {
  260. eax_validate_range<EaxDistortionEffectException>(
  261. "Edge",
  262. flEdge,
  263. EAXDISTORTION_MINEDGE,
  264. EAXDISTORTION_MAXEDGE);
  265. }
  266. void EaxDistortionEffect::validate_gain(
  267. long lGain)
  268. {
  269. eax_validate_range<EaxDistortionEffectException>(
  270. "Gain",
  271. lGain,
  272. EAXDISTORTION_MINGAIN,
  273. EAXDISTORTION_MAXGAIN);
  274. }
  275. void EaxDistortionEffect::validate_lowpass_cutoff(
  276. float flLowPassCutOff)
  277. {
  278. eax_validate_range<EaxDistortionEffectException>(
  279. "Low-pass Cut-off",
  280. flLowPassCutOff,
  281. EAXDISTORTION_MINLOWPASSCUTOFF,
  282. EAXDISTORTION_MAXLOWPASSCUTOFF);
  283. }
  284. void EaxDistortionEffect::validate_eq_center(
  285. float flEQCenter)
  286. {
  287. eax_validate_range<EaxDistortionEffectException>(
  288. "EQ Center",
  289. flEQCenter,
  290. EAXDISTORTION_MINEQCENTER,
  291. EAXDISTORTION_MAXEQCENTER);
  292. }
  293. void EaxDistortionEffect::validate_eq_bandwidth(
  294. float flEQBandwidth)
  295. {
  296. eax_validate_range<EaxDistortionEffectException>(
  297. "EQ Bandwidth",
  298. flEQBandwidth,
  299. EAXDISTORTION_MINEQBANDWIDTH,
  300. EAXDISTORTION_MAXEQBANDWIDTH);
  301. }
  302. void EaxDistortionEffect::validate_all(
  303. const EAXDISTORTIONPROPERTIES& eax_all)
  304. {
  305. validate_edge(eax_all.flEdge);
  306. validate_gain(eax_all.lGain);
  307. validate_lowpass_cutoff(eax_all.flLowPassCutOff);
  308. validate_eq_center(eax_all.flEQCenter);
  309. validate_eq_bandwidth(eax_all.flEQBandwidth);
  310. }
  311. void EaxDistortionEffect::defer_edge(
  312. float flEdge)
  313. {
  314. eax_d_.flEdge = flEdge;
  315. eax_dirty_flags_.flEdge = (eax_.flEdge != eax_d_.flEdge);
  316. }
  317. void EaxDistortionEffect::defer_gain(
  318. long lGain)
  319. {
  320. eax_d_.lGain = lGain;
  321. eax_dirty_flags_.lGain = (eax_.lGain != eax_d_.lGain);
  322. }
  323. void EaxDistortionEffect::defer_low_pass_cutoff(
  324. float flLowPassCutOff)
  325. {
  326. eax_d_.flLowPassCutOff = flLowPassCutOff;
  327. eax_dirty_flags_.flLowPassCutOff = (eax_.flLowPassCutOff != eax_d_.flLowPassCutOff);
  328. }
  329. void EaxDistortionEffect::defer_eq_center(
  330. float flEQCenter)
  331. {
  332. eax_d_.flEQCenter = flEQCenter;
  333. eax_dirty_flags_.flEQCenter = (eax_.flEQCenter != eax_d_.flEQCenter);
  334. }
  335. void EaxDistortionEffect::defer_eq_bandwidth(
  336. float flEQBandwidth)
  337. {
  338. eax_d_.flEQBandwidth = flEQBandwidth;
  339. eax_dirty_flags_.flEQBandwidth = (eax_.flEQBandwidth != eax_d_.flEQBandwidth);
  340. }
  341. void EaxDistortionEffect::defer_all(
  342. const EAXDISTORTIONPROPERTIES& eax_all)
  343. {
  344. defer_edge(eax_all.flEdge);
  345. defer_gain(eax_all.lGain);
  346. defer_low_pass_cutoff(eax_all.flLowPassCutOff);
  347. defer_eq_center(eax_all.flEQCenter);
  348. defer_eq_bandwidth(eax_all.flEQBandwidth);
  349. }
  350. void EaxDistortionEffect::defer_edge(
  351. const EaxEaxCall& eax_call)
  352. {
  353. const auto& edge =
  354. eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEdge)>();
  355. validate_edge(edge);
  356. defer_edge(edge);
  357. }
  358. void EaxDistortionEffect::defer_gain(
  359. const EaxEaxCall& eax_call)
  360. {
  361. const auto& gain =
  362. eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::lGain)>();
  363. validate_gain(gain);
  364. defer_gain(gain);
  365. }
  366. void EaxDistortionEffect::defer_low_pass_cutoff(
  367. const EaxEaxCall& eax_call)
  368. {
  369. const auto& lowpass_cutoff =
  370. eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flLowPassCutOff)>();
  371. validate_lowpass_cutoff(lowpass_cutoff);
  372. defer_low_pass_cutoff(lowpass_cutoff);
  373. }
  374. void EaxDistortionEffect::defer_eq_center(
  375. const EaxEaxCall& eax_call)
  376. {
  377. const auto& eq_center =
  378. eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQCenter)>();
  379. validate_eq_center(eq_center);
  380. defer_eq_center(eq_center);
  381. }
  382. void EaxDistortionEffect::defer_eq_bandwidth(
  383. const EaxEaxCall& eax_call)
  384. {
  385. const auto& eq_bandwidth =
  386. eax_call.get_value<EaxDistortionEffectException, const decltype(EAXDISTORTIONPROPERTIES::flEQBandwidth)>();
  387. validate_eq_bandwidth(eq_bandwidth);
  388. defer_eq_bandwidth(eq_bandwidth);
  389. }
  390. void EaxDistortionEffect::defer_all(
  391. const EaxEaxCall& eax_call)
  392. {
  393. const auto& all =
  394. eax_call.get_value<EaxDistortionEffectException, const EAXDISTORTIONPROPERTIES>();
  395. validate_all(all);
  396. defer_all(all);
  397. }
  398. // [[nodiscard]]
  399. bool EaxDistortionEffect::apply_deferred()
  400. {
  401. if (eax_dirty_flags_ == EaxDistortionEffectDirtyFlags{})
  402. {
  403. return false;
  404. }
  405. eax_ = eax_d_;
  406. if (eax_dirty_flags_.flEdge)
  407. {
  408. set_efx_edge();
  409. }
  410. if (eax_dirty_flags_.lGain)
  411. {
  412. set_efx_gain();
  413. }
  414. if (eax_dirty_flags_.flLowPassCutOff)
  415. {
  416. set_efx_lowpass_cutoff();
  417. }
  418. if (eax_dirty_flags_.flEQCenter)
  419. {
  420. set_efx_eq_center();
  421. }
  422. if (eax_dirty_flags_.flEQBandwidth)
  423. {
  424. set_efx_eq_bandwidth();
  425. }
  426. eax_dirty_flags_ = EaxDistortionEffectDirtyFlags{};
  427. return true;
  428. }
  429. void EaxDistortionEffect::set(const EaxEaxCall& eax_call)
  430. {
  431. switch(eax_call.get_property_id())
  432. {
  433. case EAXDISTORTION_NONE:
  434. break;
  435. case EAXDISTORTION_ALLPARAMETERS:
  436. defer_all(eax_call);
  437. break;
  438. case EAXDISTORTION_EDGE:
  439. defer_edge(eax_call);
  440. break;
  441. case EAXDISTORTION_GAIN:
  442. defer_gain(eax_call);
  443. break;
  444. case EAXDISTORTION_LOWPASSCUTOFF:
  445. defer_low_pass_cutoff(eax_call);
  446. break;
  447. case EAXDISTORTION_EQCENTER:
  448. defer_eq_center(eax_call);
  449. break;
  450. case EAXDISTORTION_EQBANDWIDTH:
  451. defer_eq_bandwidth(eax_call);
  452. break;
  453. default:
  454. throw EaxDistortionEffectException{"Unsupported property id."};
  455. }
  456. }
  457. } // namespace
  458. EaxEffectUPtr eax_create_eax_distortion_effect()
  459. {
  460. return std::make_unique<EaxDistortionEffect>();
  461. }
  462. #endif // ALSOFT_EAX