ref: aa05c704e7f080980bfd3ac3483d3660b7cbc29e
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 u8int sh[65] = {
0,
56, 57, 58, 59, 60, 61, 62, 63,
48, 49, 50, 51, 52, 53, 54, 55,
40, 41, 42, 43, 44, 45, 46, 47,
32, 33, 34, 35, 36, 37, 38, 39,
24, 25, 26, 27, 28, 29, 30, 31,
16, 17, 18, 19, 20, 21, 22, 23,
8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7,
};
static u64int *
rotate90128(u64int *b, int h)
{
u64int *p;
int i, j;
u64int v;
p = calloc(1, 128*h/8);
for (i = 0; i < h; i++) {
if ((v = b[i*2+1]) != 0) {
for (j = 0; j < 64; j++) {
if (v & (1ULL<<sh[j+1]))
p[j*h/64 + i/64] |= 1ULL<<(63-sh[(i&63)+1]);
}
}
if ((v = b[i*2+0]) != 0) {
for (j = 0; j < 64; j++) {
if (v & (1ULL<<sh[j+1]))
p[(j+64)*h/64 + i/64] |= 1ULL<<(63-sh[(i&63)+1]);
}
}
}
for (i = 0; i < 128*h/64; i++)
p[i] = ~p[i];
return p;
}
static int
wvimage128(Waveform *w, int offset, int nframes, Rectangle r, float zoom)
{
float m, i;
u64int *b, *p;
int x, x2, y, incy, y2;
r = Rect(0, 0, 128, MIN(nframes-offset, Dx(r)));
if (badrect(r))
return -1;
r.max.y += 64;
b = calloc(1, 128*Dy(r));
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 < nframes;) {
x = m*w->samples[(int)(i*w->nchan)+0];
i += zoom;
x2 = i < nframes ? m*w->samples[(int)(i*w->nchan)+0] : x;
incy = x == x2 ? -999 : (x2 + x)/2;
y2 = y + 1;
do {
if (x >= 0)
b[y*2+1] |= 1ULL<<sh[64-x];
else
b[y*2+0] |= 1ULL<<sh[-x];
if (x < x2)
x++;
else if (x > x2)
x--;
if (x == incy)
y++;
} while (x2 != x);
y = y2;
}
y -= (y & 63);
y += 64;
p = rotate90128(b, y);
free(b);
r.max.x = y;
r.max.y = 128;
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
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;
}
loadimage(w->image, r, b, bsz);
free(b);
return 0;
}
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;
wvimage128(w, offset, w->nframes-offset, r, zoom);
draw(screen, screen->r, display->white, nil, ZP);
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, 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 (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);
oldb = 0;
oldo = 0;
oldp = ZP;
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, offset, zoom);
}
}
oldb = m.buttons;
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 *= 1.1f;
if (zoom > 32.0f)
zoom = 32.0f;
redraw(w, offset, zoom);
break;
case '+':
zoom /= 1.1f;
if (zoom < 0.01f)
zoom = 0.01f;
redraw(w, offset, zoom);
break;
}
break;
}
}
end:
threadexitsall(nil);
}