#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include <assert.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include "dsp.c"
#include <error.h>
#include <stdlib.h>
#include <errno.h>
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);
}