ref: 6544a8049f57f231de744df73403f68428bbbe1f
parent: f1472b95fd6e0c724a2139461416b13628e0ee41
author: Sigrid Haflínudóttir <[email protected]>
date: Mon Mar 23 16:27:45 EDT 2020
add a very stupid waveform drawing code as a start
--- /dev/null
+++ b/waveform/mkfile
@@ -1,0 +1,11 @@
+</$objtype/mkfile
+
+TARG=waveform
+BIN=/$objtype/bin/daw
+
+OFILES=\
+ waveform.$O\
+
+default:V: all
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/waveform/waveform.c
@@ -1,0 +1,207 @@
+#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);
+}