static void plot_data_fft(SFSUI* ui) { cairo_t* cr; cr = cairo_create (ui->sf_dat); rounded_rectangle (cr, SS_BORDER, SS_BORDER, SS_SIZE, SS_SIZE, SS_BORDER); cairo_clip_preserve (cr); const float persistence = robtk_dial_get_value(ui->screen); float transp; cairo_set_operator (cr, CAIRO_OPERATOR_OVER); if (persistence > 0) { cairo_set_source_rgba(cr, 0, 0, 0, .25 - .0025 * persistence); transp = 0.05; } else { cairo_set_source_rgba(cr, 0, 0, 0, 1.0); transp = .5; } cairo_fill(cr); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_width (cr, 1.0); const float xmid = rintf(SS_BORDER + SS_SIZE *.5) + .5; const float dnum = SS_SIZE / ui->log_base; const float denom = ui->log_rate / (float)ui->fft_bins; cairo_set_operator (cr, CAIRO_OPERATOR_OVER); for (uint32_t i = 1; i < ui->fft_bins-1 ; ++i) { if (ui->level[i] < 0) continue; const float level = MAKEUP_GAIN + fftx_power_to_dB(ui->level[i]); if (level < -80) continue; const float y = rintf(SS_BORDER + SS_SIZE - dnum * fast_log10(1.0 + i * denom)) + .5; const float y1 = rintf(SS_BORDER + SS_SIZE - dnum * fast_log10(1.0 + (i+1) * denom)) + .5; const float pk = level > 0.0 ? 1.0 : (80 + level) / 80.0; const float a_lr = ui->lr[i]; float clr[3]; hsl2rgb(clr, .70 - .72 * pk, .9, .3 + pk * .4); cairo_set_source_rgba(cr, clr[0], clr[1], clr[2], transp + pk * .2); cairo_set_line_width (cr, MAX(1.0, (y - y1))); cairo_move_to(cr, xmid, y); cairo_line_to(cr, SS_BORDER + SS_SIZE * a_lr, y); cairo_stroke(cr); } cairo_destroy (cr); }
/* linear FFT data display */ static void plot_data_fft(MF2UI* ui) { cairo_t* cr; const double ccc = ui->width / 2.0 + .5; const double rad = (ui->width - XOFF) * .5; const float gain = robtk_dial_get_value(ui->gain); const float persistence = robtk_dial_get_value(ui->screen); cr = cairo_create (ui->sf_dat); cairo_arc (cr, ccc, ccc, rad, 0, 2.0 * M_PI); cairo_clip_preserve (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); if (persistence > 0) { cairo_set_source_rgba(cr, 0, 0, 0, .3 - .003 * persistence); } else { cairo_set_source_rgba(cr, 0, 0, 0, 1.0); } cairo_fill(cr); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); const float dnum = ui->scale * PH_RAD / ui->log_base; const float denom = ui->log_rate / (float)ui->fft_bins; const float cutoff = ui->db_cutoff; for (uint32_t i = 1; i < ui->fft_bins-1 ; ++i) { if (ui->level[i] < 0) continue; const float level = gain + fftx_power_to_dB(ui->level[i]); if (level < cutoff) continue; const float dist = dnum * fast_log10(1.0 + i * denom); const float dx = ccc + dist * sinf(ui->phase[i]); const float dy = ccc - dist * cosf(ui->phase[i]); const float pk = level > 0.0 ? 1.0 : (cutoff - level) / cutoff; draw_point(ui, cr, pk, dx, dy, ccc, dist, ui->phase[i]); } cairo_destroy (cr); }
static void process_audio(MF2UI* ui, const size_t n_elem, float const * const left, float const * const right) { pthread_mutex_lock(&ui->fft_lock); fftx_run(ui->fa, n_elem, left); bool display = !fftx_run(ui->fb, n_elem, right); if (display) { assert (fftx_bins(ui->fa) == ui->fft_bins); float peak = 0; const float db_thresh = ui->db_thresh; for (uint32_t i = 1; i < ui->fft_bins-1; i++) { if (ui->fa->power[i] < db_thresh || ui->fb->power[i] < db_thresh) { ui->phase[i] = 0; ui->level[i] = -100; continue; } const float phase0 = ui->fa->phase[i]; const float phase1 = ui->fb->phase[i]; float phase = phase1 - phase0; ui->phase[i] = phase; ui->level[i] = MAX(ui->fa->power[i], ui->fb->power[i]); if (ui->level[i] > peak) { peak = ui->level[i]; } } ui->peak += .04 * (peak - ui->peak) + 1e-15; if (isnan (ui->peak)) { ui->peak = 0; } if (ui->peak > 1000) { ui->peak = 1000; } if (robtk_cbtn_get_active(ui->btn_norm)) { robtk_dial_set_value(ui->gain, - fftx_power_to_dB(ui->peak)); } queue_draw(ui->m0); } pthread_mutex_unlock(&ui->fft_lock); }
static void plot_data_oct(SFSUI* ui) { cairo_t* cr; cr = cairo_create (ui->sf_dat); rounded_rectangle (cr, SS_BORDER, SS_BORDER, SS_SIZE, SS_SIZE, SS_BORDER); cairo_clip_preserve (cr); const float persistence = robtk_dial_get_value(ui->screen); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); if (persistence > 0) { cairo_set_source_rgba(cr, 0, 0, 0, .33 - .0033 * persistence); } else { cairo_set_source_rgba(cr, 0, 0, 0, 1.0); } cairo_fill(cr); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); const float xmid = rintf(SS_BORDER + SS_SIZE *.5) + .5; const float dnum = SS_SIZE / ui->log_base; const float denom = 2.0 * ui->log_rate / ui->rate; uint32_t fi = 1; for (uint32_t i = 0; i < ui->freq_bins; ++i) { float a_lr = 0; float a_level = 0; float a_freq = 0; uint32_t a_cnt = 0; while(fi < ui->freq_band[i]) { if (ui->level[fi] < 0) { fi++; continue; } a_freq += fi * ui->fa->freq_per_bin; a_level += ui->level[fi]; a_lr += ui->lr[fi]; a_cnt++; fi++; } if (a_cnt == 0) continue; a_level = MAKEUP_GAIN + fftx_power_to_dB (a_level); if (a_level < -80) continue; a_freq /= (float)a_cnt; a_lr /= (float)a_cnt; const float y = rintf(SS_BORDER + SS_SIZE - dnum * fast_log10(1.0 + a_freq * denom)) + .5; const float pk = a_level > 0.0 ? 1.0 : (80 + a_level) / 80.0; float clr[3]; hsl2rgb(clr, .70 - .72 * pk, .9, .3 + pk * .4); cairo_set_source_rgba(cr, clr[0], clr[1], clr[2], 0.8); if (fabsf(a_lr -.5) < .05) { cairo_set_line_width (cr, 3.0); } else { cairo_set_line_width (cr, 1.0); } cairo_move_to(cr, xmid, y); cairo_line_to(cr, SS_BORDER + SS_SIZE * a_lr, y); cairo_stroke(cr); } cairo_destroy (cr); }
FFTX_FN_PREFIX float fftx_power_at_bin(struct FFTAnalysis *ft, const int b) { return (fftx_power_to_dB(ft->power[b])); }
/* 1/Octave data display */ static void plot_data_oct(MF2UI* ui) { cairo_t* cr; const double ccc = ui->width / 2.0 + .5; const double rad = (ui->width - XOFF) * .5; const float gain = robtk_dial_get_value(ui->gain); const float persistence = robtk_dial_get_value(ui->screen); cr = cairo_create (ui->sf_dat); cairo_arc (cr, ccc, ccc, rad, 0, 2.0 * M_PI); cairo_clip_preserve (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); if (persistence > 0) { cairo_set_source_rgba(cr, 0, 0, 0, .33 - .0033 * persistence); } else { cairo_set_source_rgba(cr, 0, 0, 0, 1.0); } cairo_fill(cr); cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); const float dnum = ui->scale * PH_RAD / ui->log_base; const float denom = 2.0 * ui->log_rate / ui->rate; const float cutoff = ui->db_cutoff; uint32_t fi = 1; for (uint32_t i = 0; i < ui->freq_bins; ++i) { float ang_x = 0; float ang_y = 0; float a_level = 0; float a_freq = 0; uint32_t a_cnt = 0; while(fi < ui->freq_band[i]) { if (ui->level[fi] < 0) { fi++; continue; } a_freq += fi * ui->fa->freq_per_bin; a_level += ui->level[fi]; ang_x += sinf(ui->phase[fi]); ang_y += cosf(ui->phase[fi]); a_cnt++; fi++; } if (a_cnt == 0) continue; a_level = gain + fftx_power_to_dB (a_level); if (a_level < cutoff) continue; a_freq /= (float)a_cnt; const float dist = dnum * fast_log10(1.0 + a_freq * denom); const float pk = a_level > 0.0 ? 1.0 : (cutoff - a_level) / cutoff; float dx, dy; if (a_cnt == 1) { dx = ccc + dist * ang_x; dy = ccc - dist * ang_y; } else { const float phase = atan2f(ang_x, ang_y); dx = ccc + dist * sinf(phase); dy = ccc - dist * cosf(phase); } draw_point(ui, cr, pk, dx, dy, 0, 0, 0); } cairo_destroy (cr); }