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