1 /*
2 VCO.cc
3
4 Copyright 2004-7 Tim Goetze <tim@quitte.de>
5
6 http://quitte.de/dsp/
7
8 an oversampled triangle/saw/square oscillator, and a combination of two
9 such oscillators with hard sync.
10
11 TODO: optimize for phase clamping like this:
12 phi -= floor (phi);
13 */
14 /*
15 This program is free software; you can redistribute it and/or
16 modify it under the terms of the GNU General Public License
17 as published by the Free Software Foundation; either version 2
18 of the License, or (at your option) any later version.
19
20 This program is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
24
25 You should have received a copy of the GNU General Public License
26 along with this program; if not, write to the Free Software
27 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 02111-1307, USA or point your web browser to http://www.gnu.org.
29 */
30
31 #include "basics.h"
32
33 #include "VCO.h"
34 #include "Descriptor.h"
35
36 void
init()37 VCOs::init()
38 {
39 /* going a fair bit lower than nominal with fc because the filter
40 * rolloff is not as steep as we might like it to be. */
41 double f = .5 * M_PI / OVERSAMPLE;
42
43 /* construct the downsampler filter kernel */
44 DSP::sinc (f, down.c, FIR_SIZE);
45 DSP::kaiser<DSP::apply_window> (down.c, FIR_SIZE, 6.4);
46
47 /* normalize downsampler filter gain */
48 double s = 0;
49 for (int i = 0; i < down.n; ++i)
50 s += down.c[i];
51
52 /* scale downsampler kernel */
53 s = 1 / s;
54 for (int i = 0; i < down.n; ++i)
55 down.c[i] *= s;
56 }
57
58 template <sample_func_t F>
59 void
one_cycle(int frames)60 VCOs::one_cycle (int frames)
61 {
62 vco.set_f (getport(0), OVERSAMPLE * fs);
63 vco.set_saw_square (getport(1), getport(2));
64
65 double g = (gain == *ports[3]) ?
66 1 : pow (getport(3) / gain, 1. / (double) frames);
67
68 sample_t * d = ports[4];
69
70 for (int i = 0; i < frames; ++i)
71 {
72 F (d, i, gain * down.process (vco.get()), adding_gain);
73
74 for (int o = 1; o < OVERSAMPLE; ++o)
75 down.store (vco.get());
76
77 gain *= g;
78 }
79
80 gain = getport(3);
81 }
82
83 /* //////////////////////////////////////////////////////////////////////// */
84
85 PortInfo
86 VCOs::port_info [] =
87 {
88 {
89 "f",
90 INPUT | CONTROL,
91 {BOUNDED | LOG | DEFAULT_100, 1, 5751}
92 }, {
93 "tri .. saw",
94 INPUT | CONTROL,
95 {BOUNDED | DEFAULT_0, 0, 1}
96 }, {
97 "~ .. square",
98 INPUT | CONTROL,
99 {BOUNDED | DEFAULT_0, 0, 1}
100 }, {
101 "volume",
102 INPUT | CONTROL,
103 {BOUNDED | DEFAULT_MID, MIN_GAIN, 1}
104 }, {
105 "out",
106 OUTPUT | AUDIO,
107 {0}
108 }
109 };
110
111 template <> void
setup()112 Descriptor<VCOs>::setup()
113 {
114 UniqueID = 1783;
115 Label = "VCOs";
116 Properties = HARD_RT;
117
118 Name = CAPS "VCOs - Virtual 'analogue' oscillator";
119 Maker = "Tim Goetze <tim@quitte.de>";
120 Copyright = "GPL, 2004-7";
121
122 /* fill port info and vtable */
123 autogen();
124 }
125
126 /* //////////////////////////////////////////////////////////////////////// */
127
128 void
init()129 VCOd::init()
130 {
131 /* going a fair bit lower than nominal with fc because the filter
132 * rolloff is not as steep as we might like it to be. */
133 double f = .5 * M_PI / OVERSAMPLE;
134
135 /* construct the downsampler filter kernel */
136 DSP::sinc (f, down.c, FIR_SIZE);
137 DSP::kaiser<DSP::apply_window> (down.c, FIR_SIZE, 6.4);
138
139 /* normalize downsampler filter gain */
140 double s = 0;
141 for (int i = 0; i < down.n; ++i)
142 s += down.c[i];
143
144 /* scale downsampler kernel */
145 s = 1 / s;
146 for (int i = 0; i < down.n; ++i)
147 down.c[i] *= s;
148 }
149
150 template <sample_func_t F>
151 void
one_cycle(int frames)152 VCOd::one_cycle (int frames)
153 {
154 vco.set_f (getport(0), OVERSAMPLE * fs, getport(5));
155
156 vco.vco[0].set_saw_square (getport(1), getport(2));
157 vco.vco[1].set_saw_square (getport(3), getport(4));
158
159 vco.set_sync (getport(6));
160 vco.set_blend (getport(7));
161
162 double g = (gain == *ports[8]) ?
163 1 : pow (getport(8) / gain, 1. / (double) frames);
164
165 sample_t * d = ports[9];
166
167 for (int i = 0; i < frames; ++i)
168 {
169 F (d, i, gain * down.process (vco.get()), adding_gain);
170
171 for (int o = 1; o < OVERSAMPLE; ++o)
172 down.store (vco.get());
173
174 gain *= g;
175 }
176
177 gain = getport(8);
178 }
179
180 /* //////////////////////////////////////////////////////////////////////// */
181
182 PortInfo
183 VCOd::port_info [] =
184 {
185 {
186 "f",
187 INPUT | CONTROL,
188 {BOUNDED | LOG | DEFAULT_100, 1, 5751}
189 }, {
190 "1: tri .. saw",
191 INPUT | CONTROL,
192 {BOUNDED | DEFAULT_0, 0, 1}
193 }, {
194 "1: ~ .. square",
195 INPUT | CONTROL,
196 {BOUNDED | DEFAULT_0, 0, 1}
197 }, {
198 "2: tri .. saw",
199 INPUT | CONTROL,
200 {BOUNDED | DEFAULT_0, 0, 1}
201 }, {
202 "2: ~ .. square",
203 INPUT | CONTROL,
204 {BOUNDED | DEFAULT_0, 0, 1}
205 }, {
206 "2: tune",
207 INPUT | CONTROL,
208 {BOUNDED | DEFAULT_0, -12, 12}
209 }, {
210 "sync",
211 INPUT | CONTROL,
212 {BOUNDED | DEFAULT_0, 0, 1}
213 }, {
214 "blend",
215 INPUT | CONTROL,
216 {BOUNDED | DEFAULT_HIGH, -1, 1}
217 }, {
218 "volume",
219 INPUT | CONTROL,
220 {BOUNDED | DEFAULT_MID, MIN_GAIN, 1}
221 }, {
222 "out",
223 OUTPUT | AUDIO,
224 {0}
225 }
226 };
227
228 template <> void
setup()229 Descriptor<VCOd>::setup()
230 {
231 UniqueID = 1784;
232 Label = "VCOd";
233 Properties = HARD_RT;
234
235 Name = CAPS "VCOd - Double VCO with detune and hard sync options";
236 Maker = "Tim Goetze <tim@quitte.de>";
237 Copyright = "GPL, 2004-7";
238
239 /* fill port info and vtable */
240 autogen();
241 }
242
243