ref: a6875be771d0b79f59abe6529231d294094b4a23
dir: /piper/piper.c/
#include <u.h>
#include <libc.h>
#include <thread.h>
#include <bio.h>
#include "common.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 = 0.25;
static QLock grouplock;
static Group *groups;
static int numgroups;
static int audio;
static int record = -1;
static int rate = RATE;
static Synth *synths[] = {
&ay_3_8910,
&kick_drum,
&piano,
};
static void
usage(void)
{
print("usage: %s [-m] [-r rate] [-t 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;
}
static void
mixer(void *)
{
int i, j, n, bufframes;
s16int *pcm;
float *out, *x, f;
Inst *inst;
Group *g;
bufframes = rate/100;
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] * vol;
if (f >= 1.0)
pcm[n] = 32767;
else if (f <= -1.0)
pcm[n] = -32768;
else
pcm[n] = f * 32767.0;
}
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':
rate = atoi(EARGF(usage()));
if (rate < 1)
sysfatal("invalid rate %d", rate);
break;
case 't':
to = EARGF(usage());
break;
default:
usage();
}ARGEND;
if (argc < 1)
usage();
if (!nomixer && to != nil) {
fprint(2, "can't record with the mixer disabled\n");
threadexitsall("fail");
}
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);
}