HAAS Stereo Enhancer
[calf.git] / src / calf / multichorus.h
blobb86a5bd254850a14ffa92dfb279d14f6cf99d315
1 /* Calf DSP Library
2 * Multitap chorus class.
4 * Copyright (C) 2001-2007 Krzysztof Foltman
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02111-1307, USA.
21 #ifndef __CALF_MULTICHORUS_H
22 #define __CALF_MULTICHORUS_H
24 #include "audio_fx.h"
26 namespace dsp {
28 typedef fixed_point<unsigned int, 20> chorus_phase;
30 template<class T, uint32_t Voices>
31 class sine_multi_lfo
33 protected:
34 sine_table<int, 4096, 65535> sine;
36 public:
37 /// Current LFO phase
38 chorus_phase phase;
39 /// LFO phase increment
40 chorus_phase dphase;
41 /// LFO phase per-voice increment
42 chorus_phase vphase;
43 /// Current number of voices
44 uint32_t voices;
45 /// Current scale (output multiplier)
46 T scale;
47 /// Per-voice offset unit (the value that says how much the voices are offset with respect to each other in non-100% 'overlap' mode), scaled so that full range = 131072
48 int32_t voice_offset;
49 /// LFO Range scaling for non-100% overlap
50 uint32_t voice_depth;
51 public:
52 sine_multi_lfo()
54 phase = dphase = vphase = 0.0;
55 voice_offset = 0;
56 voice_depth = 1U << 31;
58 set_voices(Voices);
60 inline uint32_t get_voices() const
62 return voices;
64 inline void set_voices(uint32_t value)
66 voices = value;
67 // use sqrt, because some phases will cancel each other - so 1 / N is usually too low
68 scale = sqrt(1.0 / voices);
70 inline void set_overlap(float overlap)
72 // If we scale the delay amount so that full range of a single LFO is 0..1, all the overlapped LFOs will cover 0..range
73 // How it's calculated:
74 // 1. First voice is assumed to always cover the range of 0..1
75 // 2. Each remaining voice contributes an interval of a width = 1 - overlap, starting from the end of the interval of the previous voice
76 // Coverage = non-overlapped part of the LFO range in the 1st voice
77 float range = 1.f + (1.f - overlap) * (voices - 1);
78 float scaling = 1.f / range;
79 voice_offset = (int)(131072 * (1 - overlap) / range);
80 voice_depth = (unsigned int)((1U << 30) * 1.0 * scaling);
82 /// Get LFO value for given voice, returns a values in range of [-65536, 65535] (or close)
83 inline int get_value(uint32_t voice) const {
84 // find this voice's phase (= phase + voice * 360 degrees / number of voices)
85 chorus_phase voice_phase = phase + vphase * (int)voice;
86 // find table offset
87 unsigned int ipart = voice_phase.ipart();
88 // interpolate (use 14 bits of precision - because the table itself uses 17 bits and the result of multiplication must fit in int32_t)
89 // note, the result is still -65535 .. 65535, it's just interpolated
90 // it is never reaching -65536 - but that's acceptable
91 int intval = voice_phase.lerp_by_fract_int<int, 14, int>(sine.data[ipart], sine.data[ipart+1]);
92 // apply the voice offset/depth (rescale from -65535..65535 to appropriate voice's "band")
93 return -65535 + voice * voice_offset + ((voice_depth >> (30-13)) * (65536 + intval) >> 13);
95 inline void step() {
96 phase += dphase;
98 inline T get_scale() const {
99 return scale;
101 void reset() {
102 phase = 0.f;
107 * Multi-tap chorus without feedback.
108 * Perhaps MaxDelay should be a bit longer!
110 template<class T, class MultiLfo, class Postprocessor, int MaxDelay=4096>
111 class multichorus: public chorus_base
113 protected:
114 simple_delay<MaxDelay,T> delay;
115 public:
116 MultiLfo lfo;
117 Postprocessor post;
118 public:
119 multichorus() {
120 rate = 0.63f;
121 dry = 0.5f;
122 wet = 0.5f;
123 min_delay = 0.005f;
124 mod_depth = 0.0025f;
125 setup(44100);
127 void reset() {
128 delay.reset();
129 lfo.reset();
131 void set_rate(float rate) {
132 chorus_base::set_rate(rate);
133 lfo.dphase = dphase;
135 virtual void setup(int sample_rate) {
136 modulation_effect::setup(sample_rate);
137 delay.reset();
138 lfo.reset();
139 set_min_delay(get_min_delay());
140 set_mod_depth(get_mod_depth());
142 template<class OutIter, class InIter>
143 void process(OutIter buf_out, InIter buf_in, int nsamples) {
144 int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
145 int mdepth = mod_depth_samples;
146 // 1 sample peak-to-peak = mod_depth_samples of 32 (this scaling stuff is tricky and may - but shouldn't - be wrong)
147 // with 192 kHz sample rate, 1 ms = 192 samples, and the maximum 20 ms = 3840 samples (so, 4096 will be used)
148 // 3840 samples of mod depth = mdepth of 122880 (which multiplied by 65536 doesn't fit in int32_t)
149 // so, it will be right-shifted by 2, which gives it a safe range of 30720
150 // NB: calculation of mod_depth_samples (and multiply-by-32) is in chorus_base::set_mod_depth
151 mdepth = mdepth >> 2;
152 T scale = lfo.get_scale();
153 for (int i=0; i<nsamples; i++) {
154 phase += dphase;
156 float in = *buf_in++;
158 delay.put(in);
159 unsigned int nvoices = lfo.get_voices();
160 T out = 0.f;
161 // add up values from all voices, each voice tell its LFO phase and the buffer value is picked at that location
162 for (unsigned int v = 0; v < nvoices; v++)
164 int lfo_output = lfo.get_value(v);
165 // 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
166 int dv = mds + (mdepth * lfo_output >> (3 + 1));
167 int ifv = dv >> 16;
168 T fd; // signal from delay's output
169 delay.get_interp(fd, ifv, (dv & 0xFFFF)*(1.0/65536.0));
170 out += fd;
172 // apply the post filter
173 out = post.process(out);
174 T sdry = in * gs_dry.get();
175 T swet = out * gs_wet.get() * scale;
176 *buf_out++ = sdry + swet;
177 lfo.step();
179 post.sanitize();
181 float freq_gain(float freq, float sr) const
183 typedef std::complex<double> cfloat;
184 freq *= 2.0 * M_PI / sr;
185 cfloat z = 1.0 / exp(cfloat(0.0, freq)); // z^-1
186 cfloat h = 0.0;
187 int mds = min_delay_samples + mod_depth_samples * 1024 + 2*65536;
188 int mdepth = mod_depth_samples;
189 mdepth = mdepth >> 2;
190 T scale = lfo.get_scale();
191 unsigned int nvoices = lfo.get_voices();
192 for (unsigned int v = 0; v < nvoices; v++)
194 int lfo_output = lfo.get_value(v);
195 // 3 = log2(32 >> 2) + 1 because the LFO value is in range of [-65535, 65535] (17 bits)
196 int dv = mds + (mdepth * lfo_output >> (3 + 1));
197 int fldp = dv >> 16;
198 cfloat zn = std::pow(z, fldp); // z^-N
199 h += zn + (zn * z - zn) * cfloat(dv / 65536.0 - fldp);
201 // apply the post filter
202 h *= post.h_z(z);
203 // mix with dry signal
204 float v = std::abs(cfloat(gs_dry.get_last()) + cfloat(scale * gs_wet.get_last()) * h);
205 return v;
211 #endif