1 /* fil4.lv2 2 * 3 * Copyright (C) 2016 Robin Gareus <robin@gareus.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2, or (at your option) 8 * any later version. 9 * 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 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #ifdef DISPLAY_INTERFACE 20 21 #ifndef HYPOTF 22 #define HYPOTF(X,Y) (sqrtf (SQUARE(X) + SQUARE(Y))) 23 #endif 24 25 #ifndef MIN 26 #define MIN(A,B) ((A) < (B)) ? (A) : (B) 27 #endif 28 29 struct omega { 30 float c1, s1, c2, s2; 31 }; 32 33 /* drawing helpers, calculate respone for given frequency */ 34 static float get_filter_response (Fil4Paramsect const * const flt, struct omega const * const w) { 35 float x = w->c2 + flt->s1() * w->c1 + flt->s2(); 36 float y = w->s2 + flt->s1() * w->s1; 37 const float t1 = HYPOTF (x, y); 38 x += flt->g0 () * (w->c2 - 1.f); 39 y += flt->g0 () * w->s2; 40 const float t2 = HYPOTF (x, y); 41 return 20.f * log10f (t2 / t1); 42 } 43 44 static float get_shelf_response (IIRProc const * const flt, struct omega const * const w) { 45 const float _A = flt->b0 + flt->b2; 46 const float _B = flt->b0 - flt->b2; 47 const float _C = 1.0 + flt->a2; 48 const float _D = 1.0 - flt->a2; 49 50 const float A = _A * w->c1 + flt->b1; 51 const float B = _B * w->s1; 52 const float C = _C * w->c1 + flt->a1; 53 const float D = _D * w->s1; 54 return 20.f * log10f (sqrtf ((SQUARE(A) + SQUARE(B)) * (SQUARE(C) + SQUARE(D))) / (SQUARE(C) + SQUARE(D))); 55 } 56 57 static float get_highpass_response (HighPass const * const hip, const float freq) { 58 if (!hip->en) { 59 return 0; 60 } else { 61 // this is only an approx. 62 const float wr = hip->freq / freq; 63 float q; 64 float r = (0.7 + 0.78 * tanh (1.82 * ((hip->q) -.8))); // RESHP 65 if (r < 1.3) { 66 q = 3.01 * sqrt(r / (r+2)); 67 } else { 68 // clamp pole 69 q = sqrt(4 - 0.09 / (r - 1.09)); 70 } 71 return -10.f * log10f (SQUARE(1 + SQUARE(wr)) - SQUARE(q * wr)); 72 } 73 } 74 75 static float get_lowpass_response (LowPass const * const lop, const float freq, const float rate , struct omega const * const _w) { 76 if (!lop->en) { 77 return 0; 78 } else { 79 // this is only an approx. 80 const float w = sin (M_PI * freq / rate); 81 const float wc = sin (M_PI * lop->freq / rate); 82 const float q = sqrtf(4.f * lop->r / (1 + lop->r)); 83 float xhs = 0; 84 #ifdef LP_EXTRA_SHELF 85 xhs = get_shelf_response (&lop->iir_hs, _w); 86 #endif 87 return -10.f * log10f (SQUARE(1 + SQUARE(w/wc)) - SQUARE(q * w / wc)) + xhs; 88 } 89 } 90 91 static float freq_at_x (const int x, const float w) { 92 return 20.f * powf (1000.f, x / w); 93 } 94 95 static float x_at_freq (const float f, const float w) { 96 return rintf (w * logf (f / 20.0) / logf (1000.0)); 97 } 98 99 static LV2_Inline_Display_Image_Surface * 100 fil4_render(LV2_Handle instance, uint32_t w, uint32_t max_h) 101 { 102 #ifdef WITH_SIGNATURE 103 if (!is_licensed (instance)) { return NULL; } 104 #endif 105 uint32_t h = MIN (1 | (uint32_t)ceilf (w * 9.f / 16.f), max_h); 106 107 Fil4* self = (Fil4*)instance; 108 if (!self->display || self->w != w || self->h != h) { 109 if (self->display) cairo_surface_destroy(self->display); 110 self->display = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); 111 self->w = w; 112 self->h = h; 113 } 114 cairo_t* cr = cairo_create (self->display); 115 cairo_rectangle (cr, 0, 0, w, h); 116 if (self->enabled) { 117 cairo_set_source_rgba (cr, .2, .2, .2, 1.0); 118 } else { 119 cairo_set_source_rgba (cr, .1, .1, .1, 1.0); 120 } 121 cairo_fill (cr); 122 123 const float yr = (h - 2.f) / (2.f * 20); // +/- 20dB 124 const float ym = rintf ((h - 1.f) * .5f) + .5; 125 const float xw = w - 1; 126 127 const float a = self->enabled ? 1.0 : .2; 128 const float ny = x_at_freq (.5 * self->rate, xw); 129 130 /* zero line */ 131 cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 132 cairo_set_line_width(cr, 1.0); 133 cairo_set_source_rgba (cr, .6, .6, .6, a); 134 cairo_move_to (cr, 1, ym); 135 cairo_line_to (cr, w - 1, ym); 136 cairo_stroke(cr); 137 138 #define X_GRID(FREQ) { \ 139 const float xx = .5 + x_at_freq (FREQ, xw); \ 140 cairo_move_to (cr, xx, 0); \ 141 cairo_line_to (cr, xx, h); \ 142 cairo_stroke (cr); \ 143 } 144 145 #define Y_GRID(dB) { \ 146 const float yy = rintf (yr * dB); \ 147 cairo_move_to (cr, 0, ym - yy); \ 148 cairo_line_to (cr, w, ym - yy); \ 149 cairo_stroke (cr); \ 150 cairo_move_to (cr, 0, ym + yy); \ 151 cairo_line_to (cr, w, ym + yy); \ 152 cairo_stroke (cr); \ 153 } 154 155 const double dash2[] = {1, 3}; 156 cairo_save (cr); 157 cairo_set_dash(cr, dash2, 2, 2); 158 cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.5 * a); 159 X_GRID (100); 160 X_GRID (1000); 161 X_GRID (10000); 162 Y_GRID (6); 163 Y_GRID (12); 164 Y_GRID (18); 165 cairo_restore (cr); 166 167 FilterChannel const * const fc = &self->fc[0]; 168 169 if (ny < xw) { 170 cairo_rectangle (cr, 0, 0, ny, h); 171 cairo_clip (cr); 172 } 173 174 for (uint32_t i = 0; i < xw && i < ny; ++i) { 175 const float freq = freq_at_x (i, xw); 176 const float w = 2.f * M_PI * freq / self->rate; 177 struct omega _w; 178 _w.c1 = cosf (w); 179 _w.s1 = sinf (w); 180 _w.c2 = cosf (2.f * w); 181 _w.s2 = sinf (2.f * w); 182 183 float y = 0; 184 for (int j = 0; j < NSECT; ++j) { 185 y += yr * get_filter_response (&fc->_sect[j], &_w); 186 } 187 y += yr * get_shelf_response (&fc->iir_lowshelf, &_w); 188 y += yr * get_shelf_response (&fc->iir_highshelf, &_w); 189 190 y += yr * get_highpass_response (&fc->hip, freq); 191 y += yr * get_lowpass_response (&fc->lop, freq, self->rate, &_w); 192 193 if (i == 0) { 194 cairo_move_to (cr, 0.5 + i, ym - y); 195 } else { 196 cairo_line_to (cr, 0.5 + i, ym - y); 197 } 198 } 199 200 cairo_set_source_rgba (cr, .8, .8, .8, a); 201 cairo_stroke_preserve(cr); 202 cairo_line_to (cr, w, ym); 203 cairo_line_to (cr, 0, ym); 204 cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.5 * a); 205 cairo_fill (cr); 206 207 cairo_destroy (cr); 208 cairo_surface_flush (self->display); 209 self->surf.width = cairo_image_surface_get_width (self->display); 210 self->surf.height = cairo_image_surface_get_height (self->display); 211 self->surf.stride = cairo_image_surface_get_stride (self->display); 212 self->surf.data = cairo_image_surface_get_data (self->display); 213 214 return &self->surf; 215 } 216 #endif 217