#include #include #include #include #include "abuf.h" #include "guic.h" #include "rollc.h" enum{ Gtrans, Gtype, Gcutoff, Greso, Gfltatt, Gfltrel, Goscatt, Goscrel, Gexit, }; char *keywords[] = { "trans", "type", "cutoff", "reso", "flt.att", "flt.rel", "osc.att", "osc.rel", "exit", 0, }; typedef struct Osc Osc; typedef struct Env Env; typedef struct Filter Filter; typedef struct Pattern Pattern; struct Osc { enum { Sine, Saw, Square, } type; double w, ph; }; struct Env { int on; double A, R; double v; }; struct Filter { double y1, y2; double r, c; Env env; }; void envsetup(Env *e, int A, int R) { e->A = exp(-4.0/A); e->R = exp(-4.0/R); e->v = 0; } double envnext(Env *e) { double x = 1; if(e->on == 1){ e->v = x - (x - e->v) * e->A; }else{ e->v = e->v * e->R; } return e->v; } void oscreset(Osc *osc, double freq) { osc->w = freq / SRATE; } double oscnext(Osc *osc) { double s, ph; ph = osc->ph; switch(osc->type){ case Sine: s = sin(ph * 2 * PI); break; case Square: s = (fmod(ph, 1) < 0.5) * 2 - 1; break; case Saw: s = fmod(ph, 1) * 2 - 1; break; } osc->ph += osc->w; return s; } double fltnext(Filter *f, double x0) { double y0, c; c = f->c * envnext(&f->env); y0 = c * x0 + (1 + f->r - c) * f->y1 - f->r * f->y2; if(y0 > 1) y0 = 1; else if (y0 < -1) y0 = -1; f->y2 = f->y1; f->y1 = y0; return y0; } void fltsetup(Filter *f, double r, double c) { f->y1 = f->y2 = 0; f->r = r; f->c = c; } Action act, nextact; Abuf aout; Osc osc; Env env; Filter flt; double vol; int trans; void synthreset(void) { oscreset(&osc, 440); osc.ph = 0; osc.type = Saw; envsetup(&env, SRATE / 256, SRATE / 1); env.on = 0; fltsetup(&flt, 0.8, 0.2); flt.r = 0.93; flt.c = 0.2; envsetup(&flt.env, SRATE / 4, SRATE / 4); flt.env.on = 0; vol = 1; } void synthstart(Note *note) { oscreset(&osc, miditofreq(atoi(note->name) + trans)); env.on = 1; flt.env.on = 1; vol = voltomul(note->vol); } void synthstop(void) { env.on = 0; flt.env.on = 0; } double synthnext(void) { double y0; y0 = envnext(&env) * oscnext(&osc) * vol; y0 = fltnext(&flt, y0); return y0; } void checkgui(void) { Event ev; while(nbrecv(guichan, &ev) == 1) switch(ev.k){ case Gtrans: trans = ev.n; break; case Gtype: osc.type = ev.n; break; case Gcutoff: flt.c = ev.n / 127.0; break; case Greso: flt.r = ev.n / 128.0; break; case Gfltatt: flt.env.A = exp(-4.0/(++ev.n * SRATE * 1 / 128)); break; case Gfltrel: flt.env.R = exp(-4.0/(++ev.n * SRATE * 4 / 128)); break; case Goscatt: env.A = exp(-4.0/(++ev.n * SRATE * 1 / 128)); break; case Goscrel: env.R = exp(-4.0/(++ev.n * SRATE * 4 / 128)); break; case Gexit: threadexitsall(0); } } int checkact(int block) { if(nextact.action != Anone) return 1; if(block) return recv(rollchan, &nextact); else return nbrecv(rollchan, &nextact); } void playpattern(Action *act) { Note *note; int t, len; int stop; double y0; ainit(&aout, 1); do{ stop = len = act->len; note = act->notes; for(t=0; tt == t){ synthstart(note); stop = note->t + note->len; note = note->next; } if(t == stop) synthstop(); y0 = synthnext(); aputd(&aout, y0); } }while(act->action == Arepeat); wait: /* for(;;){ y1 = y0; y0 = synthnext(); // fprint(2, "%f %d %f %d\n", y0, (sample) (y0 * SMAX), y1, (sample) (y1 * SMAX)); aputd(&aout, y0); if((y1 < 0) != (y0 < 0)) break; } */ aflush(&aout); } void threadmain(int argc, char *argv[]) { rollstart(argc > 1 ? argv[1] : 0, 120); guistart(argc > 2 ? argv[2] : 0); synthreset(); for(;;){ if(checkact(1) != 1) continue; act = nextact; nextact.action = Anone; dumpact(&act); switch(act.action){ case Aplay: case Arepeat: playpattern(&act); break; case Astop: break; case Aquit: threadexitsall(0); } } }