ref: 331ab02f38db96f0a90c8bd9b195f5db01545c5a
dir: /pplay.c/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
enum{
Ndelay = 44100 / 25,
Nchunk = Ndelay * 4,
Cbg = 0,
Csamp,
Cline,
Cloop,
Ncol
};
int cat;
int T, stereo, zoom = 1;
Rectangle liner;
Point statp;
ulong nbuf;
uchar *buf, *bufp, *bufe, *viewp, *viewe, *viewmax, *loops, *loope;
Image *viewbg, *view, *col[Ncol], *disp;
Keyboardctl *kc;
Mousectl *mc;
QLock lck;
Image *
eallocimage(Rectangle r, int repl, ulong col)
{
Image *i;
if((i = allocimage(display, r, screen->chan, repl, col)) == nil)
sysfatal("allocimage: %r");
return i;
}
void
drawstat(void)
{
char s[64];
snprint(s, sizeof s, "T %d p %zd", T, bufp-buf);
string(screen, statp, col[Cline], ZP, font, s);
}
void
update(void)
{
int x;
x = screen->r.min.x + (bufp - viewp) / 4 / T;
if(liner.min.x == x || bufp < viewp && x > liner.min.x)
return;
draw(screen, screen->r, view, nil, ZP);
liner.min.x = x;
liner.max.x = x + 1;
if(bufp >= viewp)
draw(screen, liner, col[Cline], nil, ZP);
drawstat();
flushimage(display, 1);
}
void
athread(void *)
{
int n, fd;
if((fd = cat ? 1 : open("/dev/audio", OWRITE)) < 0)
sysfatal("open: %r");
for(;;){
qlock(&lck);
n = bufp + Nchunk >= loope ? loope - bufp : Nchunk;
if(write(fd, bufp, n) != n)
break;
bufp += n;
if(bufp >= loope)
bufp = loops;
update();
qunlock(&lck);
yield();
}
close(fd);
}
void
drawsamps(void)
{
int x, yl, yr, w, scal, lmin, lmax, rmin, rmax;
short s;
uchar *p, *e;
Rectangle l, r;
w = T * 4;
p = viewp;
x = 0;
yl = viewbg->r.max.y / (stereo ? 4 : 2);
yr = viewbg->r.max.y - yl;
scal = 32767 / yl;
while(p < viewe){
e = p + w;
if(e > viewe)
e = viewe;
lmin = lmax = 0;
rmin = rmax = 0;
while(p < e){
s = (short)(p[1] << 8 | p[0]);
if(s < lmin)
lmin = s;
else if(s > lmax)
lmax = s;
if(stereo){
s = (short)(p[3] << 8 | p[2]);
if(s < rmin)
rmin = s;
else if(s > rmax)
rmax = s;
}
p += 4;
}
l = Rect(x, yl - lmax / scal, x+1, yl - lmin / scal);
draw(viewbg, l, col[Csamp], nil, ZP);
if(stereo){
r = Rect(x, yr - rmax / scal, x+1, yr - rmin / scal);
draw(viewbg, r, col[Csamp], nil, ZP);
}
x++;
}
}
void
drawview(void)
{
int x;
Rectangle r;
draw(view, view->r, viewbg, nil, ZP);
if(loops != buf && loops >= viewp){
x = (loops - viewp) / 4 / T;
r = view->r;
r.min.x += x;
r.max.x = r.min.x + 1;
draw(view, r, col[Cloop], nil, ZP);
}
if(loope != bufe && loope >= viewp){
x = (loope - viewp) / 4 / T;
r = view->r;
r.min.x += x;
r.max.x = r.min.x + 1;
draw(view, r, col[Cloop], nil, ZP);
}
draw(screen, screen->r, view, nil, ZP);
draw(screen, liner, col[Cline], nil, ZP);
drawstat();
flushimage(display, 1);
}
void
redrawbg(void)
{
int w, x;
Rectangle viewr, midr;
T = nbuf / zoom / Dx(screen->r);
if(T == 0)
T = 1;
w = Dx(screen->r) * T * 4;
viewmax = bufe - w;
if(viewp < buf)
viewp = buf;
else if(viewp > viewmax)
viewp = viewmax;
viewe = viewp + w;
x = screen->r.min.x + (bufp - viewp) / 4 / T;
liner = screen->r;
liner.min.x = x;
liner.max.x = x + 1;
viewr = rectsubpt(screen->r, screen->r.min);
freeimage(viewbg);
freeimage(view);
viewbg = eallocimage(viewr, 0, DBlack);
view = eallocimage(viewr, 0, DBlack);
if(stereo){
midr = viewr;
midr.min.y = midr.max.y / 2;
midr.max.y = midr.min.y + 1;
draw(viewbg, midr, col[Csamp], nil, ZP);
statp = Pt(screen->r.min.x,
screen->r.min.y + (Dy(screen->r) - font->height) / 2 + 1);
}else
statp = Pt(screen->r.min.x, screen->r.max.y - font->height);
drawsamps();
drawview();
}
void
writepcm(int pause)
{
int fd, n, sz;
char path[256];
uchar *p;
memset(path, 0, sizeof path);
if(!pause)
qlock(&lck);
n = enter("path:", path, sizeof(path)-UTFmax, mc, kc, nil);
if(!pause)
qunlock(&lck);
if(n < 0)
return;
if((fd = create(path, OWRITE, 0664)) < 0){
fprint(2, "create: %r\n");
return;
}
if((sz = iounit(fd)) == 0)
sz = 8192;
for(p=loops; p<loope; p+=sz){
n = loope - p < sz ? loope - p : sz;
if(write(fd, p, n) != n){
fprint(2, "write: %r\n");
goto end;
}
}
end:
close(fd);
}
void
setzoom(int n)
{
int m;
m = zoom + n;
if(m < 1 || m > nbuf / Dx(screen->r))
return;
zoom = m;
if(nbuf / zoom / Dx(screen->r) != T)
redrawbg();
}
void
setpan(int n)
{
n *= T * 4 * 16;
if(zoom == 1 || viewp == buf && n < 0 || viewp == viewmax && n > 0)
return;
viewp += n;
redrawbg();
}
void
setloop(void)
{
int n;
uchar *p;
n = (mc->xy.x - screen->r.min.x) * T * 4;
p = viewp + n;
if(p < buf || p > bufe)
return;
if(p < bufp)
loops = p;
else
loope = p;
drawview();
}
void
setpos(void)
{
int n;
uchar *p;
n = (mc->xy.x - screen->r.min.x) * T * 4;
p = viewp + n;
if(p < loops || p > loope - Nchunk)
return;
bufp = p;
update();
}
void
bufrealloc(ulong n)
{
int off;
off = bufp - buf;
if((buf = realloc(buf, n)) == nil)
sysfatal("realloc: %r");
bufe = buf + n;
bufp = buf + off;
}
void
usage(void)
{
fprint(2, "usage: %s [-cs] [pcm]\n", argv0);
threadexits("usage");
}
void
threadmain(int argc, char **argv)
{
int n, sz, fd, pause;
Mouse mo;
Rune r;
ARGBEGIN{
case 'c': cat = 1; break;
case 's': stereo = 1; break;
default: usage();
}ARGEND
if((fd = *argv != nil ? open(*argv, OREAD) : 0) < 0)
sysfatal("open: %r");
if(sz = iounit(fd), sz == 0)
sz = 8192;
bufrealloc(nbuf += 4*1024*1024);
while((n = read(fd, bufp, sz)) > 0){
bufp += n;
if(bufp + sz >= bufe)
bufrealloc(nbuf += 4*1024*1024);
}
if(n < 0)
sysfatal("read: %r");
close(fd);
bufrealloc(nbuf = bufp - buf);
nbuf /= 4;
bufp = buf;
if(initdraw(nil, nil, "pplay") < 0)
sysfatal("initdraw: %r");
if(kc = initkeyboard(nil), kc == nil)
sysfatal("initkeyboard: %r");
if(mc = initmouse(nil, screen), mc == nil)
sysfatal("initmouse: %r");
col[Cbg] = display->black;
col[Csamp] = eallocimage(Rect(0,0,1,1), 1, 0x440000FF);
col[Cline] = eallocimage(Rect(0,0,1,1), 1, 0x884400FF);
col[Cloop] = eallocimage(Rect(0,0,1,1), 1, 0x777777FF);
viewp = buf;
loops = buf;
loope = bufe;
redrawbg();
pause = 0;
Alt a[] = {
{mc->resizec, nil, CHANRCV},
{mc->c, &mc->Mouse, CHANRCV},
{kc->c, &r, CHANRCV},
{nil, nil, CHANEND}
};
if(threadcreate(athread, nil, mainstacksize) < 0)
sysfatal("threadcreate: %r");
for(;;){
switch(alt(a)){
case 0:
if(getwindow(display, Refnone) < 0)
sysfatal("resize failed: %r");
redrawbg();
mo = mc->Mouse;
break;
case 1:
switch(mc->buttons){
case 1: setpos(); break;
case 2: setloop(); break;
case 4: setpan(mo.xy.x - mc->xy.x); break;
case 8: setzoom(1); break;
case 16: setzoom(-1); break;
}
mo = mc->Mouse;
break;
case 2:
switch(r){
case ' ':
if(pause ^= 1)
qlock(&lck);
else
qunlock(&lck);
break;
case 'b': bufp = loops; update(); break;
case 'r': loops = buf; loope = bufe; drawview(); break;
case Kdel:
case 'q': threadexitsall(nil);
case 'z': if(zoom == 1) break; zoom = 1; redrawbg(); break;
case '-': setzoom(-1); break;
case '=':
case '+': setzoom(1); break;
case 'w': writepcm(pause); break;
}
break;
}
}
}