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.

242 lines
7.6KB

  1. // Copyright 2016 Olivier Gillet.
  2. //
  3. // Author: Olivier Gillet (ol.gillet@gmail.com)
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. // See http://creativecommons.org/licenses/MIT/ for more information.
  24. //
  25. // -----------------------------------------------------------------------------
  26. //
  27. // Main synthesis voice.
  28. #include "plaits/dsp/voice.h"
  29. namespace plaits {
  30. using namespace std;
  31. using namespace stmlib;
  32. void Voice::Init(BufferAllocator* allocator) {
  33. engines_.Init();
  34. engines_.RegisterInstance(&virtual_analog_engine_, false, 0.8f, 0.8f);
  35. engines_.RegisterInstance(&waveshaping_engine_, false, 0.7f, 0.6f);
  36. engines_.RegisterInstance(&fm_engine_, false, 0.6f, 0.6f);
  37. engines_.RegisterInstance(&grain_engine_, false, 0.7f, 0.6f);
  38. engines_.RegisterInstance(&additive_engine_, false, 0.8f, 0.8f);
  39. engines_.RegisterInstance(&wavetable_engine_, false, 0.6f, 0.6f);
  40. engines_.RegisterInstance(&chord_engine_, false, 0.8f, 0.8f);
  41. engines_.RegisterInstance(&speech_engine_, false, -0.7f, 0.8f);
  42. engines_.RegisterInstance(&swarm_engine_, false, -3.0f, 1.0f);
  43. engines_.RegisterInstance(&noise_engine_, false, -1.0f, -1.0f);
  44. engines_.RegisterInstance(&particle_engine_, false, -2.0f, 1.0f);
  45. engines_.RegisterInstance(&string_engine_, true, -1.0f, 0.8f);
  46. engines_.RegisterInstance(&modal_engine_, true, -1.0f, 0.8f);
  47. engines_.RegisterInstance(&bass_drum_engine_, true, 0.8f, 0.8f);
  48. engines_.RegisterInstance(&snare_drum_engine_, true, 0.8f, 0.8f);
  49. engines_.RegisterInstance(&hi_hat_engine_, true, 0.8f, 0.8f);
  50. for (int i = 0; i < engines_.size(); ++i) {
  51. // All engines will share the same RAM space.
  52. allocator->Free();
  53. engines_.get(i)->Init(allocator);
  54. }
  55. engine_quantizer_.Init();
  56. previous_engine_index_ = -1;
  57. engine_cv_ = 0.0f;
  58. out_post_processor_.Init();
  59. aux_post_processor_.Init();
  60. decay_envelope_.Init();
  61. lpg_envelope_.Init();
  62. trigger_state_ = false;
  63. previous_note_ = 0.0f;
  64. trigger_delay_.Init(trigger_delay_line_);
  65. }
  66. void Voice::Render(
  67. const Patch& patch,
  68. const Modulations& modulations,
  69. Frame* frames,
  70. size_t size) {
  71. // Trigger, LPG, internal envelope.
  72. // Delay trigger by 1ms to deal with sequencers or MIDI interfaces whose
  73. // CV out lags behind the GATE out.
  74. trigger_delay_.Write(modulations.trigger);
  75. float trigger_value = trigger_delay_.Read(kTriggerDelay);
  76. bool previous_trigger_state = trigger_state_;
  77. if (!previous_trigger_state) {
  78. if (trigger_value > 0.3f) {
  79. trigger_state_ = true;
  80. if (!modulations.level_patched) {
  81. lpg_envelope_.Trigger();
  82. }
  83. decay_envelope_.Trigger();
  84. engine_cv_ = modulations.engine;
  85. }
  86. } else {
  87. if (trigger_value < 0.1f) {
  88. trigger_state_ = false;
  89. }
  90. }
  91. if (!modulations.trigger_patched) {
  92. engine_cv_ = modulations.engine;
  93. }
  94. // Engine selection.
  95. int engine_index = engine_quantizer_.Process(
  96. patch.engine,
  97. engine_cv_,
  98. engines_.size(),
  99. 0.25f);
  100. Engine* e = engines_.get(engine_index);
  101. if (engine_index != previous_engine_index_) {
  102. e->Reset();
  103. out_post_processor_.Reset();
  104. previous_engine_index_ = engine_index;
  105. }
  106. EngineParameters p;
  107. bool rising_edge = trigger_state_ && !previous_trigger_state;
  108. float note = (modulations.note + previous_note_) * 0.5f;
  109. previous_note_ = modulations.note;
  110. const PostProcessingSettings& pp_s = e->post_processing_settings;
  111. if (modulations.trigger_patched) {
  112. p.trigger = rising_edge ? TRIGGER_RISING_EDGE : TRIGGER_LOW;
  113. } else {
  114. p.trigger = TRIGGER_UNPATCHED;
  115. }
  116. const float short_decay = (200.0f * kBlockSize) / kSampleRate *
  117. SemitonesToRatio(-96.0f * patch.decay);
  118. decay_envelope_.Process(short_decay * 2.0f);
  119. const float compressed_level = max(
  120. 1.3f * modulations.level / (0.3f + fabsf(modulations.level)),
  121. 0.0f);
  122. p.accent = modulations.level_patched ? compressed_level : 0.8f;
  123. bool use_internal_envelope = modulations.trigger_patched;
  124. // Actual synthesis parameters.
  125. p.harmonics = patch.harmonics + modulations.harmonics;
  126. CONSTRAIN(p.harmonics, 0.0f, 1.0f);
  127. float internal_envelope_amplitude = 1.0f;
  128. if (engine_index == 7) {
  129. internal_envelope_amplitude = 2.0f - p.harmonics * 6.0f;
  130. CONSTRAIN(internal_envelope_amplitude, 0.0f, 1.0f);
  131. speech_engine_.set_prosody_amount(
  132. !modulations.trigger_patched || modulations.frequency_patched ?
  133. 0.0f : patch.frequency_modulation_amount);
  134. speech_engine_.set_speed(
  135. !modulations.trigger_patched || modulations.morph_patched ?
  136. 0.0f : patch.morph_modulation_amount);
  137. }
  138. p.note = ApplyModulations(
  139. patch.note + note,
  140. patch.frequency_modulation_amount,
  141. modulations.frequency_patched,
  142. modulations.frequency,
  143. use_internal_envelope,
  144. internal_envelope_amplitude * \
  145. decay_envelope_.value() * decay_envelope_.value() * 48.0f,
  146. 1.0f,
  147. -119.0f,
  148. 120.0f);
  149. p.timbre = ApplyModulations(
  150. patch.timbre,
  151. patch.timbre_modulation_amount,
  152. modulations.timbre_patched,
  153. modulations.timbre,
  154. use_internal_envelope,
  155. decay_envelope_.value(),
  156. 0.0f,
  157. 0.0f,
  158. 1.0f);
  159. p.morph = ApplyModulations(
  160. patch.morph,
  161. patch.morph_modulation_amount,
  162. modulations.morph_patched,
  163. modulations.morph,
  164. use_internal_envelope,
  165. internal_envelope_amplitude * decay_envelope_.value(),
  166. 0.0f,
  167. 0.0f,
  168. 1.0f);
  169. bool already_enveloped = pp_s.already_enveloped;
  170. e->Render(p, out_buffer_, aux_buffer_, size, &already_enveloped);
  171. bool lpg_bypass = already_enveloped || \
  172. (!modulations.level_patched && !modulations.trigger_patched);
  173. // Compute LPG parameters.
  174. if (!lpg_bypass) {
  175. const float hf = patch.lpg_colour;
  176. const float decay_tail = (20.0f * kBlockSize) / kSampleRate *
  177. SemitonesToRatio(-72.0f * patch.decay + 12.0f * hf) - short_decay;
  178. if (modulations.level_patched) {
  179. lpg_envelope_.ProcessLP(compressed_level, short_decay, decay_tail, hf);
  180. } else {
  181. const float attack = NoteToFrequency(p.note) * float(kBlockSize) * 2.0f;
  182. lpg_envelope_.ProcessPing(attack, short_decay, decay_tail, hf);
  183. }
  184. }
  185. out_post_processor_.Process(
  186. pp_s.out_gain,
  187. lpg_bypass,
  188. lpg_envelope_.gain(),
  189. lpg_envelope_.frequency(),
  190. lpg_envelope_.hf_bleed(),
  191. out_buffer_,
  192. &frames->out,
  193. size,
  194. 2);
  195. aux_post_processor_.Process(
  196. pp_s.aux_gain,
  197. lpg_bypass,
  198. lpg_envelope_.gain(),
  199. lpg_envelope_.frequency(),
  200. lpg_envelope_.hf_bleed(),
  201. aux_buffer_,
  202. &frames->aux,
  203. size,
  204. 2);
  205. }
  206. } // namespace plaits