ref: 926aff456d7ee4318c86f6757c38c8a0174dbbf7
dir: /pitch.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include "kiss_fftr.h"
typedef struct Note Note;
struct Note {
char *s;
Image **ct;
int f;
Image **cl;
};
static s16int window[2048];
static kiss_fft_cpx freq[nelem(window)/2+1];
static Image *ct, *ct2, *cl, *cl2, *cf;
static Note notes[] = {
{"A5", &ct, 880, &cl},
{"G5", &ct2, 784, &cl2},
{"F5", &ct2, 698, &cl2},
{"E5", &ct2, 659, &cl2},
{"D5", &ct2, 587, &cl2},
{"C5", &ct2, 523, &cl2},
{"B4", &ct2, 494, &cl2},
{"A4", &ct, 440, &cl},
{"G4", &ct2, 392, &cl2},
{"F4", &ct2, 349, &cl2},
{"E4", &ct2, 330, &cl2},
{"D4", &ct2, 293, &cl2},
{"C4", &ct2, 261, &cl2},
{"B3", &ct2, 246, &cl2},
{"A3", &ct, 220, &cl},
{"G3", &ct2, 196, &cl2},
{"F3", &ct2, 175, &cl2},
{"E3", &ct2, 165, &cl2},
{"D3", &ct2, 147, &cl2},
{"C3", &ct2, 130, &cl2},
{"B2", &ct2, 123, &cl2},
{"A2", &ct, 110, &cl},
{"A1", &ct, 55, &cl},
};
#define last (nelem(notes)-1)
static void
redraw(int freq, int full)
{
static int w;
static double m;
static Image *data;
static Point p0;
static int nosignal;
int i, x, rx;
Rectangle r, dr;
Point p;
Font *f;
f = display->defaultfont;
r = screen->r;
r.min.x += stringwidth(f, " ");
r.min.y += f->height;
r.max.x -= stringwidth(f, " ");
r.max.y -= f->height*2;
rx = r.max.x;
p = r.min;
if(full){
draw(screen, screen->r, display->black, nil, ZP);
freeimage(data);
data = allocimage(display, screen->r, RGB24, 0, DNofill);
w = 0;
for(i = 0; i < nelem(notes); i++){
if(w < (x = 2*stringwidth(f, notes[0].s)))
w = x;
}
m = Dy(r) / fabs(notes[0].f - notes[last].f);
}
r.max.x -= w;
for(i = 0; i < nelem(notes); i++){
p.y = r.min.y + m * fabs(notes[0].f - notes[i].f);
if(full)
string(screen, p, *notes[i].ct, ZP, f, notes[i].s);
line(
screen,
addpt(p, Pt(full ? w : r.max.x-p.x-1, f->height/2)),
addpt(p, Pt(r.max.x-p.x, f->height/2)),
Endsquare, Endsquare, 0,
*notes[i].cl,
ZP
);
if(full)
string(screen, Pt(rx-w/2, p.y), *notes[i].ct, ZP, f, notes[i].s);
}
if(freq <= notes[0].f && freq >= notes[last].f){
nosignal = 0;
p = Pt(r.max.x - 1, r.min.y + m * fabs(notes[0].f - freq));
line(screen, p, p0.x ? p0 : addpt(p, Pt(1,0)), Endsquare, Endsquare, 0, cf, ZP);
p0 = p;
}else if(nosignal++ > 4){
p0 = ZP;
}
dr = screen->r;
dr.min.x = -1;
dr.min.y = 0;
r.max.x++;
r.min.x += w;
replclipr(screen, 0, r);
draw(screen, dr, display->screenimage, nil, ZP);
flushimage(display, 1);
replclipr(screen, 0, screen->r);
}
static void
usage(void)
{
fprint(2, "usage: %s [/dev/audio]\n", argv0);
threadexitsall("usage");
}
void
threadmain(int argc, char **argv)
{
int f, n, i, x, ox[8], max, avg;
kiss_fftr_cfg *c;
Keyboardctl *kctl;
Mousectl *mctl;
Mouse m;
Rune r;
enum {
Ckey,
Cmouse,
Cresize,
Numchan,
};
Alt a[Numchan+1] = {
[Ckey] = { nil, &r, CHANRCV },
[Cmouse] = { nil, &m, CHANRCV },
[Cresize] = { nil, nil, CHANRCV },
{ nil, nil, CHANNOBLK },
};
ARGBEGIN{
default:
usage();
}ARGEND
if(argc > 1)
usage();
if((f = open(argc ? argv[0] : "/dev/audio", OREAD)) < 0)
sysfatal("%r");
if((c = kiss_fftr_alloc(nelem(window), 0, nil, nil)) == nil)
sysfatal("failed to get fft config");
if(initdraw(nil, nil, "pitch") < 0)
sysfatal("initdraw: %r");
if ((mctl = initmouse(nil, screen)) == nil)
sysfatal("initmouse: %r");
if((kctl = initkeyboard(nil)) == nil)
sysfatal("initkeyboard: %r");
a[Ckey].c = kctl->c;
a[Cmouse].c = mctl->c;
a[Cresize].c = mctl->resizec;
threadsetname("pitch");
ct = display->white;
ct2 = allocimage(display, Rect(0,0,1,1), RGB24, 1, DWhite/3);
cl = allocimage(display, Rect(0,0,1,1), RGB24, 1, DGreen);
cl2 = allocimage(display, Rect(0,0,1,1), RGB24, 1, DGreen/3);
cf = allocimage(display, Rect(0,0,1,1), RGB24, 1, DYellow);
redraw(0, 1);
memset(ox, 0, sizeof(ox));
for(;;){
if((n = readn(f, window, sizeof(window))) < 0)
sysfatal("%r");
if(n == 0)
break;
if(kiss_fftr(c, window, freq) != 0)
sysfatal("%r");
for(i = 0, x = 0, max = 5, avg = 0; i < nelem(freq); i++){
if(freq[i].r > max){
x = (44100/nelem(freq))*i/2;
max = freq[i].r;
}
avg += freq[i].r;
}
avg /= i;
if(max > avg+5){
ox[0] = x;
for(i = x = 0; i < nelem(ox); i++)
x += ox[i];
x /= nelem(ox);
memmove(ox+1, ox, sizeof(ox)-sizeof(ox[0]));
}else{
x = 0;
}
switch(alt(a)){
case Ckey:
if(r == Kdel || r == 'q')
goto done;
break;
case Cmouse:
break;
case Cresize:
getwindow(display, Refnone);
redraw(x, 1);
continue;
}
redraw(x, 0);
}
done:
kiss_fftr_free(c);
threadexitsall(nil);
}