shithub: neindaw

ref: 8747033fb429a67053e229b30c323ce1c5a25a54
dir: neindaw/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 nvalid;
	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 = w->nvalid = n / nchan;

	return w;
}

static u64int *
rotate9064(u64int *b, int h)
{
	u64int *p;
	int i, j;
	u64int v, i7;

	p = calloc(1, 64*h/8);

	for (i = 0; i < h; i++) {
		i7 = 1ULL<<(i^7);
		v = b[i];
		for (j = 0; j < 64; j++) {
			if (v & (1ULL<<(j^56)))
				p[j*h/64 + i/64] |= i7;
		}
	}
	for (i = 0; i < h; i++)
		p[i] = ~p[i];

	return p;
}

static u64int *
rotate90128(u64int *b, int h)
{
	u64int *p;
	int i, j;
	u64int v, v2, i7;

	p = calloc(1, 128*h/8);

	for (i = 0; i < h; i++) {
		i7 = 1ULL<<(i^7);
		v = b[i*2+0];
		v2 = b[i*2+1];
		for (j = 1; j < 65; j++) {
			if (v & (1ULL<<(j^56)))
				p[(j+63)*h/64 + i/64] |= i7;
			if (v2 & (1ULL<<(j^56)))
				p[j*h/64 + i/64] |= i7;
		}
	}
	for (i = 0; i < 128*h/64; i++)
		p[i] = ~p[i];

	return p;
}

static int
wvimage64(Waveform *w, int offset, Rectangle r, float zoom)
{
	float m, i;
	u64int *b, *p;
	int x, x2, y, incy, y2;

	offset = MIN(offset, w->nvalid);
	r = Rect(0, 0, 64, Dx(r));
	if (badrect(r))
		return -1;
	y = (Dy(r)+63) & ~63;
	b = calloc(1, 64*y);
	m = MAX(abs(w->min), abs(w->max));
	m = m > 1.0f ? 31.0f/m : 31.0f;

	for (y = 0, i = offset; y < Dy(r) && i < w->nvalid;) {
		x = 31 + m*w->samples[(int)i];
		i += zoom;
		x2 = i < w->nvalid ? 31 + m*w->samples[(int)i] : x;
		incy = (x2 + x)/2;
		y2 = y + 1;
		do {
			b[y+1] |= 1ULL<<(x^7);
			if (x == x2)
				break;
			else if (x < x2)
				x++;
			else if (x > x2)
				x--;
			else if (x == incy)
				y++;
		} while (1);
		y = y2;
	}

	y = (y+63) & ~63;
	p = rotate9064(b, y);
	free(b);
	r.max.x = y;
	r.max.y = 64;
	if (w->image == nil || !eqrect(r, w->image->r)) {
		freeimage(w->image);
		if ((w->image = allocimage(display, r, GREY1, 0, DNofill)) == nil)
			return -1;
	}

	if (loadimage(w->image, r, (void*)p, 64*y) < 0)
		fprint(2, "failed: %r\n");
	free(p);

	return 0;
}

static int
wvimage128(Waveform *w, int offset, Rectangle r, float zoom)
{
	float m, i;
	u64int *b, *p;
	int x, x2, y, incy, y2;

	offset = MIN(offset, w->nvalid);
	r = Rect(0, 0, 128, Dx(r));
	if (badrect(r))
		return -1;
	y = (Dy(r)+63) & ~63;
	b = calloc(1, 128*y);
	m = MAX(abs(w->min), abs(w->max));
	m = m > 1.0f ? 63.0f/m : 63.0f;

	for (y = 0, i = offset; y < Dy(r) && i < w->nvalid;) {
		x = m*w->samples[(int)i];
		i += zoom;
		x2 = i < w->nvalid ? m*w->samples[(int)i] : x;
		incy = (x2 + x)/2;
		y2 = y + 1;
		do {
			if (x >= 0)
				b[y*2+1] |= 1ULL<<(x^7);
			else
				b[y*2+0] |= 1ULL<<(-x^56);
			if (x == x2)
				break;
			else if (x < x2)
				x++;
			else if (x > x2)
				x--;
			else if (x == incy)
				y++;
		} while (1);
		y = y2;
	}

	y = (y+63) & ~63;
	p = rotate90128(b, y);
	free(b);
	r.max.x = y;
	r.max.y = 128;
	if (w->image == nil || !eqrect(r, w->image->r)) {
		freeimage(w->image);
		if ((w->image = allocimage(display, r, GREY1, 0, DNofill)) == nil)
			return -1;
	}

	if (loadimage(w->image, r, (void*)p, 128*y) < 0)
		fprint(2, "failed: %r\n");
	free(p);

	return 0;
}

static int offset;
static float zoom;

static void
redraw(Waveform *w)
{
	Rectangle r;

	lockdisplay(display);
	r = screen->r;
	r.min.y += Dy(r)/4;
	r.max.y -= Dy(r)/4;
	draw(screen, screen->r, display->white, nil, ZP);
	if (wvimage64(w, offset, r, zoom) == 0)
		draw(screen, r, w->image, nil, ZP);
	flushimage(display, 1);
	unlockdisplay(display);
}

static Waveform *
wvbuffer(int nsamples)
{
	Waveform *w;

	w = calloc(1, sizeof(*w));
	w->samples = malloc(nsamples*sizeof(float));
	w->min = -1.0f;
	w->max = 1.0f;
	w->nchan = 1;
	w->nframes = nsamples;
	w->rate = 44100;
	w->nvalid = 0;

	return w;
}

static void
wvproc(void *w_)
{
	int fi, fo, n, i;
	Waveform *w = w_;
	int c;

	fi = 0;
	fo = 1;
	for (c = 0;; c++) {
		if ((n = readn(fi, w->samples, sizeof(float)*w->nframes)) < 1)
			break;
		n /= sizeof(float);
		w->nvalid = n;
		for (i = 0; i < n; i++) {
			w->min = MIN(-1.0f, MIN(w->min, w->samples[i]));
			w->max = MAX(1.0f, MAX(w->max, w->samples[i]));
		}
		if (write(fo, w->samples, n*sizeof(float)) != n*sizeof(float))
			break;
		redraw(w);
	}

	threadexits(nil);
}

void
threadmain(int argc, char **argv)
{
	Waveform *w;
	Mousectl *mctl;
	Keyboardctl *kctl;
	Rune key;
	int oldo;
	Mouse m;
	int oldb;
	Point oldp;
	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 ((w = wvbuffer(1024)) == nil)
		sysfatal("%r");
		*/

	if (initdraw(nil, nil, "daw/waveform") < 0)
		sysfatal("initdraw: %r");
	display->locking = 1;
	unlockdisplay(display);
	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;

	threadsetname("daw/waveform");

	zoom = 1.0f;
	offset = 0;
	oldb = 0;
	oldo = 0;
	oldp = ZP;

	//proccreate(wvproc, w, mainstacksize);
	redraw(w);

	for (;;) {
		switch (alt(a)) {
		case 0: /* mouse */
			if (m.buttons == 1) {
				if (oldb == 0) {
					oldp = m.xy;
					oldo = offset;
				} else if (oldb == 1) {
					offset = MAX(0, oldo + (oldp.x - m.xy.x)*zoom);
					redraw(w);
				}
			}
			oldb = m.buttons;
			break;

		case 1: /* resize */
			getwindow(display, Refnone);
			redraw(w);
			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);
				break;
			case Kright:
				offset = MIN(w->nframes-1, offset+MAX(8, 8*MAX(1, 1/zoom)));
				redraw(w);
				break;
			case '-':
				zoom *= 1.1f;
				if (zoom > 32.0f)
					zoom = 32.0f;
				redraw(w);
				break;
			case '+':
				zoom /= 1.1f;
				if (zoom < 0.01f)
					zoom = 0.01f;
				redraw(w);
				break;
			}
			break;
		}
	}

end:
	threadexitsall(nil);
}