vmorpher.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. /**
  2. * OpenAL cross platform audio library
  3. * Copyright (C) 2019 by Anis A. Hireche
  4. * This library is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU Library General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 2 of the License, or (at your option) any later version.
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public
  15. * License along with this library; if not, write to the
  16. * Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. * Or go to http://www.gnu.org/copyleft/lgpl.html
  19. */
  20. #include "config.h"
  21. #include <cmath>
  22. #include <cstdlib>
  23. #include <algorithm>
  24. #include <functional>
  25. #include "alcmain.h"
  26. #include "alcontext.h"
  27. #include "alu.h"
  28. #include "effectslot.h"
  29. #include "math_defs.h"
  30. namespace {
  31. #define MAX_UPDATE_SAMPLES 256
  32. #define NUM_FORMANTS 4
  33. #define NUM_FILTERS 2
  34. #define Q_FACTOR 5.0f
  35. #define VOWEL_A_INDEX 0
  36. #define VOWEL_B_INDEX 1
  37. #define WAVEFORM_FRACBITS 24
  38. #define WAVEFORM_FRACONE (1<<WAVEFORM_FRACBITS)
  39. #define WAVEFORM_FRACMASK (WAVEFORM_FRACONE-1)
  40. inline float Sin(uint index)
  41. {
  42. constexpr float scale{al::MathDefs<float>::Tau() / WAVEFORM_FRACONE};
  43. return std::sin(static_cast<float>(index) * scale)*0.5f + 0.5f;
  44. }
  45. inline float Saw(uint index)
  46. { return static_cast<float>(index) / float{WAVEFORM_FRACONE}; }
  47. inline float Triangle(uint index)
  48. { return std::fabs(static_cast<float>(index)*(2.0f/WAVEFORM_FRACONE) - 1.0f); }
  49. inline float Half(uint) { return 0.5f; }
  50. template<float (&func)(uint)>
  51. void Oscillate(float *RESTRICT dst, uint index, const uint step, size_t todo)
  52. {
  53. for(size_t i{0u};i < todo;i++)
  54. {
  55. index += step;
  56. index &= WAVEFORM_FRACMASK;
  57. dst[i] = func(index);
  58. }
  59. }
  60. struct FormantFilter
  61. {
  62. float mCoeff{0.0f};
  63. float mGain{1.0f};
  64. float mS1{0.0f};
  65. float mS2{0.0f};
  66. FormantFilter() = default;
  67. FormantFilter(float f0norm, float gain)
  68. : mCoeff{std::tan(al::MathDefs<float>::Pi() * f0norm)}, mGain{gain}
  69. { }
  70. inline void process(const float *samplesIn, float *samplesOut, const size_t numInput)
  71. {
  72. /* A state variable filter from a topology-preserving transform.
  73. * Based on a talk given by Ivan Cohen: https://www.youtube.com/watch?v=esjHXGPyrhg
  74. */
  75. const float g{mCoeff};
  76. const float gain{mGain};
  77. const float h{1.0f / (1.0f + (g/Q_FACTOR) + (g*g))};
  78. float s1{mS1};
  79. float s2{mS2};
  80. for(size_t i{0u};i < numInput;i++)
  81. {
  82. const float H{(samplesIn[i] - (1.0f/Q_FACTOR + g)*s1 - s2)*h};
  83. const float B{g*H + s1};
  84. const float L{g*B + s2};
  85. s1 = g*H + B;
  86. s2 = g*B + L;
  87. // Apply peak and accumulate samples.
  88. samplesOut[i] += B * gain;
  89. }
  90. mS1 = s1;
  91. mS2 = s2;
  92. }
  93. inline void clear()
  94. {
  95. mS1 = 0.0f;
  96. mS2 = 0.0f;
  97. }
  98. };
  99. struct VmorpherState final : public EffectState {
  100. struct {
  101. /* Effect parameters */
  102. FormantFilter Formants[NUM_FILTERS][NUM_FORMANTS];
  103. /* Effect gains for each channel */
  104. float CurrentGains[MAX_OUTPUT_CHANNELS]{};
  105. float TargetGains[MAX_OUTPUT_CHANNELS]{};
  106. } mChans[MaxAmbiChannels];
  107. void (*mGetSamples)(float*RESTRICT, uint, const uint, size_t){};
  108. uint mIndex{0};
  109. uint mStep{1};
  110. /* Effects buffers */
  111. alignas(16) float mSampleBufferA[MAX_UPDATE_SAMPLES]{};
  112. alignas(16) float mSampleBufferB[MAX_UPDATE_SAMPLES]{};
  113. alignas(16) float mLfo[MAX_UPDATE_SAMPLES]{};
  114. void deviceUpdate(const ALCdevice *device, const Buffer &buffer) override;
  115. void update(const ALCcontext *context, const EffectSlot *slot, const EffectProps *props,
  116. const EffectTarget target) override;
  117. void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn,
  118. const al::span<FloatBufferLine> samplesOut) override;
  119. static std::array<FormantFilter,4> getFiltersByPhoneme(VMorpherPhenome phoneme,
  120. float frequency, float pitch);
  121. DEF_NEWDEL(VmorpherState)
  122. };
  123. std::array<FormantFilter,4> VmorpherState::getFiltersByPhoneme(VMorpherPhenome phoneme,
  124. float frequency, float pitch)
  125. {
  126. /* Using soprano formant set of values to
  127. * better match mid-range frequency space.
  128. *
  129. * See: https://www.classes.cs.uchicago.edu/archive/1999/spring/CS295/Computing_Resources/Csound/CsManual3.48b1.HTML/Appendices/table3.html
  130. */
  131. switch(phoneme)
  132. {
  133. case VMorpherPhenome::A:
  134. return {{
  135. {( 800 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
  136. {(1150 * pitch) / frequency, 0.501187f}, /* std::pow(10.0f, -6 / 20.0f); */
  137. {(2900 * pitch) / frequency, 0.025118f}, /* std::pow(10.0f, -32 / 20.0f); */
  138. {(3900 * pitch) / frequency, 0.100000f} /* std::pow(10.0f, -20 / 20.0f); */
  139. }};
  140. case VMorpherPhenome::E:
  141. return {{
  142. {( 350 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
  143. {(2000 * pitch) / frequency, 0.100000f}, /* std::pow(10.0f, -20 / 20.0f); */
  144. {(2800 * pitch) / frequency, 0.177827f}, /* std::pow(10.0f, -15 / 20.0f); */
  145. {(3600 * pitch) / frequency, 0.009999f} /* std::pow(10.0f, -40 / 20.0f); */
  146. }};
  147. case VMorpherPhenome::I:
  148. return {{
  149. {( 270 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
  150. {(2140 * pitch) / frequency, 0.251188f}, /* std::pow(10.0f, -12 / 20.0f); */
  151. {(2950 * pitch) / frequency, 0.050118f}, /* std::pow(10.0f, -26 / 20.0f); */
  152. {(3900 * pitch) / frequency, 0.050118f} /* std::pow(10.0f, -26 / 20.0f); */
  153. }};
  154. case VMorpherPhenome::O:
  155. return {{
  156. {( 450 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
  157. {( 800 * pitch) / frequency, 0.281838f}, /* std::pow(10.0f, -11 / 20.0f); */
  158. {(2830 * pitch) / frequency, 0.079432f}, /* std::pow(10.0f, -22 / 20.0f); */
  159. {(3800 * pitch) / frequency, 0.079432f} /* std::pow(10.0f, -22 / 20.0f); */
  160. }};
  161. case VMorpherPhenome::U:
  162. return {{
  163. {( 325 * pitch) / frequency, 1.000000f}, /* std::pow(10.0f, 0 / 20.0f); */
  164. {( 700 * pitch) / frequency, 0.158489f}, /* std::pow(10.0f, -16 / 20.0f); */
  165. {(2700 * pitch) / frequency, 0.017782f}, /* std::pow(10.0f, -35 / 20.0f); */
  166. {(3800 * pitch) / frequency, 0.009999f} /* std::pow(10.0f, -40 / 20.0f); */
  167. }};
  168. default:
  169. break;
  170. }
  171. return {};
  172. }
  173. void VmorpherState::deviceUpdate(const ALCdevice*, const Buffer&)
  174. {
  175. for(auto &e : mChans)
  176. {
  177. std::for_each(std::begin(e.Formants[VOWEL_A_INDEX]), std::end(e.Formants[VOWEL_A_INDEX]),
  178. std::mem_fn(&FormantFilter::clear));
  179. std::for_each(std::begin(e.Formants[VOWEL_B_INDEX]), std::end(e.Formants[VOWEL_B_INDEX]),
  180. std::mem_fn(&FormantFilter::clear));
  181. std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
  182. }
  183. }
  184. void VmorpherState::update(const ALCcontext *context, const EffectSlot *slot,
  185. const EffectProps *props, const EffectTarget target)
  186. {
  187. const ALCdevice *device{context->mDevice.get()};
  188. const float frequency{static_cast<float>(device->Frequency)};
  189. const float step{props->Vmorpher.Rate / frequency};
  190. mStep = fastf2u(clampf(step*WAVEFORM_FRACONE, 0.0f, float{WAVEFORM_FRACONE-1}));
  191. if(mStep == 0)
  192. mGetSamples = Oscillate<Half>;
  193. else if(props->Vmorpher.Waveform == VMorpherWaveform::Sinusoid)
  194. mGetSamples = Oscillate<Sin>;
  195. else if(props->Vmorpher.Waveform == VMorpherWaveform::Triangle)
  196. mGetSamples = Oscillate<Triangle>;
  197. else /*if(props->Vmorpher.Waveform == VMorpherWaveform::Sawtooth)*/
  198. mGetSamples = Oscillate<Saw>;
  199. const float pitchA{std::pow(2.0f,
  200. static_cast<float>(props->Vmorpher.PhonemeACoarseTuning) / 12.0f)};
  201. const float pitchB{std::pow(2.0f,
  202. static_cast<float>(props->Vmorpher.PhonemeBCoarseTuning) / 12.0f)};
  203. auto vowelA = getFiltersByPhoneme(props->Vmorpher.PhonemeA, frequency, pitchA);
  204. auto vowelB = getFiltersByPhoneme(props->Vmorpher.PhonemeB, frequency, pitchB);
  205. /* Copy the filter coefficients to the input channels. */
  206. for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
  207. {
  208. std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].Formants[VOWEL_A_INDEX]));
  209. std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].Formants[VOWEL_B_INDEX]));
  210. }
  211. mOutTarget = target.Main->Buffer;
  212. auto set_gains = [slot,target](auto &chan, al::span<const float,MaxAmbiChannels> coeffs)
  213. { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); };
  214. SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains);
  215. }
  216. void VmorpherState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
  217. {
  218. /* Following the EFX specification for a conformant implementation which describes
  219. * the effect as a pair of 4-band formant filters blended together using an LFO.
  220. */
  221. for(size_t base{0u};base < samplesToDo;)
  222. {
  223. const size_t td{minz(MAX_UPDATE_SAMPLES, samplesToDo-base)};
  224. mGetSamples(mLfo, mIndex, mStep, td);
  225. mIndex += static_cast<uint>(mStep * td);
  226. mIndex &= WAVEFORM_FRACMASK;
  227. auto chandata = std::begin(mChans);
  228. for(const auto &input : samplesIn)
  229. {
  230. auto& vowelA = chandata->Formants[VOWEL_A_INDEX];
  231. auto& vowelB = chandata->Formants[VOWEL_B_INDEX];
  232. /* Process first vowel. */
  233. std::fill_n(std::begin(mSampleBufferA), td, 0.0f);
  234. vowelA[0].process(&input[base], mSampleBufferA, td);
  235. vowelA[1].process(&input[base], mSampleBufferA, td);
  236. vowelA[2].process(&input[base], mSampleBufferA, td);
  237. vowelA[3].process(&input[base], mSampleBufferA, td);
  238. /* Process second vowel. */
  239. std::fill_n(std::begin(mSampleBufferB), td, 0.0f);
  240. vowelB[0].process(&input[base], mSampleBufferB, td);
  241. vowelB[1].process(&input[base], mSampleBufferB, td);
  242. vowelB[2].process(&input[base], mSampleBufferB, td);
  243. vowelB[3].process(&input[base], mSampleBufferB, td);
  244. alignas(16) float blended[MAX_UPDATE_SAMPLES];
  245. for(size_t i{0u};i < td;i++)
  246. blended[i] = lerp(mSampleBufferA[i], mSampleBufferB[i], mLfo[i]);
  247. /* Now, mix the processed sound data to the output. */
  248. MixSamples({blended, td}, samplesOut, chandata->CurrentGains, chandata->TargetGains,
  249. samplesToDo-base, base);
  250. ++chandata;
  251. }
  252. base += td;
  253. }
  254. }
  255. struct VmorpherStateFactory final : public EffectStateFactory {
  256. al::intrusive_ptr<EffectState> create() override
  257. { return al::intrusive_ptr<EffectState>{new VmorpherState{}}; }
  258. };
  259. } // namespace
  260. EffectStateFactory *VmorpherStateFactory_getFactory()
  261. {
  262. static VmorpherStateFactory VmorpherFactory{};
  263. return &VmorpherFactory;
  264. }