chorus.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. #include "config.h"
  2. #include <optional>
  3. #include <stdexcept>
  4. #include "AL/al.h"
  5. #include "AL/efx.h"
  6. #include "alc/context.h"
  7. #include "alnumeric.h"
  8. #include "core/logging.h"
  9. #include "effects.h"
  10. #if ALSOFT_EAX
  11. #include <cassert>
  12. #include "al/eax/effect.h"
  13. #include "al/eax/exception.h"
  14. #include "al/eax/utils.h"
  15. #endif // ALSOFT_EAX
  16. namespace {
  17. static_assert(ChorusMaxDelay >= AL_CHORUS_MAX_DELAY, "Chorus max delay too small");
  18. static_assert(FlangerMaxDelay >= AL_FLANGER_MAX_DELAY, "Flanger max delay too small");
  19. static_assert(AL_CHORUS_WAVEFORM_SINUSOID == AL_FLANGER_WAVEFORM_SINUSOID, "Chorus/Flanger waveform value mismatch");
  20. static_assert(AL_CHORUS_WAVEFORM_TRIANGLE == AL_FLANGER_WAVEFORM_TRIANGLE, "Chorus/Flanger waveform value mismatch");
  21. constexpr std::optional<ChorusWaveform> WaveformFromEnum(ALenum type) noexcept
  22. {
  23. switch(type)
  24. {
  25. case AL_CHORUS_WAVEFORM_SINUSOID: return ChorusWaveform::Sinusoid;
  26. case AL_CHORUS_WAVEFORM_TRIANGLE: return ChorusWaveform::Triangle;
  27. }
  28. return std::nullopt;
  29. }
  30. constexpr ALenum EnumFromWaveform(ChorusWaveform type)
  31. {
  32. switch(type)
  33. {
  34. case ChorusWaveform::Sinusoid: return AL_CHORUS_WAVEFORM_SINUSOID;
  35. case ChorusWaveform::Triangle: return AL_CHORUS_WAVEFORM_TRIANGLE;
  36. }
  37. throw std::runtime_error{fmt::format("Invalid chorus waveform: {}",
  38. int{al::to_underlying(type)})};
  39. }
  40. constexpr EffectProps genDefaultChorusProps() noexcept
  41. {
  42. ChorusProps props{};
  43. props.Waveform = WaveformFromEnum(AL_CHORUS_DEFAULT_WAVEFORM).value();
  44. props.Phase = AL_CHORUS_DEFAULT_PHASE;
  45. props.Rate = AL_CHORUS_DEFAULT_RATE;
  46. props.Depth = AL_CHORUS_DEFAULT_DEPTH;
  47. props.Feedback = AL_CHORUS_DEFAULT_FEEDBACK;
  48. props.Delay = AL_CHORUS_DEFAULT_DELAY;
  49. return props;
  50. }
  51. constexpr EffectProps genDefaultFlangerProps() noexcept
  52. {
  53. ChorusProps props{};
  54. props.Waveform = WaveformFromEnum(AL_FLANGER_DEFAULT_WAVEFORM).value();
  55. props.Phase = AL_FLANGER_DEFAULT_PHASE;
  56. props.Rate = AL_FLANGER_DEFAULT_RATE;
  57. props.Depth = AL_FLANGER_DEFAULT_DEPTH;
  58. props.Feedback = AL_FLANGER_DEFAULT_FEEDBACK;
  59. props.Delay = AL_FLANGER_DEFAULT_DELAY;
  60. return props;
  61. }
  62. } // namespace
  63. const EffectProps ChorusEffectProps{genDefaultChorusProps()};
  64. void ChorusEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val)
  65. {
  66. switch(param)
  67. {
  68. case AL_CHORUS_WAVEFORM:
  69. if(auto formopt = WaveformFromEnum(val))
  70. props.Waveform = *formopt;
  71. else
  72. context->throw_error(AL_INVALID_VALUE, "Invalid chorus waveform: {:#04x}",
  73. as_unsigned(val));
  74. return;
  75. case AL_CHORUS_PHASE:
  76. if(!(val >= AL_CHORUS_MIN_PHASE && val <= AL_CHORUS_MAX_PHASE))
  77. context->throw_error(AL_INVALID_VALUE, "Chorus phase out of range: {}", val);
  78. props.Phase = val;
  79. return;
  80. }
  81. context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}",
  82. as_unsigned(param));
  83. }
  84. void ChorusEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals)
  85. { SetParami(context, props, param, *vals); }
  86. void ChorusEffectHandler::SetParamf(ALCcontext *context, ChorusProps &props, ALenum param, float val)
  87. {
  88. switch(param)
  89. {
  90. case AL_CHORUS_RATE:
  91. if(!(val >= AL_CHORUS_MIN_RATE && val <= AL_CHORUS_MAX_RATE))
  92. context->throw_error(AL_INVALID_VALUE, "Chorus rate out of range: {:f}", val);
  93. props.Rate = val;
  94. return;
  95. case AL_CHORUS_DEPTH:
  96. if(!(val >= AL_CHORUS_MIN_DEPTH && val <= AL_CHORUS_MAX_DEPTH))
  97. context->throw_error(AL_INVALID_VALUE, "Chorus depth out of range: {:f}", val);
  98. props.Depth = val;
  99. return;
  100. case AL_CHORUS_FEEDBACK:
  101. if(!(val >= AL_CHORUS_MIN_FEEDBACK && val <= AL_CHORUS_MAX_FEEDBACK))
  102. context->throw_error(AL_INVALID_VALUE, "Chorus feedback out of range: {:f}", val);
  103. props.Feedback = val;
  104. return;
  105. case AL_CHORUS_DELAY:
  106. if(!(val >= AL_CHORUS_MIN_DELAY && val <= AL_CHORUS_MAX_DELAY))
  107. context->throw_error(AL_INVALID_VALUE, "Chorus delay out of range: {:f}", val);
  108. props.Delay = val;
  109. return;
  110. }
  111. context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}",
  112. as_unsigned(param));
  113. }
  114. void ChorusEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals)
  115. { SetParamf(context, props, param, *vals); }
  116. void ChorusEffectHandler::GetParami(ALCcontext *context, const ChorusProps &props, ALenum param, int *val)
  117. {
  118. switch(param)
  119. {
  120. case AL_CHORUS_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
  121. case AL_CHORUS_PHASE: *val = props.Phase; return;
  122. }
  123. context->throw_error(AL_INVALID_ENUM, "Invalid chorus integer property {:#04x}",
  124. as_unsigned(param));
  125. }
  126. void ChorusEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals)
  127. { GetParami(context, props, param, vals); }
  128. void ChorusEffectHandler::GetParamf(ALCcontext *context, const ChorusProps &props, ALenum param, float *val)
  129. {
  130. switch(param)
  131. {
  132. case AL_CHORUS_RATE: *val = props.Rate; return;
  133. case AL_CHORUS_DEPTH: *val = props.Depth; return;
  134. case AL_CHORUS_FEEDBACK: *val = props.Feedback; return;
  135. case AL_CHORUS_DELAY: *val = props.Delay; return;
  136. }
  137. context->throw_error(AL_INVALID_ENUM, "Invalid chorus float property {:#04x}",
  138. as_unsigned(param));
  139. }
  140. void ChorusEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals)
  141. { GetParamf(context, props, param, vals); }
  142. const EffectProps FlangerEffectProps{genDefaultFlangerProps()};
  143. void FlangerEffectHandler::SetParami(ALCcontext *context, ChorusProps &props, ALenum param, int val)
  144. {
  145. switch(param)
  146. {
  147. case AL_FLANGER_WAVEFORM:
  148. if(auto formopt = WaveformFromEnum(val))
  149. props.Waveform = *formopt;
  150. else
  151. context->throw_error(AL_INVALID_VALUE, "Invalid flanger waveform: {:#04x}",
  152. as_unsigned(val));
  153. return;
  154. case AL_FLANGER_PHASE:
  155. if(!(val >= AL_FLANGER_MIN_PHASE && val <= AL_FLANGER_MAX_PHASE))
  156. context->throw_error(AL_INVALID_VALUE, "Flanger phase out of range: {}", val);
  157. props.Phase = val;
  158. return;
  159. }
  160. context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}",
  161. as_unsigned(param));
  162. }
  163. void FlangerEffectHandler::SetParamiv(ALCcontext *context, ChorusProps &props, ALenum param, const int *vals)
  164. { SetParami(context, props, param, *vals); }
  165. void FlangerEffectHandler::SetParamf(ALCcontext *context, ChorusProps &props, ALenum param, float val)
  166. {
  167. switch(param)
  168. {
  169. case AL_FLANGER_RATE:
  170. if(!(val >= AL_FLANGER_MIN_RATE && val <= AL_FLANGER_MAX_RATE))
  171. context->throw_error(AL_INVALID_VALUE, "Flanger rate out of range: {:f}", val);
  172. props.Rate = val;
  173. return;
  174. case AL_FLANGER_DEPTH:
  175. if(!(val >= AL_FLANGER_MIN_DEPTH && val <= AL_FLANGER_MAX_DEPTH))
  176. context->throw_error(AL_INVALID_VALUE, "Flanger depth out of range: {:f}", val);
  177. props.Depth = val;
  178. return;
  179. case AL_FLANGER_FEEDBACK:
  180. if(!(val >= AL_FLANGER_MIN_FEEDBACK && val <= AL_FLANGER_MAX_FEEDBACK))
  181. context->throw_error(AL_INVALID_VALUE, "Flanger feedback out of range: {:f}", val);
  182. props.Feedback = val;
  183. return;
  184. case AL_FLANGER_DELAY:
  185. if(!(val >= AL_FLANGER_MIN_DELAY && val <= AL_FLANGER_MAX_DELAY))
  186. context->throw_error(AL_INVALID_VALUE, "Flanger delay out of range: {:f}", val);
  187. props.Delay = val;
  188. return;
  189. }
  190. context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}",
  191. as_unsigned(param));
  192. }
  193. void FlangerEffectHandler::SetParamfv(ALCcontext *context, ChorusProps &props, ALenum param, const float *vals)
  194. { SetParamf(context, props, param, *vals); }
  195. void FlangerEffectHandler::GetParami(ALCcontext *context, const ChorusProps &props, ALenum param, int *val)
  196. {
  197. switch(param)
  198. {
  199. case AL_FLANGER_WAVEFORM: *val = EnumFromWaveform(props.Waveform); return;
  200. case AL_FLANGER_PHASE: *val = props.Phase; return;
  201. }
  202. context->throw_error(AL_INVALID_ENUM, "Invalid flanger integer property {:#04x}",
  203. as_unsigned(param));
  204. }
  205. void FlangerEffectHandler::GetParamiv(ALCcontext *context, const ChorusProps &props, ALenum param, int *vals)
  206. { GetParami(context, props, param, vals); }
  207. void FlangerEffectHandler::GetParamf(ALCcontext *context, const ChorusProps &props, ALenum param, float *val)
  208. {
  209. switch(param)
  210. {
  211. case AL_FLANGER_RATE: *val = props.Rate; return;
  212. case AL_FLANGER_DEPTH: *val = props.Depth; return;
  213. case AL_FLANGER_FEEDBACK: *val = props.Feedback; return;
  214. case AL_FLANGER_DELAY: *val = props.Delay; return;
  215. }
  216. context->throw_error(AL_INVALID_ENUM, "Invalid flanger float property {:#04x}",
  217. as_unsigned(param));
  218. }
  219. void FlangerEffectHandler::GetParamfv(ALCcontext *context, const ChorusProps &props, ALenum param, float *vals)
  220. { GetParamf(context, props, param, vals); }
  221. #if ALSOFT_EAX
  222. namespace {
  223. struct EaxChorusTraits {
  224. using EaxProps = EAXCHORUSPROPERTIES;
  225. using Committer = EaxChorusCommitter;
  226. static constexpr auto efx_effect() { return AL_EFFECT_CHORUS; }
  227. static constexpr auto eax_none_param_id() { return EAXCHORUS_NONE; }
  228. static constexpr auto eax_allparameters_param_id() { return EAXCHORUS_ALLPARAMETERS; }
  229. static constexpr auto eax_waveform_param_id() { return EAXCHORUS_WAVEFORM; }
  230. static constexpr auto eax_phase_param_id() { return EAXCHORUS_PHASE; }
  231. static constexpr auto eax_rate_param_id() { return EAXCHORUS_RATE; }
  232. static constexpr auto eax_depth_param_id() { return EAXCHORUS_DEPTH; }
  233. static constexpr auto eax_feedback_param_id() { return EAXCHORUS_FEEDBACK; }
  234. static constexpr auto eax_delay_param_id() { return EAXCHORUS_DELAY; }
  235. static constexpr auto eax_min_waveform() { return EAXCHORUS_MINWAVEFORM; }
  236. static constexpr auto eax_min_phase() { return EAXCHORUS_MINPHASE; }
  237. static constexpr auto eax_min_rate() { return EAXCHORUS_MINRATE; }
  238. static constexpr auto eax_min_depth() { return EAXCHORUS_MINDEPTH; }
  239. static constexpr auto eax_min_feedback() { return EAXCHORUS_MINFEEDBACK; }
  240. static constexpr auto eax_min_delay() { return EAXCHORUS_MINDELAY; }
  241. static constexpr auto eax_max_waveform() { return EAXCHORUS_MAXWAVEFORM; }
  242. static constexpr auto eax_max_phase() { return EAXCHORUS_MAXPHASE; }
  243. static constexpr auto eax_max_rate() { return EAXCHORUS_MAXRATE; }
  244. static constexpr auto eax_max_depth() { return EAXCHORUS_MAXDEPTH; }
  245. static constexpr auto eax_max_feedback() { return EAXCHORUS_MAXFEEDBACK; }
  246. static constexpr auto eax_max_delay() { return EAXCHORUS_MAXDELAY; }
  247. static constexpr auto eax_default_waveform() { return EAXCHORUS_DEFAULTWAVEFORM; }
  248. static constexpr auto eax_default_phase() { return EAXCHORUS_DEFAULTPHASE; }
  249. static constexpr auto eax_default_rate() { return EAXCHORUS_DEFAULTRATE; }
  250. static constexpr auto eax_default_depth() { return EAXCHORUS_DEFAULTDEPTH; }
  251. static constexpr auto eax_default_feedback() { return EAXCHORUS_DEFAULTFEEDBACK; }
  252. static constexpr auto eax_default_delay() { return EAXCHORUS_DEFAULTDELAY; }
  253. static constexpr auto efx_min_waveform() { return AL_CHORUS_MIN_WAVEFORM; }
  254. static constexpr auto efx_min_phase() { return AL_CHORUS_MIN_PHASE; }
  255. static constexpr auto efx_min_rate() { return AL_CHORUS_MIN_RATE; }
  256. static constexpr auto efx_min_depth() { return AL_CHORUS_MIN_DEPTH; }
  257. static constexpr auto efx_min_feedback() { return AL_CHORUS_MIN_FEEDBACK; }
  258. static constexpr auto efx_min_delay() { return AL_CHORUS_MIN_DELAY; }
  259. static constexpr auto efx_max_waveform() { return AL_CHORUS_MAX_WAVEFORM; }
  260. static constexpr auto efx_max_phase() { return AL_CHORUS_MAX_PHASE; }
  261. static constexpr auto efx_max_rate() { return AL_CHORUS_MAX_RATE; }
  262. static constexpr auto efx_max_depth() { return AL_CHORUS_MAX_DEPTH; }
  263. static constexpr auto efx_max_feedback() { return AL_CHORUS_MAX_FEEDBACK; }
  264. static constexpr auto efx_max_delay() { return AL_CHORUS_MAX_DELAY; }
  265. static constexpr auto efx_default_waveform() { return AL_CHORUS_DEFAULT_WAVEFORM; }
  266. static constexpr auto efx_default_phase() { return AL_CHORUS_DEFAULT_PHASE; }
  267. static constexpr auto efx_default_rate() { return AL_CHORUS_DEFAULT_RATE; }
  268. static constexpr auto efx_default_depth() { return AL_CHORUS_DEFAULT_DEPTH; }
  269. static constexpr auto efx_default_feedback() { return AL_CHORUS_DEFAULT_FEEDBACK; }
  270. static constexpr auto efx_default_delay() { return AL_CHORUS_DEFAULT_DELAY; }
  271. static ChorusWaveform eax_waveform(unsigned long type)
  272. {
  273. if(type == EAX_CHORUS_SINUSOID) return ChorusWaveform::Sinusoid;
  274. if(type == EAX_CHORUS_TRIANGLE) return ChorusWaveform::Triangle;
  275. return ChorusWaveform::Sinusoid;
  276. }
  277. }; // EaxChorusTraits
  278. struct EaxFlangerTraits {
  279. using EaxProps = EAXFLANGERPROPERTIES;
  280. using Committer = EaxFlangerCommitter;
  281. static constexpr auto efx_effect() { return AL_EFFECT_FLANGER; }
  282. static constexpr auto eax_none_param_id() { return EAXFLANGER_NONE; }
  283. static constexpr auto eax_allparameters_param_id() { return EAXFLANGER_ALLPARAMETERS; }
  284. static constexpr auto eax_waveform_param_id() { return EAXFLANGER_WAVEFORM; }
  285. static constexpr auto eax_phase_param_id() { return EAXFLANGER_PHASE; }
  286. static constexpr auto eax_rate_param_id() { return EAXFLANGER_RATE; }
  287. static constexpr auto eax_depth_param_id() { return EAXFLANGER_DEPTH; }
  288. static constexpr auto eax_feedback_param_id() { return EAXFLANGER_FEEDBACK; }
  289. static constexpr auto eax_delay_param_id() { return EAXFLANGER_DELAY; }
  290. static constexpr auto eax_min_waveform() { return EAXFLANGER_MINWAVEFORM; }
  291. static constexpr auto eax_min_phase() { return EAXFLANGER_MINPHASE; }
  292. static constexpr auto eax_min_rate() { return EAXFLANGER_MINRATE; }
  293. static constexpr auto eax_min_depth() { return EAXFLANGER_MINDEPTH; }
  294. static constexpr auto eax_min_feedback() { return EAXFLANGER_MINFEEDBACK; }
  295. static constexpr auto eax_min_delay() { return EAXFLANGER_MINDELAY; }
  296. static constexpr auto eax_max_waveform() { return EAXFLANGER_MAXWAVEFORM; }
  297. static constexpr auto eax_max_phase() { return EAXFLANGER_MAXPHASE; }
  298. static constexpr auto eax_max_rate() { return EAXFLANGER_MAXRATE; }
  299. static constexpr auto eax_max_depth() { return EAXFLANGER_MAXDEPTH; }
  300. static constexpr auto eax_max_feedback() { return EAXFLANGER_MAXFEEDBACK; }
  301. static constexpr auto eax_max_delay() { return EAXFLANGER_MAXDELAY; }
  302. static constexpr auto eax_default_waveform() { return EAXFLANGER_DEFAULTWAVEFORM; }
  303. static constexpr auto eax_default_phase() { return EAXFLANGER_DEFAULTPHASE; }
  304. static constexpr auto eax_default_rate() { return EAXFLANGER_DEFAULTRATE; }
  305. static constexpr auto eax_default_depth() { return EAXFLANGER_DEFAULTDEPTH; }
  306. static constexpr auto eax_default_feedback() { return EAXFLANGER_DEFAULTFEEDBACK; }
  307. static constexpr auto eax_default_delay() { return EAXFLANGER_DEFAULTDELAY; }
  308. static constexpr auto efx_min_waveform() { return AL_FLANGER_MIN_WAVEFORM; }
  309. static constexpr auto efx_min_phase() { return AL_FLANGER_MIN_PHASE; }
  310. static constexpr auto efx_min_rate() { return AL_FLANGER_MIN_RATE; }
  311. static constexpr auto efx_min_depth() { return AL_FLANGER_MIN_DEPTH; }
  312. static constexpr auto efx_min_feedback() { return AL_FLANGER_MIN_FEEDBACK; }
  313. static constexpr auto efx_min_delay() { return AL_FLANGER_MIN_DELAY; }
  314. static constexpr auto efx_max_waveform() { return AL_FLANGER_MAX_WAVEFORM; }
  315. static constexpr auto efx_max_phase() { return AL_FLANGER_MAX_PHASE; }
  316. static constexpr auto efx_max_rate() { return AL_FLANGER_MAX_RATE; }
  317. static constexpr auto efx_max_depth() { return AL_FLANGER_MAX_DEPTH; }
  318. static constexpr auto efx_max_feedback() { return AL_FLANGER_MAX_FEEDBACK; }
  319. static constexpr auto efx_max_delay() { return AL_FLANGER_MAX_DELAY; }
  320. static constexpr auto efx_default_waveform() { return AL_FLANGER_DEFAULT_WAVEFORM; }
  321. static constexpr auto efx_default_phase() { return AL_FLANGER_DEFAULT_PHASE; }
  322. static constexpr auto efx_default_rate() { return AL_FLANGER_DEFAULT_RATE; }
  323. static constexpr auto efx_default_depth() { return AL_FLANGER_DEFAULT_DEPTH; }
  324. static constexpr auto efx_default_feedback() { return AL_FLANGER_DEFAULT_FEEDBACK; }
  325. static constexpr auto efx_default_delay() { return AL_FLANGER_DEFAULT_DELAY; }
  326. static ChorusWaveform eax_waveform(unsigned long type)
  327. {
  328. if(type == EAX_FLANGER_SINUSOID) return ChorusWaveform::Sinusoid;
  329. if(type == EAX_FLANGER_TRIANGLE) return ChorusWaveform::Triangle;
  330. return ChorusWaveform::Sinusoid;
  331. }
  332. }; // EaxFlangerTraits
  333. template<typename TTraits>
  334. struct ChorusFlangerEffect {
  335. using Traits = TTraits;
  336. using EaxProps = typename Traits::EaxProps;
  337. using Committer = typename Traits::Committer;
  338. using Exception = typename Committer::Exception;
  339. struct WaveformValidator {
  340. void operator()(unsigned long ulWaveform) const
  341. {
  342. eax_validate_range<Exception>(
  343. "Waveform",
  344. ulWaveform,
  345. Traits::eax_min_waveform(),
  346. Traits::eax_max_waveform());
  347. }
  348. }; // WaveformValidator
  349. struct PhaseValidator {
  350. void operator()(long lPhase) const
  351. {
  352. eax_validate_range<Exception>(
  353. "Phase",
  354. lPhase,
  355. Traits::eax_min_phase(),
  356. Traits::eax_max_phase());
  357. }
  358. }; // PhaseValidator
  359. struct RateValidator {
  360. void operator()(float flRate) const
  361. {
  362. eax_validate_range<Exception>(
  363. "Rate",
  364. flRate,
  365. Traits::eax_min_rate(),
  366. Traits::eax_max_rate());
  367. }
  368. }; // RateValidator
  369. struct DepthValidator {
  370. void operator()(float flDepth) const
  371. {
  372. eax_validate_range<Exception>(
  373. "Depth",
  374. flDepth,
  375. Traits::eax_min_depth(),
  376. Traits::eax_max_depth());
  377. }
  378. }; // DepthValidator
  379. struct FeedbackValidator {
  380. void operator()(float flFeedback) const
  381. {
  382. eax_validate_range<Exception>(
  383. "Feedback",
  384. flFeedback,
  385. Traits::eax_min_feedback(),
  386. Traits::eax_max_feedback());
  387. }
  388. }; // FeedbackValidator
  389. struct DelayValidator {
  390. void operator()(float flDelay) const
  391. {
  392. eax_validate_range<Exception>(
  393. "Delay",
  394. flDelay,
  395. Traits::eax_min_delay(),
  396. Traits::eax_max_delay());
  397. }
  398. }; // DelayValidator
  399. struct AllValidator {
  400. void operator()(const EaxProps& all) const
  401. {
  402. WaveformValidator{}(all.ulWaveform);
  403. PhaseValidator{}(all.lPhase);
  404. RateValidator{}(all.flRate);
  405. DepthValidator{}(all.flDepth);
  406. FeedbackValidator{}(all.flFeedback);
  407. DelayValidator{}(all.flDelay);
  408. }
  409. }; // AllValidator
  410. public:
  411. static void SetDefaults(EaxEffectProps &props)
  412. {
  413. auto&& all = props.emplace<EaxProps>();
  414. all.ulWaveform = Traits::eax_default_waveform();
  415. all.lPhase = Traits::eax_default_phase();
  416. all.flRate = Traits::eax_default_rate();
  417. all.flDepth = Traits::eax_default_depth();
  418. all.flFeedback = Traits::eax_default_feedback();
  419. all.flDelay = Traits::eax_default_delay();
  420. }
  421. static void Get(const EaxCall &call, const EaxProps &all)
  422. {
  423. switch(call.get_property_id())
  424. {
  425. case Traits::eax_none_param_id():
  426. break;
  427. case Traits::eax_allparameters_param_id():
  428. call.template set_value<Exception>(all);
  429. break;
  430. case Traits::eax_waveform_param_id():
  431. call.template set_value<Exception>(all.ulWaveform);
  432. break;
  433. case Traits::eax_phase_param_id():
  434. call.template set_value<Exception>(all.lPhase);
  435. break;
  436. case Traits::eax_rate_param_id():
  437. call.template set_value<Exception>(all.flRate);
  438. break;
  439. case Traits::eax_depth_param_id():
  440. call.template set_value<Exception>(all.flDepth);
  441. break;
  442. case Traits::eax_feedback_param_id():
  443. call.template set_value<Exception>(all.flFeedback);
  444. break;
  445. case Traits::eax_delay_param_id():
  446. call.template set_value<Exception>(all.flDelay);
  447. break;
  448. default:
  449. Committer::fail_unknown_property_id();
  450. }
  451. }
  452. static void Set(const EaxCall &call, EaxProps &all)
  453. {
  454. switch(call.get_property_id())
  455. {
  456. case Traits::eax_none_param_id():
  457. break;
  458. case Traits::eax_allparameters_param_id():
  459. Committer::template defer<AllValidator>(call, all);
  460. break;
  461. case Traits::eax_waveform_param_id():
  462. Committer::template defer<WaveformValidator>(call, all.ulWaveform);
  463. break;
  464. case Traits::eax_phase_param_id():
  465. Committer::template defer<PhaseValidator>(call, all.lPhase);
  466. break;
  467. case Traits::eax_rate_param_id():
  468. Committer::template defer<RateValidator>(call, all.flRate);
  469. break;
  470. case Traits::eax_depth_param_id():
  471. Committer::template defer<DepthValidator>(call, all.flDepth);
  472. break;
  473. case Traits::eax_feedback_param_id():
  474. Committer::template defer<FeedbackValidator>(call, all.flFeedback);
  475. break;
  476. case Traits::eax_delay_param_id():
  477. Committer::template defer<DelayValidator>(call, all.flDelay);
  478. break;
  479. default:
  480. Committer::fail_unknown_property_id();
  481. }
  482. }
  483. static bool Commit(const EaxProps &props, EaxEffectProps &props_, ChorusProps &al_props_)
  484. {
  485. if(auto *cur = std::get_if<EaxProps>(&props_); cur && *cur == props)
  486. return false;
  487. props_ = props;
  488. al_props_.Waveform = Traits::eax_waveform(props.ulWaveform);
  489. al_props_.Phase = static_cast<int>(props.lPhase);
  490. al_props_.Rate = props.flRate;
  491. al_props_.Depth = props.flDepth;
  492. al_props_.Feedback = props.flFeedback;
  493. al_props_.Delay = props.flDelay;
  494. if(EaxTraceCommits) UNLIKELY
  495. {
  496. TRACE("Chorus/flanger commit:\n"
  497. " Waveform: {}\n"
  498. " Phase: {}\n"
  499. " Rate: {:f}\n"
  500. " Depth: {:f}\n"
  501. " Feedback: {:f}\n"
  502. " Delay: {:f}", al::to_underlying(al_props_.Waveform), al_props_.Phase,
  503. al_props_.Rate, al_props_.Depth, al_props_.Feedback, al_props_.Delay);
  504. }
  505. return true;
  506. }
  507. }; // EaxChorusFlangerEffect
  508. using ChorusCommitter = EaxCommitter<EaxChorusCommitter>;
  509. using FlangerCommitter = EaxCommitter<EaxFlangerCommitter>;
  510. } // namespace
  511. template<>
  512. struct ChorusCommitter::Exception : public EaxException
  513. {
  514. explicit Exception(const char *message) : EaxException{"EAX_CHORUS_EFFECT", message}
  515. { }
  516. };
  517. template<>
  518. [[noreturn]] void ChorusCommitter::fail(const char *message)
  519. {
  520. throw Exception{message};
  521. }
  522. bool EaxChorusCommitter::commit(const EAXCHORUSPROPERTIES &props)
  523. {
  524. using Committer = ChorusFlangerEffect<EaxChorusTraits>;
  525. return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
  526. }
  527. void EaxChorusCommitter::SetDefaults(EaxEffectProps &props)
  528. {
  529. using Committer = ChorusFlangerEffect<EaxChorusTraits>;
  530. Committer::SetDefaults(props);
  531. }
  532. void EaxChorusCommitter::Get(const EaxCall &call, const EAXCHORUSPROPERTIES &props)
  533. {
  534. using Committer = ChorusFlangerEffect<EaxChorusTraits>;
  535. Committer::Get(call, props);
  536. }
  537. void EaxChorusCommitter::Set(const EaxCall &call, EAXCHORUSPROPERTIES &props)
  538. {
  539. using Committer = ChorusFlangerEffect<EaxChorusTraits>;
  540. Committer::Set(call, props);
  541. }
  542. template<>
  543. struct FlangerCommitter::Exception : public EaxException
  544. {
  545. explicit Exception(const char *message) : EaxException{"EAX_FLANGER_EFFECT", message}
  546. { }
  547. };
  548. template<>
  549. [[noreturn]] void FlangerCommitter::fail(const char *message)
  550. {
  551. throw Exception{message};
  552. }
  553. bool EaxFlangerCommitter::commit(const EAXFLANGERPROPERTIES &props)
  554. {
  555. using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
  556. return Committer::Commit(props, mEaxProps, mAlProps.emplace<ChorusProps>());
  557. }
  558. void EaxFlangerCommitter::SetDefaults(EaxEffectProps &props)
  559. {
  560. using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
  561. Committer::SetDefaults(props);
  562. }
  563. void EaxFlangerCommitter::Get(const EaxCall &call, const EAXFLANGERPROPERTIES &props)
  564. {
  565. using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
  566. Committer::Get(call, props);
  567. }
  568. void EaxFlangerCommitter::Set(const EaxCall &call, EAXFLANGERPROPERTIES &props)
  569. {
  570. using Committer = ChorusFlangerEffect<EaxFlangerTraits>;
  571. Committer::Set(call, props);
  572. }
  573. #endif // ALSOFT_EAX