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