ref: 1330d1fff8e87caf7a85e4d32c17feb965484bae
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 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); }