ref: 57f49b5ea9632be13d0b695a2932fc7e6ed7ac0b
dir: /piper/piper.c/
#include <u.h> #include <libc.h> #include <thread.h> #include <bio.h> #include "piper.h" #include "util.h" typedef struct Inst Inst; typedef struct Group Group; struct Inst { void *aux; int data; int id; int numout; }; struct Group { char *path; Synth *synth; Inst *inst; int numinst; int clone; }; static int bpm = 120; static float bar = 2.0; static float vol = 1.0; static QLock grouplock; static Group *groups; static int numgroups; static int audio; static int record = -1; static Synth *synths[] = { &ay_3_8910, &kick_drum, &piano, }; static void usage(void) { print("usage: %s [-m] [-r FILE] DIR...\n", argv0); threadexitsall("usage"); } static Inst * getinst(Group *g, int id) { Inst *inst; void *aux; char *path, *s, tmp[8]; Biobuf *b; int i, n, f; for (i = 0; i < g->numinst; i++) { if (id == g->inst[i].id) return &g->inst[i]; } for (i = g->numinst; i <= id;) { /* first see if it already exists */ if ((path = smprint("%s/%d", g->path, i)) == nil) sysfatal("memory"); if ((f = open(path, OREAD)) >= 0) close(f); free(path); if (f < 0) { /* doesn't exist, clone */ seek(g->clone, 0, 0); if ((n = read(g->clone, tmp, sizeof(tmp))) < 1) sysfatal("clone failed"); tmp[n] = 0; i = atoi(tmp); } if ((path = smprint("%s/%d/%s", g->path, i, g->synth->name)) == nil) sysfatal("memory"); if ((aux = g->synth->alloc(path)) == nil) sysfatal("couldn't alloc instance: %r"); free(path); if ((path = smprint("%s/%d/data", g->path, i)) == nil) sysfatal("memory"); if ((f = open(path, OREAD)) < 0) sysfatal("couldn't open data: %r"); free(path); if ((g->inst = realloc(g->inst, sizeof(Inst)*(g->numinst+1))) == nil) sysfatal("memory"); inst = &g->inst[g->numinst]; memset(inst, 0, sizeof(*inst)); inst->data = f; inst->id = i; inst->aux = aux; g->numinst++; path = smprint("%s/%d/ctl", g->path, i); if ((b = Bopen(path, OREAD)) == nil) sysfatal("%r"); free(path); while ((s = Brdline(b, '\n')) != nil) { if (strncmp(s, "numout\t", 7) == 0) { s[Blinelen(b)-1] = 0; inst->numout = atoi(s+7); break; } } Bterm(b); if (inst->numout != 1 && inst->numout != 2) sysfatal("%s/%d: %d channels", g->path, i, inst->numout); if (i == id) return inst; if (f >= 0) i++; } return nil; } static char * parse(char *s, Group *g) { Cmd c; char *e; Inst *inst; int i, n; float f; for (i = 1; s[i] != 0 && s[i] != '\n' && s[i] != ';'; i++); e = (s[i] == 0 || s[i] == '\n') ? nil : s + i + 1; s[i] = 0; if (g != nil) { s++; if ((inst = getinst(g, i36(*s++))) != nil) { memset(&c, 0, sizeof(c)); c.type = -1; n = 0; nextnote: f = 1.0; if (*s == '!') { f = -1.0; s++; } if ((*s >= 'a' && *s <= 'g') || (*s >= 'A' && *s <= 'G')) { if (s[1] != 0) { f *= note2freq(s[0], s[1]); s += 2; c.type = CmdNote; c.note[n].freq = f; c.note[n].vel = 1.0; c.note[n].dur = bar / 16.0; if (*s != 0) { c.note[n].vel = (float)i36(*s) / 16.0; s++; if (*s != 0) { f = i36(*s); c.note[n].dur = bar / (f != 0 ? f : 1); s++; } } n++; if (*s == ':') { s++; /* for the lack of better ideas, overwrite */ if (n >= nelem(c.note)) n--; goto nextnote; } } c.numnotes = n; } else if (strncmp(s, "vol", 3) == 0) { c.type = CmdVol; c.vol = atof(s+3) / 100.0; } else if (*s != 0) { c.type = CmdRaw; c.raw = s; } if (c.type >= 0) g->synth->cmd(inst->aux, &c); } } else if (strncmp(s, "bpm", 3) == 0) { if ((i = atoi(s+3)) > 0) { bpm = i; bar = 240.0 / (float)bpm; } } else if (strncmp(s, "vol", 3) == 0) { vol = atof(s+3) / 100.0; } return e; } enum { Bufframes = 44100/100, }; static void mixer(void *) { int i, j, n; s16int *pcm; float *out, *x, f; Inst *inst; Group *g; pcm = malloc(2*Bufframes*sizeof(*pcm)); /* stereo */ out = malloc(2*Bufframes*sizeof(*out)); x = malloc(2*Bufframes*sizeof(*x)); for (;;) { memset(out, 0, 2*Bufframes*sizeof(*out)); qlock(&grouplock); for (i = 0; i < numgroups; i++) { g = &groups[i]; for (j = 0; j < g->numinst; j++) { inst = &g->inst[j]; if (readn(inst->data, x, inst->numout*Bufframes*sizeof(*x)) < 1) break; if (inst->numout == 1) { for (n = 0; n < Bufframes; n++) { out[n*2+0] += x[n]; out[n*2+1] += x[n]; } } else { for (n = 0; n < 2*Bufframes; n++) out[n] += x[n]; } } } qunlock(&grouplock); for (n = 0; n < 2*Bufframes; n++) { f = out[n] * 8192.0 * vol; if (f > 32767.0) f = 32767.0; else if (f < -32767.0) f = -32767.0; pcm[n] = f; } if (write(audio, pcm, 2*Bufframes*sizeof(*pcm)) < 0) { fprint(2, "audio: %r\n"); break; } if (record >= 0 && write(record, pcm, 2*Bufframes*sizeof(*pcm)) < 0) { fprint(2, "record: %r\n"); break; } } threadexits(nil); } void threadmain(int argc, char **argv) { char *s, t[256], *to; Synth *synth; Biobuf *b; int i, j, n, nomixer; nomixer = 0; to = nil; ARGBEGIN{ case 'm': nomixer = 1; break; case 'r': to = EARGF(usage()); break; default: usage(); }ARGEND; if (argc < 1) usage(); if (!nomixer && (audio = open("/dev/audio", OWRITE)) < 0) sysfatal("%r"); if (to != nil && (record = create(to, OWRITE|OTRUNC, 0644)) < 0) sysfatal("%r"); quotefmtinstall(); setfcr(FPPDBL|FPRNR); /* go through all groups */ for (i = 0; i < argc; i++) { /* search for specific synth handler by its name */ s = smprint("%s/metadata", argv[i]); if ((b = Bopen(s, OREAD)) == nil) sysfatal("%r"); free(s); synth = nil; while ((s = Brdline(b, '\n')) != nil) { if (strncmp(s, "name\t", 5) == 0) { s[Blinelen(b)-1] = 0; for (j = 0; j < nelem(synths) && strcmp(synths[j]->name, s+5) != 0; j++); if (j >= nelem(synths)) sysfatal("unknown synth %q\n", s+5); if ((groups = realloc(groups, sizeof(Group)*(numgroups+1))) == nil) sysfatal("memory"); synth = synths[j]; memset(&groups[numgroups], 0, sizeof(Group)); if ((s = smprint("%s/clone", argv[i])) == nil) sysfatal("memory"); if ((groups[numgroups].clone = open(s, OREAD)) < 0) sysfatal("%r"); free(s); groups[numgroups].path = argv[i]; groups[numgroups++].synth = synth; break; } } Bterm(b); if (synth == nil) sysfatal("no name set in %s/metadata", argv[i]); } if (!nomixer) proccreate(mixer, nil, 4096); for (;;) { if ((n = read(0, t, sizeof(t)-1)) < 1) break; t[n] = 0; for (s = t; s != nil && *s;) { i = i36(s[0]); qlock(&grouplock); s = parse(s, i < numgroups ? &groups[i] : nil); qunlock(&grouplock); } } threadexitsall(nil); }