/* WebAssembly Example 5 - WEFAX decoder Copyright 2019 Ahmet Inan */ #include "quirks.hh" #include "atan2.hh" #include "complex.hh" #include "const.hh" #include "ema.hh" #include "fmd.hh" #include "phasor.hh" #include "example.hh" const int INPUT_MAX = 16384; float input[INPUT_MAX]; typedef DSP::Complex complex_type; DSP::EMACascade lowpass; DSP::Phasor phasor; DSP::FMD1 demod; const int FAX_WIDTH = 1024; const int FAX_HEIGHT = 1024; const int FAX_LENGTH = FAX_WIDTH * FAX_HEIGHT; int pixels[FAX_LENGTH]; float line[FAX_WIDTH]; int sample_rate = 0; int lines_per_minute = 120; int samples_per_line = 0; int current_position = 0; void _start() { for (int i = 0; i < FAX_LENGTH; ++i) pixels[i] = 0xff000000; } int gray(float v) { v = min(max(v, 0.f), 1.f); v = sqrt(v); int V = nearbyint(255.f * v); return 0xff000000|(0x00010101*V); } void new_line() { for (int j = 0; j < FAX_HEIGHT-1; ++j) for (int i = 0; i < FAX_WIDTH; ++i) pixels[FAX_WIDTH*j+i] = pixels[FAX_WIDTH*(j+1)+i]; for (int i = 0; i < FAX_WIDTH; ++i) pixels[FAX_WIDTH*(FAX_HEIGHT-1)+i] = gray(0.5f + 0.5f * line[i]); } void process_input(int samples) { for (int i = 0; i < samples; ++i) { int pos = (current_position * FAX_WIDTH) / samples_per_line; line[pos] = demod(lowpass(input[i] * phasor())); if (++current_position >= samples_per_line) { current_position = 0; new_line(); } } } void commit_changes() { lowpass.cutoff(400, sample_rate); phasor.omega(-1900, sample_rate); demod.bandwidth(800.f / sample_rate); float seconds_per_line = 60.f / lines_per_minute; samples_per_line = seconds_per_line * sample_rate; } void change_lpm(int lpm) { lines_per_minute = lpm; commit_changes(); } void change_rate(int rate) { sample_rate = rate; commit_changes(); } int input_length() { return INPUT_MAX; } float *input_pointer() { return input; } int fax_length() { return FAX_LENGTH; } int fax_width() { return FAX_WIDTH; } int fax_height() { return FAX_HEIGHT; } int *fax_pointer() { return pixels; }