Reverse Delay: wet/dry knob
[calf.git] / src / analyzer.cpp
blob1a84863fc25d0b2ba49eab142b802cbab9e8f0ce
1 /* Calf Analyzer FFT Library
2 * Copyright (C) 2007-2013 Krzysztof Foltman, Markus Schmidt,
3 * Christian Holschuh and others
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
21 #include <cairo/cairo.h>
22 #include <limits.h>
23 #include <memory.h>
24 #include <math.h>
25 #include <fftw3.h>
26 #include <calf/giface.h>
27 #include <calf/analyzer.h>
28 #include <calf/modules_dev.h>
29 #include <sys/time.h>
30 #include <calf/utils.h>
32 using namespace dsp;
33 using namespace calf_plugins;
35 #define sinc(x) (!x) ? 1 : sin(M_PI * x)/(M_PI * x);
36 #define RGBAtoINT(r, g, b, a) ((uint32_t)(r * 255) << 24) + ((uint32_t)(g * 255) << 16) + ((uint32_t)(b * 255) << 8) + (uint32_t)(a * 255)
38 analyzer::analyzer() {
39 _accuracy = -1;
40 _acc = -1;
41 _scale = -1;
42 _mode = -1;
43 _post = -1;
44 _hold = -1;
45 _smooth = -1;
46 _resolution = -1.f;
47 _offset = -1.f;
48 _freeze = -1;
49 _view = -1;
50 _windowing = -1;
51 _speed = -1;
52 fpos = 0;
53 _draw_upper = 0;
54 sanitize = true;
55 recreate_plan = true;
57 spline_buffer = (int*) calloc(200, sizeof(int));
59 fft_buffer = (float*) calloc(max_fft_buffer_size, sizeof(float));
61 fft_inL = (float*) calloc(max_fft_cache_size, sizeof(float));
62 fft_outL = (float*) calloc(max_fft_cache_size, sizeof(float));
63 fft_inR = (float*) calloc(max_fft_cache_size, sizeof(float));
64 fft_outR = (float*) calloc(max_fft_cache_size, sizeof(float));
66 fft_smoothL = (float*) calloc(max_fft_cache_size, sizeof(float));
67 fft_smoothR = (float*) calloc(max_fft_cache_size, sizeof(float));
69 fft_deltaL = (float*) calloc(max_fft_cache_size, sizeof(float));
70 fft_deltaR = (float*) calloc(max_fft_cache_size, sizeof(float));
72 fft_holdL = (float*) calloc(max_fft_cache_size, sizeof(float));
73 fft_holdR = (float*) calloc(max_fft_cache_size, sizeof(float));
75 fft_freezeL = (float*) calloc(max_fft_cache_size, sizeof(float));
76 fft_freezeR = (float*) calloc(max_fft_cache_size, sizeof(float));
78 fft_plan = NULL;
80 analyzer_phase_drawn = 0;
82 analyzer::~analyzer()
84 free(fft_freezeR);
85 free(fft_freezeL);
86 free(fft_holdR);
87 free(fft_holdL);
88 free(fft_deltaR);
89 free(fft_deltaL);
90 free(fft_smoothR);
91 free(fft_smoothL);
92 free(fft_outR);
93 free(fft_outL);
94 free(fft_inR);
95 free(fft_inL);
96 free(spline_buffer);
97 if (fft_plan) {
98 fftwf_destroy_plan(fft_plan);
99 fft_plan = NULL;
102 void analyzer::set_sample_rate(uint32_t sr) {
103 srate = sr;
106 void analyzer::set_params(float resolution, float offset, int accuracy, int hold, int smoothing, int mode, int scale, int post, int speed, int windowing, int view, int freeze)
108 _speed = speed;
109 _windowing = windowing;
110 _freeze = freeze;
111 _view = view;
113 if(accuracy != _acc) {
114 _accuracy = 1 << (7 + (int)accuracy);
115 _acc = accuracy;
116 recreate_plan = true;
118 if(hold != _hold) {
119 _hold = hold;
120 sanitize = true;
122 if(smoothing != _smooth) {
123 _smooth = smoothing;
124 sanitize = true;
126 if (mode != _mode) {
127 _mode = mode;
128 sanitize = true;
129 redraw_graph = true;
131 if(scale != _scale) {
132 _scale = scale;
133 sanitize = true;
135 if(post != _post) {
136 _post = post;
137 sanitize = true;
139 if(resolution != _resolution || offset != _offset) {
140 _resolution = resolution;
141 _offset = offset;
142 redraw_graph = true;
145 void analyzer::process(float L, float R) {
146 fft_buffer[fpos] = L;
147 fft_buffer[fpos + 1] = R;
148 fpos += 2;
149 fpos %= (max_fft_buffer_size - 2);
152 bool analyzer::do_fft(int subindex, int points) const
154 if (recreate_plan) {
155 // recreate fftw plan
156 if (fft_plan) fftwf_destroy_plan (fft_plan);
157 //fft_plan = rfftw_create_plan(_accuracy, FFTW_FORWARD, 0);
158 fft_plan = fftwf_plan_r2r_1d(_accuracy, NULL, NULL, FFTW_R2HC, FFTW_ESTIMATE);
159 lintrans = -1;
160 recreate_plan = false;
161 sanitize = true;
163 if (sanitize) {
164 // null the overall buffer
165 dsp::zero(fft_inL, max_fft_cache_size);
166 dsp::zero(fft_inR, max_fft_cache_size);
167 dsp::zero(fft_outL, max_fft_cache_size);
168 dsp::zero(fft_outR, max_fft_cache_size);
169 dsp::zero(fft_holdL, max_fft_cache_size);
170 dsp::zero(fft_holdR, max_fft_cache_size);
171 dsp::zero(fft_smoothL, max_fft_cache_size);
172 dsp::zero(fft_smoothR, max_fft_cache_size);
173 dsp::zero(fft_deltaL, max_fft_cache_size);
174 dsp::zero(fft_deltaR, max_fft_cache_size);
175 dsp::zero(spline_buffer, 200);
176 analyzer_phase_drawn = 0;
177 sanitize = false;
180 bool fftdone = false; // if fft was renewed, this one is set to true
181 int __speed = 16 - (int)_speed;
182 if(_mode == 5 and _smooth) {
183 // there's no falling for difference mode, only smoothing
184 _smooth = 2;
186 if(_mode > 5 and _mode < 11) {
187 // there's no smoothing for spectralizer mode
188 //_smooth = 0;
191 if(subindex == 0) {
192 // #####################################################################
193 // We are doing FFT here, so we first have to setup fft-buffers from
194 // the main buffer and we use this cycle for filling other buffers
195 // like smoothing, delta and hold
196 // #####################################################################
197 if(!((int)analyzer_phase_drawn % __speed)) {
198 // seems we have to do a fft, so let's read the latest data from the
199 // buffer to send it to fft afterwards
200 // we want to remember old fft_out values for smoothing as well
201 // and we fill the hold buffer in this (extra) cycle
202 for(int i = 0; i < _accuracy; i++) {
203 // go to the right position back in time according to accuracy
204 // settings and cycling in the main buffer
205 int _fpos = (fpos - _accuracy * 2 \
206 + (i * 2)) % max_fft_buffer_size;
207 if(_fpos < 0)
208 _fpos = max_fft_buffer_size + _fpos;
209 float L = fft_buffer[_fpos];
210 float R = fft_buffer[_fpos + 1];
211 float win = 0.54 - 0.46 * cos(2 * M_PI * i / _accuracy);
212 L *= win;
213 R *= win;
215 // #######################################
216 // Do some windowing functions on the
217 // buffer
218 // #######################################
219 int _m = 2;
220 float _f = 1.f;
221 float _a, a0, a1, a2, a3;
222 switch(_windowing) {
223 case 0:
224 default:
225 // Linear
226 _f = 1.f;
227 break;
228 case 1:
229 // Hamming
230 _f = 0.54 + 0.46 * cos(2 * M_PI * (i - 2 / points));
231 break;
232 case 2:
233 // von Hann
234 _f = 0.5 * (1 + cos(2 * M_PI * (i - 2 / points)));
235 break;
236 case 3:
237 // Blackman
238 _a = 0.16;
239 a0 = 1.f - _a / 2.f;
240 a1 = 0.5;
241 a2 = _a / 2.f;
242 _f = a0 + a1 * cos((2.f * M_PI * i) / points - 1) + \
243 a2 * cos((4.f * M_PI * i) / points - 1);
244 break;
245 case 4:
246 // Blackman-Harris
247 a0 = 0.35875;
248 a1 = 0.48829;
249 a2 = 0.14128;
250 a3 = 0.01168;
251 _f = a0 - a1 * cos((2.f * M_PI * i) / points - 1) + \
252 a2 * cos((4.f * M_PI * i) / points - 1) - \
253 a3 * cos((6.f * M_PI * i) / points - 1);
254 break;
255 case 5:
256 // Blackman-Nuttall
257 a0 = 0.3653819;
258 a1 = 0.4891775;
259 a2 = 0.1365995;
260 a3 = 0.0106411;
261 _f = a0 - a1 * cos((2.f * M_PI * i) / points - 1) + \
262 a2 * cos((4.f * M_PI * i) / points - 1) - \
263 a3 * cos((6.f * M_PI * i) / points - 1);
264 break;
265 case 6:
266 // Sine
267 _f = sin((M_PI * i) / (points - 1));
268 break;
269 case 7:
270 // Lanczos
271 _f = sinc((2.f * i) / (points - 1) - 1);
272 break;
273 case 8:
274 // Gauß
275 _a = 2.718281828459045;
276 _f = pow(_a, -0.5f * pow((i - (points - 1) / 2) / (0.4 * (points - 1) / 2.f), 2));
277 break;
278 case 9:
279 // Bartlett
280 _f = (2.f / (points - 1)) * (((points - 1) / 2.f) - \
281 fabs(i - ((points - 1) / 2.f)));
282 break;
283 case 10:
284 // Triangular
285 _f = (2.f / points) * ((2.f / points) - fabs(i - ((points - 1) / 2.f)));
286 break;
287 case 11:
288 // Bartlett-Hann
289 a0 = 0.62;
290 a1 = 0.48;
291 a2 = 0.38;
292 _f = a0 - a1 * fabs((i / (points - 1)) - 0.5) - \
293 a2 * cos((2 * M_PI * i) / (points - 1));
294 break;
296 L *= _f;
297 if(_mode > _m)
298 R *= _f;
300 // perhaps we need to compute two FFT's, so store left and right
301 // channel in case we need only one FFT, the left channel is
302 // used as 'standard'"
303 float valL;
304 float valR;
306 switch(_mode) {
307 default:
308 // left channel (mode 1)
309 // or both channels (mode 3, 4, 5, 7, 9, 10)
310 valL = L;
311 valR = R;
312 break;
313 case 0:
314 case 6:
315 // average (mode 0)
316 valL = (L + R) / 2;
317 valR = (L + R) / 2;
318 break;
319 case 2:
320 case 8:
321 // right channel (mode 2)
322 valL = R;
323 valR = L;
324 break;
326 // store values in analyzer buffer
327 fft_inL[i] = valL;
328 fft_inR[i] = valR;
330 // fill smoothing & falling buffer
331 if(_smooth == 2) {
332 fft_smoothL[i] = fft_outL[i];
333 fft_smoothR[i] = fft_outR[i];
335 if(_smooth == 1) {
336 if(fft_smoothL[i] < fabs(fft_outL[i])) {
337 fft_smoothL[i] = fabs(fft_outL[i]);
338 fft_deltaL[i] = 1.f;
340 if(fft_smoothR[i] < fabs(fft_outR[i])) {
341 fft_smoothR[i] = fabs(fft_outR[i]);
342 fft_deltaR[i] = 1.f;
346 // fill hold buffer with last out values
347 // before fft is recalced
348 if(fabs(fft_outL[i]) > fft_holdL[i])
349 fft_holdL[i] = fabs(fft_outL[i]);
350 if(fabs(fft_outR[i]) > fft_holdR[i])
351 fft_holdR[i] = fabs(fft_outR[i]);
354 // run fft
355 // this takes our latest buffer and returns an array with
356 // non-normalized
357 if (fft_plan)
358 fftwf_execute_r2r(fft_plan, fft_inL, fft_outL);
359 //run fft for for right channel too. it is needed for stereo image
360 //and stereo difference modes
361 if(_mode >= 3 and fft_plan) {
362 fftwf_execute_r2r(fft_plan, fft_inR, fft_outR);
364 // ...and set some values for later use
365 analyzer_phase_drawn = 0;
366 fftdone = true;
368 analyzer_phase_drawn ++;
370 return fftdone;
373 void analyzer::draw(int subindex, float *data, int points, bool fftdone) const
375 double freq; // here the frequency of the actual drawn pixel gets stored
376 int iter = 0; // this is the pixel we have been drawing the last box/bar/line
377 int _iter = 1; // this is the next pixel we want to draw a box/bar/line
378 int _last = -1; // used for mode 10 (parallel spectralizer) to prevent overwriting real values with INFINITY
379 float posneg = 1;
380 int __speed = 16 - (int)_speed;
381 if (lintrans < 0) {
382 // accuracy was changed so we have to recalc linear transition
383 int _lintrans = (int)((float)points * log((20.f + 2.f * \
384 (float)srate / (float)_accuracy) / 20.f) / log(1000.f));
385 lintrans = (int)(_lintrans + points % _lintrans / \
386 floor(points / _lintrans)) / 2; // / 4 was added to see finer bars but breaks low end
388 for (int i = 0; i <= points; i++)
390 // #####################################################################
391 // Real business starts here. We will cycle through all pixels in
392 // x-direction of the line-graph and decide what to show
393 // #####################################################################
394 // cycle through the points to draw
395 // we need to know the exact frequency at this pixel
396 freq = 20.f * pow (1000.f, (float)i / points);
398 // we need to know the last drawn value over the time
399 float lastoutL = 0.f;
400 float lastoutR = 0.f;
402 // let's see how many pixels we may want to skip until the drawing
403 // function has to draw a bar/box/line
404 if(_scale or _view == 2) {
405 // we have linear view enabled or we want to see tit... erm curves
406 if((i % lintrans == 0 and points - i > lintrans) or i == points - 1) {
407 _iter = std::max(1, (int)floor(freq * \
408 (float)_accuracy / (float)srate));
410 } else {
411 // we have logarithmic view enabled
412 _iter = std::max(1, (int)floor(freq * (float)_accuracy / (float)srate));
414 if(_iter > iter) {
415 // we are flipping one step further in drawing
416 if(fftdone and i) {
417 // ################################
418 // Manipulate the fft_out values
419 // according to the post processing
420 // ################################
421 int n = 0;
422 float var1L = 0.f; // used later for denoising peaks
423 float var1R = 0.f;
424 float diff_fft;
425 switch(_mode) {
426 default:
427 // all normal modes
428 posneg = 1;
429 // only if we don't see difference mode
430 switch(_post) {
431 case 0:
432 // Analyzer Normalized - nothing to do
433 break;
434 case 1:
435 // Analyzer Additive - cycle through skipped values and
436 // add them
437 // if fft was renewed, recalc the absolute values if
438 // frequencies are skipped
439 for(int j = iter + 1; j < _iter; j++) {
440 fft_outL[_iter] += fabs(fft_outL[j]);
441 fft_outR[_iter] += fabs(fft_outR[j]);
443 fft_outL[_iter] /= (_iter - iter);
444 fft_outR[_iter] /= (_iter - iter);
445 break;
446 case 2:
447 // Analyzer Additive - cycle through skipped values and
448 // add them
449 // if fft was renewed, recalc the absolute values if
450 // frequencies are skipped
451 for(int j = iter + 1; j < _iter; j++) {
452 fft_outL[_iter] += fabs(fft_outL[j]);
453 fft_outR[_iter] += fabs(fft_outR[j]);
455 break;
456 case 3:
457 // Analyzer Denoised Peaks - filter out unwanted noise
458 for(int k = 0; k < std::max(10 , std::min(400 ,\
459 (int)(2.f*(float)((_iter - iter))))); k++) {
460 //collect amplitudes in the environment of _iter to
461 //be able to erase them from signal and leave just
462 //the peaks
463 if(_iter - k > 0) {
464 var1L += fabs(fft_outL[_iter - k]);
465 n++;
467 if(k != 0) var1L += fabs(fft_outL[_iter + k]);
468 else if(i) var1L += fabs(lastoutL);
469 else var1L += fabs(fft_outL[_iter]);
470 if(_mode == 3 or _mode == 4) {
471 if(_iter - k > 0) {
472 var1R += fabs(fft_outR[_iter - k]);
473 n++;
475 if(k != 0) var1R += fabs(fft_outR[_iter + k]);
476 else if(i) var1R += fabs(lastoutR);
477 else var1R += fabs(fft_outR[_iter]);
479 n++;
481 //do not forget fft_out[_iter] for the next time
482 lastoutL = fft_outL[_iter];
483 //pumping up actual signal an erase surrounding
484 // sounds
485 fft_outL[_iter] = 0.25f * std::max(n * 0.6f * \
486 fabs(fft_outL[_iter]) - var1L , 1e-20);
487 if(_mode == 3 or _mode == 4) {
488 // do the same with R channel if needed
489 lastoutR = fft_outR[_iter];
490 fft_outR[_iter] = 0.25f * std::max(n * \
491 0.6f * fabs(fft_outR[_iter]) - var1R , 1e-20);
493 break;
495 break;
496 case 5:
497 // Stereo Difference - draw the difference between left
498 // and right channel if fft was renewed, recalc the
499 // absolute values in left and right if frequencies are
500 // skipped.
501 // this is additive mode - no other mode is available
502 //for(int j = iter + 1; j < _iter; j++) {
503 // fft_outL[_iter] += fabs(fft_outL[j]);
504 // fft_outR[_iter] += fabs(fft_outR[j]);
506 //calculate difference between left an right channel
507 diff_fft = fabs(fft_outL[_iter]) - fabs(fft_outR[_iter]);
508 posneg = fabs(diff_fft) / diff_fft;
509 //fft_outL[_iter] = diff_fft / _accuracy;
510 break;
513 iter = _iter;
514 // #######################################
515 // Calculate transitions for falling and
516 // smooting and fill delta buffers if fft
517 // was done above
518 // #######################################
519 if(subindex == 0) {
520 float _fdelta = 0.91;
521 float _ffactor = 2000.f;
523 if(_mode > 5 and _mode < 11) {
524 _fdelta = .99f;
525 _ffactor = 50.f;
527 if(_smooth == 2) {
528 // smoothing
529 if(fftdone) {
530 // rebuild delta values after fft was done
531 if(_mode < 5 or _mode > 5) {
532 fft_deltaL[iter] = pow(fabs(fft_outL[iter]) / fabs(fft_smoothL[iter]), 1.f / __speed);
533 } else {
534 fft_deltaL[iter] = (posneg * fabs(fft_outL[iter]) - fft_smoothL[iter]) / __speed;
536 } else {
537 // change fft_smooth according to delta
538 if(_mode < 5 or _mode > 5) {
539 fft_smoothL[iter] *= fft_deltaL[iter];
540 } else {
541 fft_smoothL[iter] += fft_deltaL[iter];
544 } else if(_smooth == 1) {
545 // falling
546 if(fftdone) {
547 // rebuild delta values after fft was done
548 //fft_deltaL[iter] = _fdelta;
550 // change fft_smooth according to delta
551 fft_smoothL[iter] *= fft_deltaL[iter];
553 if(fft_deltaL[iter] > _fdelta) {
554 fft_deltaL[iter] *= 1.f - (16.f - __speed) / _ffactor;
558 if((_mode > 2 and _mode < 5) or (_mode > 8 and _mode < 11)) {
559 // we need right buffers, too for stereo image and
560 // stereo analyzer
561 if(_smooth == 2) {
562 // smoothing
563 if(fftdone) {
564 // rebuild delta values after fft was done
565 if(_mode < 5) {
566 fft_deltaR[iter] = pow(fabs(fft_outR[iter]) / fabs(fft_smoothR[iter]), 1.f / __speed);
567 } else {
568 fft_deltaR[iter] = (posneg * fabs(fft_outR[iter]) - fft_smoothR[iter]) / __speed;
570 } else {
571 // change fft_smooth according to delta
572 if(_mode < 5) {
573 fft_smoothR[iter] *= fft_deltaR[iter];
574 } else {
575 fft_smoothR[iter] += fft_deltaR[iter];
578 } else if(_smooth == 1) {
579 // falling
580 if(fftdone) {
581 // rebuild delta values after fft was done
582 //fft_deltaR[iter] = _fdelta;
584 // change fft_smooth according to delta
585 fft_smoothR[iter] *= fft_deltaR[iter];
586 if(fft_deltaR[iter] > _fdelta)
587 fft_deltaR[iter] *= 1.f - (16.f - __speed) / _ffactor;
591 // #######################################
592 // Choose the L and R value from the right
593 // buffer according to view settings
594 // #######################################
595 float valL = 0.f;
596 float valR = 0.f;
597 if (_freeze) {
598 // freeze enabled
599 valL = fft_freezeL[iter];
600 valR = fft_freezeR[iter];
601 } else if ((subindex == 1 and _mode < 3) \
602 or subindex > 1 \
603 or (_mode > 5 and _hold)) {
604 // we draw the hold buffer
605 valL = fft_holdL[iter];
606 valR = fft_holdR[iter];
607 } else {
608 // we draw normally (no freeze)
609 switch(_smooth) {
610 case 0:
611 // off
612 valL = fft_outL[iter];
613 valR = fft_outR[iter];
614 break;
615 case 1:
616 // falling
617 valL = fft_smoothL[iter];
618 valR = fft_smoothR[iter];
619 break;
620 case 2:
621 // smoothing
622 valL = fft_smoothL[iter];
623 valR = fft_smoothR[iter];
624 break;
626 // fill freeze buffer
627 fft_freezeL[iter] = valL;
628 fft_freezeR[iter] = valR;
630 if(_view < 2) {
631 // #####################################
632 // send values back to painting function
633 // according to mode setting but only if
634 // we are drawing lines or boxes
635 // #####################################
636 float tmp;
637 int pos1, pos2;
638 switch(_mode) {
639 case 3:
640 case 9:
641 // stereo analyzer/spectralizer
642 if(subindex == 0 or subindex == 2) {
643 data[i] = dB_grid(fabs(valL) / _accuracy * 2.f + 1e-20, _resolution, _offset);
644 } else {
645 data[i] = dB_grid(fabs(valR) / _accuracy * 2.f + 1e-20, _resolution, _offset);
647 break;
648 case 4:
649 // we want to draw Stereo Image
650 if(subindex == 0 or subindex == 2) {
651 // Left channel signal
652 tmp = dB_grid(fabs(valL) / _accuracy * 2.f + 1e-20, _resolution, _offset);
653 //only signals above the middle are interesting
654 data[i] = tmp < 0 ? 0 : tmp;
655 } else if (subindex == 1 or subindex == 3) {
656 // Right channel signal
657 tmp = dB_grid(fabs(valR) / _accuracy * 2.f + 1e-20, _resolution, _offset);
658 //only signals above the middle are interesting. after cutting away
659 //the unneeded stuff, the curve is flipped vertical at the middle.
660 if(tmp < 0) tmp = 0;
661 data[i] = -1.f * tmp;
663 break;
664 case 5:
665 // We want to draw Stereo Difference
666 if(i) {
667 tmp = dB_grid(fabs((fabs(valL) - fabs(valR))) / _accuracy * 2.f + 1e-20, _resolution, 1.f / _offset);
668 //only show differences above a threshhold which results from the db_grid-calculation
669 if (tmp < 0) tmp=0;
670 //bring right signals below the middle
671 tmp *= fabs(valL) < fabs(valR) ? -1.f : 1.f;
672 data[i] = tmp;
674 else data[i] = 0.f;
675 break;
676 case 10:
677 // spectralizer parallel
678 pos1 = i / 2;
679 pos2 = points / 2 + pos1;
680 data[pos1] = dB_grid(fabs(valL) / _accuracy * 2.f + 1e-20, _resolution, _offset);
681 data[pos2] = dB_grid(fabs(valR) / _accuracy * 2.f + 1e-20, _resolution, _offset);
682 _last = pos1;
683 break;
684 default:
685 // normal analyzer behavior
686 data[i] = dB_grid(fabs(valL) / _accuracy * 2.f + 1e-20, _resolution, _offset);
687 break;
691 else if(_view == 2) {
692 // we have to draw splines, so we draw every x-pixel according to
693 // the pre-generated fft_splinesL and fft_splinesR buffers
694 data[i] = INFINITY;
696 // int _splinepos = -1;
697 // *mode=0;
699 // for (int i = 0; i<=points; i++) {
700 // if (subindex == 1 and i == spline_buffer[_splinepos]) _splinepos++;
702 // freq = 20.f * pow (1000.f, (float)i / points); //1000=20000/20
703 // float a0,b0,c0,d0,a1,b1,c1,d1,a2,b2,c2,d2;
705 // if(((i % lintrans == 0 and points - i > lintrans) or i == points - 1 ) and subindex == 0) {
707 // _iter = std::max(1, (int)floor(freq * (float)_accuracy / (float)srate));
708 // //printf("_iter %3d\n",_iter);
709 // }
710 // if(_iter > iter and subindex == 0)
711 // {
712 // _splinepos++;
713 // spline_buffer[_splinepos] = _iter;
714 // //printf("_splinepos: %3d - lintrans: %3d\n", _splinepos,lintrans);
715 // if(fftdone and i and subindex == 0)
716 // {
717 // // if fft was renewed, recalc the absolute values if frequencies
718 // // are skipped
719 // for(int j = iter + 1; j < _iter; j++) {
720 // fft_out[_iter] += fabs(fft_out[j]);
721 // }
722 // }
723 // }
725 // if(fftdone and subindex == 1 and _splinepos >= 0 and (_splinepos % 3 == 0 or _splinepos == 0))
726 // {
727 // float mleft, mright, y0, y1, y2, y3, y4;
729 // //jetzt spline interpolation
730 // y0 = dB_grid(fft_out[spline_buffer[_splinepos]] / _accuracy * 2.f + 1e-20, pow(64, _level), 0.5f);
731 // y1 = dB_grid(fft_out[spline_buffer[_splinepos + 1]] / _accuracy * 2.f + 1e-20, pow(64, _level), 0.5f);
732 // y2 = dB_grid(fft_out[spline_buffer[_splinepos + 2]] / _accuracy * 2.f + 1e-20, pow(64, _level), 0.5f);
733 // y3 = dB_grid(fft_out[spline_buffer[_splinepos + 3]] / _accuracy * 2.f + 1e-20, pow(64, _level), 0.5f);
734 // y4 = dB_grid(fft_out[spline_buffer[_splinepos + 4]] / _accuracy * 2.f + 1e-20, pow(64, _level), 0.5f);
735 // mleft = y1 - y0;
736 // mright = y4 - y3;
737 // printf("y-werte %3d, %3d, %3d, %3d, %3d\n",y0,y1,y2,y3,y4);
738 // a0 = (-3*y3+15*y2-44*y1+32*y0+mright+22*mleft)/34;
739 // b0 = -(-3*y3+15*y2-78*y1+66*y0+mright+56*mleft)/34;
740 // c0 = mleft;
741 // d0 = y0;
742 // a1 = -(-15*y3+41*y2-50*y1+24*y0+5*mright+8*mleft)/34;
743 // b1 = (-6*y3+30*y2-54*y1+30*y0+2*mright+10*mleft)/17;
744 // c1 = -(3*y3-15*y2-24*y1+36*y0-mright+12*mleft)/34;
745 // d1 = y1;
746 // a2 = (-25*y3+40*y2-21*y1+6*y0+14*mright+2*mleft)/17;
747 // b2 = -(-33*y3+63*y2-42*y1+12*y0+11*mright+4*mleft)/17;
748 // c2 = (9*y3+6*y2-21*y1+6*y0-3*mright+2*mleft)/17;
749 // d2 = y2;
750 // }
751 // iter = _iter;
754 // if(i > spline_buffer[_splinepos] and i <= spline_buffer[_splinepos + 1] and _splinepos >= 0 and subindex == 1)
755 // {
756 // data[i] = a0 * pow(i / lintrans - _splinepos, 3) + b0 * pow(i / lintrans - _splinepos, 2) + c0 * (i / lintrans - _splinepos) + d0;
757 // printf("1.spline\n");
758 // }
759 // if(i > spline_buffer[_splinepos + 1] and i <= spline_buffer[_splinepos + 2] and _splinepos >= 0 and subindex == 1)
760 // {
761 // printf("2.spline\n");
762 // data[i] = a1 * pow(i / lintrans - _splinepos, 3) + b1 * pow(i / lintrans - _splinepos, 2) + c1 * (i / lintrans - _splinepos) + d1;
763 // }
764 // if(i > spline_buffer[_splinepos + 2] and i <= spline_buffer[_splinepos + 3] and _splinepos >= 0 and subindex == 1)
765 // {
766 // printf("3.spline\n");
767 // data[i] = a2 * pow(i / lintrans - _splinepos, 3) + b2 * pow(i / lintrans - _splinepos, 2) + c2 * (i / lintrans - _splinepos) + d2;
768 // }
769 // if(subindex==1) printf("data[i] %3d _splinepos %2d\n", data[i], _splinepos);
770 // if (subindex == 0)
771 // {
772 // data[i] = INFINITY;
773 // } else data[i] = dB_grid(i/200, pow(64, _level), 0.5f);
775 // }
778 else {
779 if (_mode != 10)
780 data[i] = INFINITY;
781 else if (_last != i / 2) {
782 data[i / 2] = INFINITY;
783 data[points / 2 + i / 2] = INFINITY;
789 bool analyzer::get_graph(int subindex, int phase, float *data, int points, cairo_iface *context, int *mode) const
791 if (!phase)
792 return false;
794 if ((subindex == 1 and !_hold and _mode < 3) \
795 or (subindex > 1 and _mode < 3) \
796 or (subindex == 2 and !_hold and (_mode == 3 or _mode == 4)) \
797 or (subindex == 4 and (_mode == 3 or _mode == 4)) \
798 or (subindex == 1 and _mode == 5) \
799 or _mode > 5 \
801 // stop drawing when all curves have been drawn according to the mode
802 // and hold settings
803 return false;
805 bool fftdone = false;
806 if (!subindex)
807 fftdone = do_fft(subindex, points);
808 draw(subindex, data, points, fftdone);
810 // #############################
811 // choose a drawing mode between
812 // boxes, lines and bars
813 // #############################
815 // modes:
816 // 0: left
817 // 1: right
818 // 2: average
819 // 3: stereo
820 // 4: image
821 // 5: difference
823 // views:
824 // 0: bars
825 // 1: lines
826 // 2: cubic splines
828 // *modes to set:
829 // 0: lines
830 // 1: blob
831 // 2: bars
832 // 3: boxes (little things on the values position
833 // 4: centered bars (0dB is centered in y direction)
835 if (_mode > 3 and _mode < 6) {
836 // centered viewing modes like stereo image and stereo difference
837 if(!_view) {
838 // boxes
839 if(subindex > 1) {
840 // boxes (hold)
841 *mode = 3;
842 } else {
843 // bars (signal)
844 *mode = 4;
846 } else {
847 // lines
848 *mode = 0;
850 } else if(!_view) {
851 // bars
852 if((subindex == 0 and _mode < 3) or (subindex <= 1 and _mode == 3)) {
853 // draw bars
854 *mode = 2;
855 } else {
856 // draw boxes
857 *mode = 3;
859 } else {
860 // draw lines
861 *mode = 0;
863 // ###################################
864 // colorize the curves/boxes according
865 // to the chosen display settings
866 // ###################################
868 // change alpha (or color) for hold lines or stereo modes
869 if((subindex == 1 and _mode < 3) or (subindex > 1 and _mode == 4)) {
870 // subtle hold line on left, right, average or stereo image
871 context->set_source_rgba(0.15, 0.2, 0.0, 0.2);
873 if(subindex == 0 and _mode == 3) {
874 // left channel in stereo analyzer
875 context->set_source_rgba(0.25, 0.10, 0.0, 0.33);
877 if(subindex == 1 and _mode == 3) {
878 // right channel in stereo analyzer
879 context->set_source_rgba(0.05, 0.25, 0.0, 0.33);
881 if(subindex == 2 and _mode == 3) {
882 // left hold in stereo analyzer
883 context->set_source_rgba(0.45, 0.30, 0.2, 0.2);
885 if(subindex == 3 and _mode == 3) {
886 // right hold in stereo analyzer
887 context->set_source_rgba(0.25, 0.45, 0.2, 0.2);
890 context->set_line_width(0.75);
891 return true;
894 bool analyzer::get_moving(int subindex, int &direction, float *data, int x, int y, int &offset, uint32_t &color) const
896 if ((subindex and _mode != 9) or subindex > 1)
897 return false;
898 bool fftdone = false;
899 if (!subindex)
900 fftdone = do_fft(subindex, x);
901 draw(subindex, data, x, fftdone);
902 direction = LG_MOVING_UP;
903 offset = 0;
904 if (_mode == 9 and subindex) {
905 color = RGBAtoINT(0.35, 0.1, 0, 0.4);
906 } else if (_mode == 9) {
907 color = RGBAtoINT(0.15, 0.35, 0, 0.4);
909 return true;
912 bool analyzer::get_gridline(int subindex, int phase, float &pos, bool &vertical, std::string &legend, cairo_iface *context) const
914 if (phase)
915 return false;
916 redraw_graph = false;
917 float gain;
918 int sub = subindex + (_draw_upper % 2) - 1;
919 static const double dash[] = {2.0};
920 switch (_mode) {
921 case 0:
922 case 1:
923 case 2:
924 case 3:
925 default:
926 return get_freq_gridline(subindex, pos, vertical, legend, context, true, _resolution, _offset);
927 case 4:
928 // stereo image
929 if(subindex < 28)
930 return get_freq_gridline(subindex, pos, vertical, legend, context, true);
931 else {
932 subindex -= 28;
934 gain = _draw_upper > 0 ? 1.f / (1 << (subindex - _draw_upper))
935 : 1.f / (1 << subindex);
936 pos = dB_grid(gain, _resolution, _offset);
937 if (_draw_upper > 0)
938 pos *= -1;
940 context->set_dash(dash, 1);
941 if ((!(subindex & 1) and !_draw_upper)
942 or ((sub & 1) and _draw_upper > 0)) {
943 // add a label and make the lines straight
944 std::stringstream ss;
945 ss << (subindex - std::max(0, _draw_upper)) * -6 << " dB";
946 legend = ss.str();
947 context->set_dash(dash, 0);
950 if (pos < 0 and !_draw_upper) {
951 // start drawing the lower end
952 _draw_upper = subindex;
953 pos = -2;
955 if (_draw_upper < 0) {
956 // end the drawing of the grid
957 _draw_upper = 0;
958 return false;
960 if (pos > 0 and _draw_upper > 0) {
961 // center line
962 _draw_upper = -1;
963 pos = 0;
964 context->set_dash(dash, 0);
966 else if (subindex)
967 context->set_source_rgba(0, 0, 0, 0.1);
968 vertical = false;
969 return true;
970 case 5:
971 // stereo difference
972 if(subindex < 28)
973 return get_freq_gridline(subindex, pos, vertical, legend, context, true);
974 else
975 subindex -= 28;
977 gain = _draw_upper > 0 ? 1.0 / (1 << (subindex - _draw_upper))
978 : (1 << subindex);
979 pos = dB_grid(gain, _resolution, 0);
981 context->set_dash(dash, 1);
982 if ((!(subindex & 1) and !_draw_upper)
983 or ((subindex & 1) and _draw_upper)) {
984 std::stringstream ss;
985 ss << (subindex - std::max(0, _draw_upper)) * 6 - 72 << " dB";
986 legend = ss.str();
987 context->set_dash(dash, 0);
990 if (pos > 1 and !_draw_upper and (subindex & 1)) {
991 _draw_upper = subindex;
993 if (pos < -1 and _draw_upper) {
994 _draw_upper = 0;
995 return false;
998 if (subindex)
999 context->set_source_rgba(0, 0, 0, 0.1);
1000 vertical = false;
1001 return true;
1002 case 6:
1003 case 7:
1004 case 8:
1005 case 9:
1006 if (subindex < 28) {
1007 vertical = true;
1008 if (subindex == 9) legend = "100 Hz";
1009 if (subindex == 18) legend = "1 kHz";
1010 if (subindex == 27) legend = "10 kHz";
1012 float freq = subindex_to_freq(subindex);
1013 pos = log(freq / 20.0) / log(1000);
1015 if (!legend.empty()) {
1016 context->set_source_rgba(0, 0, 0, 0.33);
1017 } else {
1018 context->set_source_rgba(0, 0, 0, 0.2);
1020 return true;
1022 return false;
1023 case 10:
1024 //if (!subindex) {
1025 //cairo_t *ctx = context->context;
1026 //cairo_set_source_rgb(ctx, 0.35, 0.4, 0.2);
1027 //cairo_select_font_face(ctx, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
1028 //cairo_set_font_size(ctx, 20);
1029 //cairo_move_to (ctx, context->size_x / 2 - 20, 40);
1030 //cairo_show_text (ctx, "L");
1031 //cairo_move_to (ctx, context->size_x / 2 + 12, 40);
1032 //cairo_show_text (ctx, "R");
1034 if (subindex < 56) {
1035 vertical = true;
1036 if (subindex == 9 or subindex == 36) legend = "100 Hz";
1037 if (subindex == 18 or subindex == 45) legend = "1 kHz";
1038 if (subindex == 27 or subindex == 54) legend = "10 kHz";
1040 float freq = subindex_to_freq(subindex - (subindex > 27 ? 27 : 0));
1041 pos = log(freq / 20.0) / log(1000) / 2 + (subindex > 27 ? 0.5 : 0);
1042 if (!legend.empty() and subindex != 28) {
1043 context->set_source_rgba(0, 0, 0, 0.33);
1044 } else if (subindex != 28) {
1045 context->set_source_rgba(0, 0, 0, 0.2);
1047 return true;
1049 return false;
1051 return false;
1054 bool analyzer::get_layers(int generation, unsigned int &layers) const
1056 if (_mode > 5 and _mode < 11)
1057 layers = LG_REALTIME_MOVING;
1058 else
1059 layers = LG_REALTIME_GRAPH;
1060 layers |= ((!generation or redraw_graph) ? LG_CACHE_GRID : 0);
1061 return true;