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