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);
}