shithub: neindaw

ref: 6544a8049f57f231de744df73403f68428bbbe1f
dir: /waveform/waveform.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>

#define MIN(a,b) ((a)<=(b)?(a):(b))
#define MAX(a,b) ((a)>=(b)?(a):(b))

typedef struct Waveform Waveform;

struct Waveform {
	float *samples;
	Image *image;
	float min;
	float max;
	int nchan;
	int nframes;
	int rate;
};

static Waveform *
wvform(int f, int nchan, int rate)
{
	Waveform *w;
	int i, r, n, sz;

	w = calloc(1, sizeof(*w));
	w->nchan = nchan;
	w->rate = rate;
	sz = 128*nchan;
	w->samples = malloc(sz*sizeof(float));
	for (n = 0;;) {
		if (sz-n < 1) {
			sz *= 2;
			w->samples = realloc(w->samples, sz*sizeof(float));
		}
		if ((r = read(f, w->samples+n, (sz-n)*sizeof(float))) < 0) {
			free(w->samples);
			free(w);
			return nil;
		}
		r /= sizeof(float);
		if (r == 0)
			break;
		if (n == 0)
			w->min = w->max = w->samples[0];
		for (i = 0; i < r; i++, n++) {
			w->min = MIN(w->min, w->samples[n]);
			w->max = MAX(w->max, w->samples[n]);
		}
	}

	w->samples = realloc(w->samples, n*sizeof(float));
	w->nframes = n / nchan;

	return w;
}

static int
wvimage(Waveform *w, int offset, int nframes, Rectangle r, float zoom)
{
	int i, yd, yi, bsz, xdone, ydone;
	float x, ox, oyi, dyi;
	u8int *b, col;

	r = Rect(0, 0, Dx(r), Dy(r));
	freeimage(w->image);
	if ((w->image = allocimage(display, r, GREY8, 0, DNofill)) == nil)
		return -1;
	bsz = Dx(r)*Dy(r);
	if ((b = malloc(bsz)) == nil) {
		freeimage(w->image);
		w->image = nil;
		return -1;
	}
	memset(b, 0xff, bsz);

	yd = Dy(r)/2;
	if (w->max > 1.0f)
		yd /= w->max;
	yd--;
	oyi = yd;
	for (ox = x = 0, i = offset; i < offset+nframes; i++, x += 1.0f/zoom) {
		yi = yd + w->samples[i*w->nchan+0] * yd;
		if (yi >= 0 && yi < Dy(r) && x < Dx(r))
			b[(int)x + yi*Dx(r)] = 0;
		dyi = (yi < oyi ? -1.0f : 1.0f)/MAX(1.0f, zoom);
		xdone = ydone = 0;
		col = MIN(0x80, zoom*(abs(yi - oyi) + abs(x - ox)));
		while (ox < Dx(r) && (!xdone || !ydone)) {
			b[(int)ox + (int)oyi*Dx(r)] = col;
			if (ox < x)
				ox = MIN(ox + 1.0f/MAX(1.0f, zoom), x);
			else
				xdone = 1;
			if ((dyi > 0 && oyi < yi) || (dyi < 0 && oyi > yi))
				oyi = MAX(0, MIN(oyi+dyi, Dy(r)-1));
			else
				ydone = 1;
		}
		if (x >= Dx(r))
			break;
		ox = x;
		oyi = yi;
	}

	return loadimage(w->image, r, b, bsz);
}

static void
redraw(Waveform *w, int offset, float zoom)
{
	Rectangle r;

	r = screen->r;
	r.min.y += Dy(r)/4;
	r.max.y -= Dy(r)/4;
	if (wvimage(w, offset, w->nframes-offset, r, zoom) < 0)
		sysfatal("couldn't create image: %r");

	draw(screen, r, w->image, nil, ZP);
	flushimage(display, 1);
}

void
threadmain(int argc, char **argv)
{
	Waveform *w;
	Mousectl *mctl;
	Keyboardctl *kctl;
	Rune key;
	float zoom;
	int offset;
	Mouse m;
	Alt a[] = {
		{ nil, &m, CHANRCV },
		{ nil, nil, CHANRCV },
		{ nil, &key, CHANRCV },
		{ nil, nil, CHANEND },
	};

	USED(argc); USED(argv);

	if ((w = wvform(0, 1, 44100)) == nil)
		sysfatal("%r");

	if (initdraw(nil, nil, "daw/waveform") < 0)
		sysfatal("initdraw: %r");
	if ((mctl = initmouse(nil, screen)) == nil)
		sysfatal("initmouse: %r");
	if ((kctl = initkeyboard(nil)) == nil)
		sysfatal("initkeyboard: %r");

	a[0].c = mctl->c;
	a[1].c = mctl->resizec;
	a[2].c = kctl->c;

	srand(time(0));
	threadsetname("daw/cfg");

	zoom = 1.0f;
	offset = 0;
	redraw(w, offset, zoom);
	for (;;) {
		switch (alt(a)) {
		case 0: /* mouse */
			break;

		case 1: /* resize */
			getwindow(display, Refnone);
			redraw(w, offset, zoom);
			break;

		case 2: /* keyboard */
			switch (key) {
			case Kdel:
				goto end;
			case Kleft:
				offset = MAX(0, offset-MAX(8, 8*MAX(1, 1/zoom)));
				redraw(w, offset, zoom);
				break;
			case Kright:
				offset = MIN(w->nframes-1, offset+MAX(8, 8*MAX(1, 1/zoom)));
				redraw(w, offset, zoom);
				break;
			case '-':
				zoom *= 2.0f;
				if (zoom > 32.0f)
					zoom = 32.0f;
				redraw(w, offset, zoom);
				break;
			case '+':
				zoom /= 2.0f;
				if (zoom < 0.01f)
					zoom = 0.01f;
				redraw(w, offset, zoom);
				break;
			}
			break;
		}
	}

end:
	threadexitsall(nil);
}