#include #include #include #include #include #include #include #include #include #include "dsp.c" #include #include #include enum whattodraw { whattodraw_abs, whattodraw_re, whattodraw_im }; int main (int argc, char ** argv) { if (argc != 3) error(1, 0, "argv[1] must be set. fft length will then be 2**argv[1]\nargv[2] must be set. peak value will then be argv[2] -- use a floating point."); double peak = strtod(argv[2], NULL); Display *dpy = XOpenDisplay(NULL); assert(dpy); int blackColor = BlackPixel(dpy, DefaultScreen(dpy)); int width = 200; int height = 400; Window w = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 0, 0, width, height, 0, blackColor, blackColor); // We want to get MapNotify events XSelectInput(dpy, w, StructureNotifyMask | KeyPressMask | ExposureMask | VisibilityChangeMask); // "Map" the window (that is, make it appear on the screen) XMapWindow(dpy, w); // Create a "Graphics Context" GC gc = XCreateGC(dpy, w, 0, NULL); // Tell the GC we draw using the white color XSetForeground(dpy, gc, 0xff0000); // Wait for the MapNotify event struct pollfd pollfd[2] = { { .fd = XConnectionNumber(dpy), .events = POLLIN | POLLHUP }, { .fd = STDIN_FILENO, .events = POLLIN } }; int flags = fcntl(XConnectionNumber(dpy), F_GETFL, 0); assert(flags != -1); flags |= O_NONBLOCK; assert(fcntl(XConnectionNumber(dpy), F_SETFL, flags) == 0); bool flush = false; int spectrumheight = 100; void draw_ui () { XSetForeground(dpy, gc, 0xff0000); XDrawLine(dpy, w, gc, 0, spectrumheight+1, width, spectrumheight+1); } int capturesize = 1 << atoi(argv[1]); unsigned received_bytes = 0; double samples[capturesize]; enum whattodraw whattodraw = whattodraw_abs; unsigned block = 0; while (XPending(dpy) || poll(pollfd, 2, -1) > 0) { if (pollfd[0].revents & POLLIN || XPending(dpy)) { while (XPending(dpy)) { XEvent e; XNextEvent(dpy, &e); switch (e.type) { case ConfigureNotify: width = e.xconfigure.width; height = e.xconfigure.height; break; case MapNotify: case Expose: case VisibilityChangeMask: draw_ui(); flush = true; break; case DestroyNotify: goto end; case KeyPress: switch (XLookupKeysym(&e.xkey, 0)) { case XK_Up: spectrumheight--; break; case XK_Down: spectrumheight++; break; case XK_a: case XK_A: whattodraw = whattodraw_abs; break; case XK_r: case XK_R: whattodraw = whattodraw_re; break; case XK_i: case XK_I: whattodraw = whattodraw_im; break; } draw_ui(); flush = true; break; case MappingNotify: XRefreshKeyboardMapping(&e.xmapping); break; } } } if (pollfd[1].revents & POLLIN) { int rr = read(STDIN_FILENO, ((void *) samples)+received_bytes, sizeof samples-received_bytes); if (rr == 0) { // EOF pollfd[1].events = 0; continue; } if (rr < 0) error(1, errno, "stdin read"); received_bytes += rr; if (received_bytes == capturesize*sizeof(samples[0])) { double complex complex_samples[capturesize]; for (int i = 0; i < capturesize; i++) complex_samples[i] = samples[i]; double complex spectrum[capturesize]; fft(spectrum, complex_samples, capturesize, false, 1); XSetForeground(dpy, gc, blackColor); XFillRectangle(dpy, w, gc, 0, 0, width, spectrumheight+1); XSetForeground(dpy, gc, 0x00ff00); for (int i = 0; i < capturesize; i++) { double frequency; switch (whattodraw) { case whattodraw_abs: frequency = cabs(spectrum[i]); break; case whattodraw_re: frequency = creal(spectrum[i]); break; case whattodraw_im: frequency = cimag(spectrum[i]); break; } if (frequency > peak) frequency = peak; XSetForeground(dpy, gc, 0x00ff00); XDrawLine(dpy, w, gc, i, spectrumheight, i, spectrumheight-(frequency/peak)*spectrumheight); XSetForeground(dpy, gc, (frequency/peak)*0x00ff00+(frequency/peak)*0x0000ff); XDrawPoint(dpy, w, gc, i, spectrumheight+2+block%(height-spectrumheight-2)); } int scanner = spectrumheight+2+block%(height-spectrumheight-2); XSetForeground(dpy, gc, 0xabcdef); XFillRectangle(dpy, w, gc, 0, scanner+1, capturesize, 3); flush = true; received_bytes = 0; block++; } } if (flush) { XFlush(dpy); flush = false; } } end: XCloseDisplay(dpy); }