History

implemented basic DSP for Spectacle

Joel Matthys committed 12 years ago

Commit 4325a601 parent 0ecc72732 files · +3252 −1434Browse files

Showing the changes to Spectacle/Spectacle-dsp.cpp (1 of 32 files). Show all

Unified · Split

Spectacle/Spectacle-dsp.cpp+382−0
@@ -0,0 +1,382 @@
1+/*
2+ * Copyright (C) 2007 John Gibson
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License, version 2, as
6+ * published by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ */
13+
14+// derived from RTcmix SPECTACLE2.cpp, (C) John Gibson 2005.
15+
16+
17+#include "Spectacle-dsp.h"
18+#include <float.h>
19+#include <cstddef>
20+
21+//#define DEBUG
22+//#define PRINT_DELTIMES
23+//#define PRINT_DELTIME_CHANGES
24+
25+#if defined(PRINT_DELTIME_CHANGES) && !defined(PRINT_DELTIMES)
26+ #define PRINT_DELTIMES
27+#endif
28+
29+const float kMaxDelayTime = 20.0f;
30+#ifdef ANTI_DENORM
31+ const float kAntiDenormConstant = 1e-18f;
32+#endif
33+
34+
35+// ---------------------------------------------------------------- Spectacle_dsp --
36+Spectacle_dsp::Spectacle_dsp()
37+ : _maxdelsamps(0L), _maxdeltime(0.0f), _eqconst(0.0f), _deltimeconst(0.0f),
38+ _feedbackconst(0.0f), _delay_minfreq(-FLT_MAX), _delay_maxfreq(-FLT_MAX),
39+ _eqtable(NULL), _deltimetable(NULL), _feedbacktable(NULL),
40+ _delay_bin_groups(NULL), _delay_binmap_table(NULL), _delay_table_size(0)
41+{
42+#ifdef ANTI_DENORM
43+ _antidenorm_offset = kAntiDenormConstant;
44+#endif
45+}
46+
47+
48+// --------------------------------------------------------------- ~Spectacle_dsp --
49+Spectacle_dsp::~Spectacle_dsp()
50+{
51+ // NB: we don't own the EQ, delay time, and feedback tables.
52+
53+ for (int i = 0; i < _half_fftlen; i++) {
54+ if (_real_delay[i])
55+ delete _real_delay[i];
56+ if (_imag_delay[i])
57+ delete _imag_delay[i];
58+ }
59+
60+ delete [] _delay_bin_groups;
61+}
62+
63+
64+// --------------------------------------------------------------------- init --
65+// Invoke base init, and perform any initialization specific to this subclass.
66+// Return 0 if okay, -1 if not.
67+
68+int Spectacle_dsp::init(int fftlen, int windowlen, int overlap, float srate,
69+ float maxdeltime)
70+{
71+ if (SpectacleBase::init(fftlen, windowlen, overlap, srate) != 0)
72+ return -1;
73+
74+ // Compute maximum delay lag and create delay lines for FFT real and
75+ // imaginary values. Remember that these delays function at the decimation
76+ // rate, not at the audio rate, so the memory footprint is not as large
77+ // as you would expect -- about 44100 samples per second at fftlen=1024,
78+ // overlap=2 and SR=44100.
79+
80+ _maxdeltime = maxdeltime;
81+ _maxdelsamps = long(maxdeltime * get_srate() / float(_decimation) + 0.5);
82+
83+ for (int i = 0; i < _half_fftlen; i++) {
84+ Odelay *delay = new Odelay(_maxdelsamps);
85+ if (delay == NULL)
86+ goto bad_alloc;
87+ _real_delay[i] = delay;
88+ delay = new Odelay(_maxdelsamps);
89+ if (delay == NULL)
90+ goto bad_alloc;
91+ _imag_delay[i] = delay;
92+ }
93+
94+ _delay_bin_groups = new int [_half_fftlen];
95+
96+ set_delay_freqrange(0.0f, 0.0f);
97+
98+ return 0;
99+bad_alloc:
100+ //error("%s: Not enough memory for delay lines.", instname());
101+ return -1;
102+}
103+
104+
105+// -------------------------------------------------------------------- clear --
106+void Spectacle_dsp::clear()
107+{
108+ for (int i = 0; i < _half_fftlen; i++) {
109+ _real_delay[i]->clear();
110+ _imag_delay[i]->clear();
111+ }
112+ SpectacleBase::clear();
113+}
114+
115+
116+// ---------------------------------------------------------------- set_srate --
117+void Spectacle_dsp::set_srate(float srate)
118+{
119+ if (_delay_maxfreq == _nyquist)
120+ _delay_maxfreq = 0.0f;
121+ SpectacleBase::set_srate(srate);
122+ if (_delay_maxfreq == 0.0f) // nyquist might have changed
123+ _delay_maxfreq = _nyquist;
124+ set_maxdeltime(_maxdeltime);
125+ update_bin_groups(_delay_bin_groups, _delay_binmap_table,
126+ _delay_minfreq, _delay_maxfreq, _delay_table_size);
127+}
128+
129+
130+// ----------------------------------------------------------- set_maxdeltime --
131+// Reset delay lines to accommodate a maximum delay of <time> seconds at the
132+// current sampling rate. Assumes that caller constrains all delay times to
133+// fit this new maximum.
134+void Spectacle_dsp::set_maxdeltime(float time)
135+{
136+ _maxdeltime = time;
137+ _maxdelsamps = long(time * get_srate() / float(_decimation) + 0.5);
138+ for (int i = 0; i < _half_fftlen; i++) {
139+ _real_delay[i]->resize(_maxdelsamps);
140+ _imag_delay[i]->resize(_maxdelsamps);
141+ }
142+}
143+
144+
145+// -------------------------------------------------------------- set_eqtable --
146+void Spectacle_dsp::set_eqtable(float *table, int len)
147+{
148+ if (len == 0) { // no table set yet by caller
149+ _eqconst = 0.0;
150+ _eqtable = NULL;
151+ _control_table_size = 0;
152+ }
153+ else if (len == 1) {
154+ _eqconst = table[0];
155+ _eqtable = NULL;
156+ _control_table_size = 0;
157+ }
158+ else {
159+ _eqconst = 0.0;
160+ _eqtable = table;
161+ if (len != _control_table_size) {
162+ _control_table_size = len;
163+ update_bin_groups(_bin_groups, NULL, get_minfreq(), get_maxfreq(),
164+ _control_table_size);
165+ }
166+ }
167+}
168+
169+
170+// ------------------------------------------------------------- set_deltable --
171+void Spectacle_dsp::set_deltable(float *table, int len)
172+{
173+ if (len == 0) { // no table set yet by caller
174+ _deltimeconst = 0.0;
175+ _deltimetable = NULL;
176+ }
177+ else if (len == 1) {
178+ _deltimeconst = table[0];
179+ _deltimetable = NULL;
180+ // NB: don't set _delay_table_size to zero
181+ }
182+ else {
183+ _deltimeconst = 0.0;
184+ _deltimetable = table;
185+ if (len != _delay_table_size) {
186+ _delay_table_size = len;
187+ update_bin_groups(_delay_bin_groups, _delay_binmap_table,
188+ _delay_minfreq, _delay_maxfreq, _delay_table_size);
189+ }
190+ }
191+}
192+
193+
194+// ------------------------------------------------------------ set_feedtable --
195+void Spectacle_dsp::set_feedtable(float *table, int len)
196+{
197+ if (len == 0) { // no table set yet by caller
198+ _feedbackconst = 0.0;
199+ _feedbacktable = NULL;
200+ }
201+ else if (len == 1) {
202+ _feedbackconst = table[0];
203+ _feedbacktable = NULL;
204+ }
205+ else if (len == _delay_table_size) {
206+ _feedbackconst = 0.0;
207+ _feedbacktable = table;
208+ }
209+ // else don't do anything -- spectacle_fb_msg insures we never get here
210+}
211+
212+
213+// ------------------------------------------------------ set_delay_freqrange --
214+// Similar to set_freqrange in base class, but for _delay_bin_groups.
215+bool Spectacle_dsp::set_delay_freqrange(float min, float max)
216+{
217+ if (min != _delay_minfreq || max != _delay_maxfreq) {
218+ if (max == 0.0f)
219+ max = _nyquist;
220+ if (max < min)
221+ _fswap(&min, &max);
222+ if (max > _nyquist)
223+ max = _nyquist;
224+ _delay_maxfreq = max;
225+ _delay_minfreq = _fclamp(0.0f, min, max);
226+ update_bin_groups(_delay_bin_groups, _delay_binmap_table,
227+ _delay_minfreq, max, _delay_table_size);
228+ return true;
229+ }
230+ return false;
231+}
232+
233+
234+// --------------------------------------------------------- set_binmap_table --
235+// Set pointer to externally allocated table. Pass table=NULL for no table.
236+// <len> must be the same as the current control table size, or else we use
237+// no table, without warning -- responsibility of caller to handle this.
238+
239+void Spectacle_dsp::set_binmap_table(int *table, int len)
240+{
241+ if (table == NULL || len == 0 || len != _control_table_size)
242+ _binmaptable = NULL;
243+ else
244+ _binmaptable = table;
245+ update_bin_groups(_bin_groups, NULL, get_minfreq(), get_maxfreq(),
246+ _control_table_size);
247+}
248+
249+
250+// --------------------------------------------------- set_delay_binmap_table --
251+// Set pointer to externally allocated table. Pass table=NULL for no table.
252+// <len> must be the same as the current delay table size, or else we use
253+// no table, without warning -- responsibility of caller to handle this.
254+
255+void Spectacle_dsp::set_delay_binmap_table(int *table, int len)
256+{
257+ if (table == NULL || len == 0 || len != _delay_table_size)
258+ _delay_binmap_table = NULL;
259+ else
260+ _delay_binmap_table = table;
261+ update_bin_groups(_delay_bin_groups, _delay_binmap_table,
262+ _delay_minfreq, _delay_maxfreq, _delay_table_size);
263+}
264+
265+
266+// ---------------------------------------------------------- modify_analysis --
267+void Spectacle_dsp::modify_analysis(bool reading_input)
268+{
269+ DPRINT("modify_analysis: .....................");
270+
271+#ifdef PRINT_DELTIMES
272+ static float *prevdeltimes = NULL;
273+ if (prevdeltimes == NULL) {
274+ prevdeltimes = new float [_delay_table_size];
275+ post("\nmodify_analysis: delay times --------------------------");
276+ for (int i = 0; i < _delay_table_size; i++) {
277+ prevdeltimes[i] = _deltimetable[i];
278+ post("[%d] %f", i, _deltimetable[i]);
279+ }
280+ #ifdef PRINT_DELTIME_CHANGES
281+ post("\nmodify_analysis: delay time changes -------------------");
282+ #endif
283+ }
284+#endif
285+
286+ const bool posteq = get_posteq();
287+ float eq;
288+
289+ // NB: check EQ table size, rather than table pointer, to determine whether
290+ // we should use an EQ constant. Otherwise, there's a danger we could read
291+ // a null EQ table pointer, if set_eqtable called by non-perf thread.
292+ if (_control_table_size == 0)
293+ eq = _ampdb(_eqconst);
294+
295+ for (int i = 0; i < _half_fftlen; i++) {
296+ int index = i << 1;
297+
298+ if (_control_table_size > 0) {
299+ // EQ uses base class bin groups array.
300+ const int bg = _bin_groups[i];
301+ eq = _ampdb(_eqtable[bg]);
302+ }
303+
304+ float real, imag;
305+ if (reading_input) {
306+ if (posteq) {
307+ real = _fft_buf[index];
308+ imag = _fft_buf[index + 1];
309+ }
310+ else {
311+ real = _fft_buf[index] * eq;
312+ imag = _fft_buf[index + 1] * eq;
313+ }
314+ }
315+ else {
316+ real = 0.0f;
317+ imag = 0.0f;
318+ }
319+
320+ const int bg = _delay_bin_groups[i];
321+
322+ // NB: caller must assure that deltime is in range
323+ const float deltime = _deltimetable ? _deltimetable[bg] : _deltimeconst;
324+
325+#ifdef PRINT_DELTIME_CHANGES
326+ if (deltime != prevdeltimes[bg]) {
327+ post("[%d] %f", bg, deltime);
328+ prevdeltimes[bg] = deltime;
329+ }
330+#endif
331+
332+ if (deltime == 0.0f) {
333+ if (posteq) {
334+ _fft_buf[index] = real * eq;
335+ _fft_buf[index + 1] = imag * eq;
336+ }
337+ else {
338+ _fft_buf[index] = real;
339+ _fft_buf[index + 1] = imag;
340+ }
341+ }
342+ else {
343+ const long delsamps = long((deltime * get_srate()) + 0.5)
344+ / _decimation;
345+ const float newreal = _real_delay[i]->getsamp(delsamps);
346+ const float newimag = _imag_delay[i]->getsamp(delsamps);
347+ const float feedback = _feedbacktable ? _feedbacktable[bg]
348+ : _feedbackconst;
349+ if (feedback != 0.0) {
350+#ifdef ANTI_DENORM
351+ float fbsig = (newreal * feedback) + _antidenorm_offset;
352+ _real_delay[i]->putsamp(real + fbsig);
353+ fbsig = (newimag * feedback) + _antidenorm_offset;
354+ _imag_delay[i]->putsamp(imag + fbsig);
355+#else
356+ _real_delay[i]->putsamp(real + (newreal * feedback));
357+ _imag_delay[i]->putsamp(imag + (newimag * feedback));
358+#endif
359+ }
360+ else {
361+ _real_delay[i]->putsamp(real);
362+ _imag_delay[i]->putsamp(imag);
363+ }
364+ if (posteq) {
365+ _fft_buf[index] = newreal * eq;
366+ _fft_buf[index + 1] = newimag * eq;
367+ }
368+ else {
369+ _fft_buf[index] = newreal;
370+ _fft_buf[index + 1] = newimag;
371+ }
372+ }
373+ }
374+
375+ _fft_buf[1] = 0.0f; // clear Nyquist real value
376+
377+#ifdef ANTI_DENORM
378+ _antidenorm_offset = -_antidenorm_offset;
379+#endif
380+}
381+
382+

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t