summaryrefslogblamecommitdiffstats
path: root/prog/baozveza/vis.c
blob: 02a4e99774c9599871402a3cad0dfd3eb8fed4e7 (plain) (tree)




































































































































































                                                                                                                                                                      
#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);
}