ref: 6544a8049f57f231de744df73403f68428bbbe1f
dir: /waveform/waveform.c/
#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); }