/* Simple dumb zoomable audio oscilloscope using ALSA alsa-utils. * * arecord sometimes craps out in the middle, especially when using * 48ksps and getting buffer overruns, so we restart it; also, I’ve * gotten lots of problems where it will fail at first because * something else still has the audio open. The 60-millisecond buffer * (-B 60000) is a balance between the risk of getting buffer overruns * and the latency and jitter imposed by a large buffer, which for * some reason defaults to 500ms. * * There is no shortage of ways you could improve this * oscilloscope — brightness adjustment, overflow protection * (saturating arithmetic, plus maybe a bit of blur) for the beam * trace, a graticule, partial screen updates at wide zooms, support * for varying sample rates, interpolation between samples for * high-frequency signals, FFT display, triggering, waveform storage, * big-endian support, 96ksps support (though my sound card’s 96ksps * support is totally fake), etc. */ #define _BSD_SOURCE /* for usleep */ #include #include #include #include #include "xshmu.h" int main() { xshmu w = xshmu_open("oscope", 1536, 1080, ""); char *cmd = "arecord -f S16_LE -r 48000 -B 60000"; FILE *pipe = popen(cmd, "r"); int ts = 1; /* timescale */ for (;;) { for (xshmu_event *ev; (ev = xshmu_get_event(w));) { if (xshmu_as_die_event(ev)) return 0; xshmu_key_event *kev = xshmu_as_key_event(ev); if (kev && kev->down) { if (ts > 1 && kev->keysym == '+') ts /= 2; if (ts <= 8 && kev->keysym == '-') ts *= 2; } } xshmu_pic fb = xshmu_framebuffer(w); memset(fb.p, 0, fb.w*fb.h*4); int b = fb.h / 2, /* Y-intercept, or offset */ mi = (32768 + b - 1) / b; /* inverse slope, or gain */ int16_t buf[65536]; int n = fread((char*)&buf, 2, fb.w*ts, pipe); for (int i = 0; i < n; i++) *xshmu_pix(fb, i/ts, b+buf[i]/mi) += 0x337755; xshmu_flush(w); if (n == 0) { /* restart arecord if it fails */ pclose(pipe); usleep(100000); /* but not without pausing */ pipe = popen(cmd, "r"); } } }