1 // Copyright 2016 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.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 
29 #ifndef PLAITS_DSP_VOICE_H_
30 #define PLAITS_DSP_VOICE_H_
31 
32 #include "stmlib/stmlib.h"
33 
34 #include "stmlib/dsp/filter.h"
35 #include "stmlib/dsp/limiter.h"
36 #include "stmlib/utils/buffer_allocator.h"
37 
38 #include "plaits/dsp/engine/additive_engine.h"
39 #include "plaits/dsp/engine/bass_drum_engine.h"
40 #include "plaits/dsp/engine/chord_engine.h"
41 #include "plaits/dsp/engine/engine.h"
42 #include "plaits/dsp/engine/fm_engine.h"
43 #include "plaits/dsp/engine/grain_engine.h"
44 #include "plaits/dsp/engine/hi_hat_engine.h"
45 #include "plaits/dsp/engine/modal_engine.h"
46 #include "plaits/dsp/engine/noise_engine.h"
47 #include "plaits/dsp/engine/particle_engine.h"
48 #include "plaits/dsp/engine/snare_drum_engine.h"
49 #include "plaits/dsp/engine/speech_engine.h"
50 #include "plaits/dsp/engine/string_engine.h"
51 #include "plaits/dsp/engine/swarm_engine.h"
52 #include "plaits/dsp/engine/virtual_analog_engine.h"
53 #include "plaits/dsp/engine/waveshaping_engine.h"
54 #include "plaits/dsp/engine/wavetable_engine.h"
55 
56 #include "plaits/dsp/envelope.h"
57 
58 #include "plaits/dsp/fx/low_pass_gate.h"
59 
60 namespace plaits {
61 
62 const int kMaxEngines = 16;
63 const int kMaxTriggerDelay = 8;
64 const int kTriggerDelay = 5;
65 
66 class ChannelPostProcessor {
67  public:
ChannelPostProcessor()68   ChannelPostProcessor() { }
~ChannelPostProcessor()69   ~ChannelPostProcessor() { }
70 
Init()71   void Init() {
72     lpg_.Init();
73     Reset();
74   }
75 
Reset()76   void Reset() {
77     limiter_.Init();
78   }
79 
Process(float gain,bool bypass_lpg,float low_pass_gate_gain,float low_pass_gate_frequency,float low_pass_gate_hf_bleed,float * in,short * out,size_t size,size_t stride)80   void Process(
81       float gain,
82       bool bypass_lpg,
83       float low_pass_gate_gain,
84       float low_pass_gate_frequency,
85       float low_pass_gate_hf_bleed,
86       float* in,
87       short* out,
88       size_t size,
89       size_t stride) {
90     if (gain < 0.0f) {
91       limiter_.Process(-gain, in, size);
92     }
93     const float post_gain = (gain < 0.0f ? 1.0f : gain) * -32767.0f;
94     if (!bypass_lpg) {
95       lpg_.Process(
96           post_gain * low_pass_gate_gain,
97           low_pass_gate_frequency,
98           low_pass_gate_hf_bleed,
99           in,
100           out,
101           size,
102           stride);
103     } else {
104       while (size--) {
105         *out = stmlib::Clip16(1 + static_cast<int32_t>(*in++ * post_gain));
106         out += stride;
107       }
108     }
109   }
110 
111  private:
112   stmlib::Limiter limiter_;
113   LowPassGate lpg_;
114 
115   DISALLOW_COPY_AND_ASSIGN(ChannelPostProcessor);
116 };
117 
118 struct Patch {
119   float note;
120   float harmonics;
121   float timbre;
122   float morph;
123   float frequency_modulation_amount;
124   float timbre_modulation_amount;
125   float morph_modulation_amount;
126 
127   int engine;
128   float decay;
129   float lpg_colour;
130 };
131 
132 struct Modulations {
133   float engine;
134   float note;
135   float frequency;
136   float harmonics;
137   float timbre;
138   float morph;
139   float trigger;
140   float level;
141 
142   bool frequency_patched;
143   bool timbre_patched;
144   bool morph_patched;
145   bool trigger_patched;
146   bool level_patched;
147 };
148 
149 class Voice {
150  public:
Voice()151   Voice() { }
~Voice()152   ~Voice() { }
153 
154   struct Frame {
155     short out;
156     short aux;
157   };
158 
159   void Init(stmlib::BufferAllocator* allocator);
160   void Render(
161       const Patch& patch,
162       const Modulations& modulations,
163       Frame* frames,
164       size_t size);
active_engine()165   inline int active_engine() const { return previous_engine_index_; }
166 
167  private:
168   void ComputeDecayParameters(const Patch& settings);
169 
ApplyModulations(float base_value,float modulation_amount,bool use_external_modulation,float external_modulation,bool use_internal_envelope,float envelope,float default_internal_modulation,float minimum_value,float maximum_value)170   inline float ApplyModulations(
171       float base_value,
172       float modulation_amount,
173       bool use_external_modulation,
174       float external_modulation,
175       bool use_internal_envelope,
176       float envelope,
177       float default_internal_modulation,
178       float minimum_value,
179       float maximum_value) {
180     float value = base_value;
181     modulation_amount *= std::max(fabsf(modulation_amount) - 0.05f, 0.05f);
182     modulation_amount *= 1.05f;
183 
184     float modulation = use_external_modulation
185         ? external_modulation
186         : (use_internal_envelope ? envelope : default_internal_modulation);
187     value += modulation_amount * modulation;
188     CONSTRAIN(value, minimum_value, maximum_value);
189     return value;
190   }
191 
192   AdditiveEngine additive_engine_;
193   BassDrumEngine bass_drum_engine_;
194   ChordEngine chord_engine_;
195   FMEngine fm_engine_;
196   GrainEngine grain_engine_;
197   HiHatEngine hi_hat_engine_;
198   ModalEngine modal_engine_;
199   NoiseEngine noise_engine_;
200   ParticleEngine particle_engine_;
201   SnareDrumEngine snare_drum_engine_;
202   SpeechEngine speech_engine_;
203   StringEngine string_engine_;
204   SwarmEngine swarm_engine_;
205   VirtualAnalogEngine virtual_analog_engine_;
206   WaveshapingEngine waveshaping_engine_;
207   WavetableEngine wavetable_engine_;
208 
209   stmlib::HysteresisQuantizer engine_quantizer_;
210 
211   int previous_engine_index_;
212   float engine_cv_;
213 
214   float previous_note_;
215   bool trigger_state_;
216 
217   DecayEnvelope decay_envelope_;
218   LPGEnvelope lpg_envelope_;
219 
220   float trigger_delay_line_[kMaxTriggerDelay];
221   DelayLine<float, kMaxTriggerDelay> trigger_delay_;
222 
223   ChannelPostProcessor out_post_processor_;
224   ChannelPostProcessor aux_post_processor_;
225 
226   EngineRegistry<kMaxEngines> engines_;
227 
228   float out_buffer_[kMaxBlockSize];
229   float aux_buffer_[kMaxBlockSize];
230 
231   DISALLOW_COPY_AND_ASSIGN(Voice);
232 };
233 
234 }  // namespace plaits
235 
236 #endif  // PLAITS_DSP_VOICE_H_
237