/* WebAssembly Example 2 - Dummy spectrum analyzer Copyright 2019 Ahmet Inan */ #include "quirks.hh" #include "complex.hh" #include "example.hh" #include "window.hh" #include "coeffs.hh" #include "decibel.hh" #include "fft.hh" static const int BINS = 2205; static const int SPECTRUM_WIDTH = BINS / 2; static const int SPECTRUM_HEIGHT = max(SPECTRUM_WIDTH / 4, 64); static const int SPECTROGRAM_WIDTH = SPECTRUM_WIDTH; static const int SPECTROGRAM_HEIGHT = SPECTRUM_HEIGHT; int spectrum_rgba[SPECTRUM_WIDTH * SPECTRUM_HEIGHT]; int spectrogram_rgba[SPECTROGRAM_WIDTH * SPECTROGRAM_HEIGHT]; value_type input[BINS]; complex_type inp[BINS], out[BINS]; DSP::Hann window; // acts like a lowpass with 1/(BINS-1) cutoff DSP::Lanczos filter; DSP::Coeffs win(&window, &filter); DSP::FastFourierTransform fwd; bool show_rainbow = true; bool show_phosphor = true; int draw_color = 0; // only made for abs(x0-x1) <= 1 void draw_line(int x0, int y0, int x1, int y1, int c) { int a0 = min(y0, (y0 + y1) / 2); int a1 = max(y0, (y0 + y1) / 2); for (int y = a0; y <= a1; ++y) spectrum_rgba[SPECTRUM_WIDTH * y + x0] = c; int b0 = min((y0 + y1) / 2, y1); int b1 = max((y0 + y1) / 2, y1); for (int y = b0; y <= b1; ++y) spectrum_rgba[SPECTRUM_WIDTH * y + x1] = c; } int rgba(float r, float g, float b, float a) { r = min(max(r, 0.f), 1.f); g = min(max(g, 0.f), 1.f); b = min(max(b, 0.f), 1.f); a = min(max(a, 0.f), 1.f); r = sqrt(r); g = sqrt(g); b = sqrt(b); //a = sqrt(a); int R = nearbyint(255.f * r); int G = nearbyint(255.f * g); int B = nearbyint(255.f * b); int A = nearbyint(255.f * a); return (A << 24) | (B << 16) | (G << 8) | (R << 0); } int alpha(float a) { a = min(max(a, 0.f), 1.f); int A = nearbyint(255.f * a); return A << 24; } int rainbow(float v) { v = min(max(v, 0.f), 1.f); float r = 4.f * v - 2.f; float g = 1.f - 4.f * abs(v - .5f); float b = 2.f - 4.f * v; float a = 4.f * v; return rgba(r, g, b, a); } void _start() { for (int j = 0; j < SPECTROGRAM_HEIGHT; ++j) for (int i = 0; i < SPECTROGRAM_WIDTH; ++i) spectrogram_rgba[SPECTROGRAM_WIDTH*j+i] = rainbow((float)i/SPECTROGRAM_WIDTH); } void stft() { for (int i = 0; i < BINS; ++i) inp[i] = win[i] * input[i]; fwd(out, inp); if (show_phosphor) { for (int i = 0; i < SPECTRUM_HEIGHT * SPECTRUM_WIDTH; ++i) spectrum_rgba[i] = draw_color | (((((spectrum_rgba[i]>>24)&255)*7)>>3)<<24); } else { for (int i = 0; i < SPECTRUM_HEIGHT * SPECTRUM_WIDTH; ++i) spectrum_rgba[i] = draw_color; } for (int b = 0, i0, j0; b < SPECTRUM_WIDTH; ++b) { float dB = DSP::decibel(norm(out[b])); float scale = (SPECTRUM_HEIGHT-1) / (-60.f); int v = nearbyint(scale * dB); int i1 = b; int j1 = min(max(v, 0), SPECTRUM_HEIGHT-1); if (b) draw_line(i0, j0, i1, j1, 0xff000000 | draw_color); i0 = i1; j0 = j1; } for (int j = SPECTROGRAM_HEIGHT-1; j; --j) for (int i = 0; i < SPECTROGRAM_WIDTH; ++i) spectrogram_rgba[SPECTROGRAM_WIDTH*j+i] = spectrogram_rgba[SPECTROGRAM_WIDTH*(j-1)+i]; for (int b = 0; b < SPECTROGRAM_WIDTH; ++b) { float dB = DSP::decibel(norm(out[b])); float v = (1.f/(-60.f)) * dB; if (show_rainbow) spectrogram_rgba[b] = rainbow(1.f-v); else spectrogram_rgba[b] = alpha(1.f-v) | draw_color; } } void toggle_rainbow() { show_rainbow = !show_rainbow; } void toggle_phosphor() { show_phosphor = !show_phosphor; } void change_color(int color) { draw_color = 0x00ffffff & color; } int input_length() { return BINS; } float *input_pointer() { return input; } int spectrum_length() { return SPECTRUM_WIDTH * SPECTRUM_HEIGHT; } int spectrum_width() { return SPECTRUM_WIDTH; } int spectrum_height() { return SPECTRUM_HEIGHT; } int *spectrum_pointer() { return spectrum_rgba; } int spectrogram_length() { return SPECTROGRAM_WIDTH * SPECTROGRAM_HEIGHT; } int spectrogram_width() { return SPECTROGRAM_WIDTH; } int spectrogram_height() { return SPECTROGRAM_HEIGHT; } int *spectrogram_pointer() { return spectrogram_rgba; }