stew

a monorepo of some sort
Log | Files | Refs

commit de2c1409887cf7f1563c0d2bc4c89d6f0f968785
parent fd1313e58cce4cf2020ff9d53b130a9c9d4c4984
Author: rpa <rpa@laika>
Date:   Sun,  8 Jan 2023 21:37:39 +0000

wave: switch from awkward envelope editing to microprogramming modifiers with hexes

Diffstat:
Msrc/wave/inst.c | 302++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msrc/wave/mkfile | 4++--
Msrc/wave/piano.c | 40++--------------------------------------
Asrc/wave/prog.c | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/wave/prog.h | 44++++++++++++++++++++++++++++++++++++++++++++
Msrc/wave/smoke.c | 39+++++++++++++++++++++++++--------------
Msrc/wave/util.h | 14+++++++++-----
Msrc/wave/wavetable.c | 63++++++++++++++-------------------------------------------------
Msrc/wave/wavetable.h | 31+------------------------------
9 files changed, 409 insertions(+), 226 deletions(-)

diff --git a/src/wave/inst.c b/src/wave/inst.c @@ -1,7 +1,29 @@ /* - Visual instrument editor + visual instrument editor loads/modifies/saves envelopes allows previewing settings by playing notes + + LINE: + cmd param loop + [b] [2b] X 5 + [b] + + or + + cmd param + [2b] [2b] X 4 + + PROG: + 128 lines + + cmd selects modifier and if value is absolute or relative + might also use for control commands? + modifiers: volume, frequency, wavetable, panoram + might also use extra mods for filter? + param is s16int + loop - negative values are jump + + TOTAL: + 2048 bytes per instrument's program, nice */ #include <u.h> @@ -9,24 +31,37 @@ #include <thread.h> #include <draw.h> #include <mouse.h> +#include <cursor.h> #include <keyboard.h> #include "wavetable.h" +#include "prog.h" +#include "util.h" enum { NameBufSize = 256, OutBufSize = 882, + KbdBufSize = 64, }; +typedef struct FBuf FBuf; typedef struct Widget Widget; typedef struct UI UI; +struct FBuf { + char path[NameBufSize]; + long n; + char *p; +}; + struct UI { Mousectl *mctl; Mouse mv; int rv[2]; Keyboardctl *kctl; Rune kv; + int env_n; + int env_node; Widget *hot; Widget **wp; }; @@ -35,75 +70,85 @@ struct Widget { Rectangle r; Image **palette; void *aux; - void (*draw)(void *); + void (*draw)(UI *, void *); void (*activate)(UI *, void *); }; -struct wavetable { - s16int *p; - char fname[NameBufSize]; -}; - -struct synth { - Inst inst; -}; - void usage(void); void threadsynth(void *); +void threadkbd(void *); void handlemouse(UI *); -void wtdraw(void *); -void wtactivate(UI *, void *); -int loadwavetable(void *, char *); +int loadfile(char *, void *, long); void rcalc(UI *); Widget * findhot(UI *); +void drawwidgets(UI *); +void wxfiledraw(UI *, void *); +void wxfileactivate(UI *, void *); + +const Rectangle ZR = {0, 0, 1, 1}; + +FBuf wavetable, prog; +UI ui; +Widget wxf_wt, wxf_prog; +Image *palette[8]; +Channel *synthc; void threadmain(int argc, char **argv) { - struct synth synth; - struct wavetable wavetable; - Image *palette[8]; - UI UI; - Widget wt; ARGBEGIN{ default: usage(); } ARGEND - if (argc != 1) usage(); + if (argc != 2) usage(); - wavetable.p = mallocz(sizeof(s16int) * WFSize * WTSize, 1); - if (loadwavetable(wavetable.p, argv[0]) != 0) { + wavetable.n = sizeof(s16int) * WFSize * WTSize; + wavetable.p = mallocz(wavetable.n, 1); + if (loadfile(argv[0], wavetable.p, wavetable.n) != 0) { fprint(2, "loading %s\n", argv[0]); - snprint(wavetable.fname, NameBufSize, "%s", argv[0]); - } else wavetable.fname[0] = '\0'; + snprint(wavetable.path, NameBufSize, "%s", argv[0]); + } else wavetable.path[0] = '\0'; + + prog.n = ProgMax * 16; + prog.p = mallocz(prog.n, 1); + if (loadfile(argv[1], prog.p, prog.n) != 0) { + fprint(2, "loading %s\n", argv[1]); + snprint(prog.path, NameBufSize, "%s", argv[1]); + } else prog.path[0] = '\0'; if (initdraw(nil, nil, "inst") == 0) sysfatal("%r"); - if ((UI.mctl = initmouse(nil, nil)) == nil) sysfatal("%r"); - if ((UI.kctl = initkeyboard(nil)) == nil) sysfatal("%r"); - proccreate(threadsynth, &synth, 64 * 1024); + if ((ui.mctl = initmouse(nil, screen)) == nil) sysfatal("%r"); + if ((ui.kctl = initkeyboard(nil)) == nil) sysfatal("%r"); + synthc = chancreate(sizeof(Rune), 8); + proccreate(threadsynth, nil, 64 * 1024); + proccreate(threadkbd, nil, 64 * 1024); + + palette[0] = allocimage(display, ZR, XRGB32, 1, 0x7f0000ff); + palette[1] = allocimage(display, ZR, XRGB32, 1, 0xffff00ff); + palette[2] = allocimage(display, ZR, XRGB32, 1, 0xff0000ff); - palette[0] = allocimage(display, Rect(0,0,1,1), XRGB32, 1, 0x7f3f00ff); - palette[1] = allocimage(display, Rect(0,0,1,1), XRGB32, 1, 0xffff00ff); - wt.palette = palette; - wt.aux = &wavetable; - wt.draw = wtdraw; - wt.activate = wtactivate; + wxf_wt = (Widget) { + ZR, palette, &wavetable, wxfiledraw, wxfileactivate, + }; + wxf_prog = (Widget) { + ZR, palette, &prog, wxfiledraw, wxfileactivate, + }; - Widget *wp[] = {&wt, nil}; - UI.wp = wp; + Widget *wp[] = {&wxf_wt, &wxf_prog, nil}; + ui.wp = wp; - rcalc(&UI); + rcalc(&ui); draw(screen, screen->r, display->black, nil, ZP); - wt.draw(&wt); + drawwidgets(&ui); flushimage(display, 1); Alt alts[] = { - {UI.mctl->resizec, UI.rv, CHANRCV}, - {UI.mctl->c, &(UI.mv), CHANRCV}, - {UI.kctl->c, &(UI.kv), CHANRCV}, + {ui.mctl->resizec, ui.rv, CHANRCV}, + {ui.mctl->c, &(ui.mv), CHANRCV}, + {ui.kctl->c, &(ui.kv), CHANRCV}, {nil, nil, CHANEND}, }; @@ -111,12 +156,12 @@ threadmain(int argc, char **argv) case 0: /* resize */ if(getwindow(display, Refnone) < 0) sysfatal("resize failed: %r"); draw(screen, screen->r, display->black, nil, ZP); - rcalc(&UI); - wt.draw(&wt); + rcalc(&ui); + drawwidgets(&ui); flushimage(display, 1); break; case 1: /* mouse */ - handlemouse(&UI); + handlemouse(&ui); break; case 2: /* keyboard */ break; @@ -126,28 +171,52 @@ threadmain(int argc, char **argv) void usage(void) { - fprint(2, "usage: %s\n", argv0); + fprint(2, "usage: %s wavetable prog\n", argv0); threadexitsall("usage"); } void -threadsynth(void *arg) +threadsynth(void *) { - struct synth *synth; - int audio; + Channel *c = synthc; + Inter inter; + inter.progstart = prog.p; + int i, audio, note = 0, newnote, freq = 0, flag = 0; + double t = 0, f; + Rune r; s16int *buf; - synth = (struct synth *) arg; audio = open("/dev/audio", OWRITE); if (audio < 0) { fprint(2, "threadsynth: %r\n"); return; } - buf = mallocz(OutBufSize, 1); - while (write(audio, buf, OutBufSize) > 0) { - int i; + buf = mallocz(OutBufSize * sizeof(s16int), 1); + if (buf == nil) { + sysfatal("threadsynth failed to allocate buffer: %r"); + } + fprint(2, "threadsynth online\n"); + + while (write(audio, buf, OutBufSize * sizeof(s16int)) > 0) { + interstep(&inter, flag); + if (nbrecv(c, &r) != 0) { + if (r == L'\0') { + flag = 0; + } else { + newnote = rune2note(r); + if ((newnote >=0) && + ((newnote != note) || (flag == 0))) { + interreset(&inter); + flag |= FHold; + note = newnote; + t = 0; + freq = note2freq(note); + } + } + } for (i = 0; i < OutBufSize - 1; i += 2) { - // TODO: insert synth code here - buf[i] = 0; + f = wtosc((s16int *)wavetable.p, inter.mod[1], t) * inter.mod[0]; + t += freq / 44100.0; + buf[i] = f / (double)0x7fff; buf[i+1] = buf[i]; } } @@ -155,76 +224,133 @@ threadsynth(void *arg) } void -handlemouse(UI *UI) +threadkbd(void *) +{ + int kbd; + Rune r; + long n; + char kbuf[KbdBufSize]; + Rune rbuf[KbdBufSize]; + kbd = open("/dev/kbd", OREAD); + if (kbd < 0) { + fprint(2, "threadkbd: %r\n"); + return; + } + memset(kbuf, 0, KbdBufSize); + + fprint(2, "threadkbd online\n"); + + while ((read(kbd, kbuf, KbdBufSize)) > 0) { + n = runesnprint(rbuf, KbdBufSize, "%s", kbuf + 1); + switch (kbuf[0]) { + case 'K': + case 'k': + if (n == 0) r = L'\0'; + else r = rbuf[n - 1]; + nbsend(synthc, &r); + break; + case 'c': + /* hacks! */ + nbsend(ui.kctl->c, &rbuf[0]); + break; + } + memset(kbuf, 0, KbdBufSize); + } + close(kbd); +} + +void +handlemouse(UI *ui) { enum { MFree, }; static state = MFree; switch (state) { case MFree: - UI->hot = findhot(UI); - if ((UI->mv.buttons != 0) && (UI->hot != nil)) { - UI->hot->activate(UI, UI->hot); - UI->hot->draw(UI->hot); + ui->hot = findhot(ui); + if ((ui->mv.buttons == 1) && (ui->hot != nil)) { + ui->hot->activate(ui, ui->hot); + drawwidgets(ui); flushimage(display, 1); } - if (UI->mv.buttons == 4) threadexitsall(nil); + if (ui->mv.buttons == 4) threadexitsall(nil); break; } } -void -wtdraw(void *arg) -{ - Widget *w = (Widget *) arg; - struct wavetable *wt = (struct wavetable *) w->aux; - char buf[256]; - snprint(buf, NameBufSize, "wt: %s", wt->fname); - draw(screen, w->r, w->palette[0], nil, ZP); - string(screen, w->r.min, w->palette[1], ZP, font, buf); -} - -void -wtactivate(UI *UI, void *arg) -{ - Widget *w = (Widget *) arg; - struct wavetable *wt = (struct wavetable *) w->aux; - if (enter("wt:", wt->fname, NameBufSize, UI->mctl, UI->kctl, nil) != 0) { - loadwavetable(wt->p, wt->fname); - } -} - int -loadwavetable(void *p, char *f) +loadfile(char *path, void *p, long size) { int fd; long n; - fd = open(f, OREAD); + fd = open(path, OREAD); if (fd < 0) { fprint(2, "%r\n"); return 0; }; - n = read(fd, p, sizeof(s16int) * WFSize * WTSize); + n = read(fd, p, size); close(fd); return n > 0; } +int +loadwavetable(void *p, char *f) +{ + return loadfile(f, p, sizeof(s16int) * WFSize * WTSize); +} + void -rcalc(UI *UI) +rcalc(UI *ui) { Point min = screen->r.min; Point max = screen->r.max; + Rectangle r = Rpt(min, Pt(max.x, min.y + font->height)); - UI->wp[0]->r = r; + ui->wp[0]->r = r; + Rectangle r2 = Rpt(Pt(r.min.x, r.max.y), Pt(max.x, r.max.y + font->height)); + ui->wp[1]->r = r2; + } Widget * -findhot(UI *UI) +findhot(UI *ui) { - Widget **w = UI->wp; - Point xy = UI->mv.xy; + Widget **w = ui->wp; + Point xy = ui->mv.xy; for (; *w != nil; w++) { if (ptinrect(xy, (*w)->r) != 0) return *w; } return nil; } + +void +drawwidgets(UI *ui) +{ + Widget **wp = ui->wp; + while (*wp != nil) { + (*wp)->draw(ui, *wp); + wp++; + }; +} + + +void +wxfiledraw(UI *, void *arg) +{ + Widget *w = (Widget *) arg; + FBuf *fb = w->aux; + static char buf[NameBufSize]; + snprint(buf, NameBufSize, "path: %s", fb->path); + draw(screen, w->r, w->palette[0], nil, ZP); + string(screen, w->r.min, w->palette[1], ZP, font, buf); +} + +void +wxfileactivate(UI *ui, void *arg) +{ + Widget *w = (Widget *) arg; + FBuf *fb = w->aux; + if (enter("path:", fb->path, NameBufSize, ui->mctl, ui->kctl, nil) != 0) { + loadfile(fb->path, fb->p, fb->n); + } +} diff --git a/src/wave/mkfile b/src/wave/mkfile @@ -2,7 +2,7 @@ TARG=draw loop fade piano sampler harm smoke inst BIN=/$objtype/bin -HFILES=util.h wavetable.h -OFILES=wavetable.$O +HFILES=util.h wavetable.h prog.h +OFILES=wavetable.$O prog.$O util.$O </sys/src/cmd/mkmany diff --git a/src/wave/piano.c b/src/wave/piano.c @@ -6,50 +6,14 @@ #include <u.h> #include <libc.h> +#include "util.h" + #define BSIZE 4096 Rune *keys = L"zsxdcvgbhnjmq2w3er5t6y7ui9o0p["; Rune *notes = L"CCDDEFFGGAAB"; Rune *semi = L"-#-#--#-#-#-"; -/* - * Frequencies for equal-tempered scale - * from https://pages.mtu.edu/~suits/notefreqs.html - */ - -double freq[] = { - 261.63, // C-4 - 277.18, - 293.66, - 311.13, - 329.63, - 349.23, - 369.99, - 392.00, - 415.30, - 440.00, // A-4 - 466.16, - 493.88, - 523.25, // C-5 - 554.37, - 587.33, - 622.25, - 659.25, - 698.46, - 739.99, - 783.99, - 830.61, - 880.00, // A-5 - 932.33, - 987.77, - 1046.50, - 1108.73, - 1174.66, - 1244.51, - 1318.51, - 1396.91 // F-6 -}; - void usage(void) { diff --git a/src/wave/prog.c b/src/wave/prog.c @@ -0,0 +1,98 @@ +#include <u.h> +#include <libc.h> + +#include "prog.h" + + +void +interreset(Inter *inter) +{ + inter->PC = 0; + inter->state = InLoad; + memset(inter->cmd, 0, 16); + memset(inter->mod, 0, sizeof(s16int) * 4); +} + +void +interstep(Inter *inter, int flags) +{ + switch (inter->state) { + case InHalt: + return; + case InRepeat: + interexec(inter, flags); + break; + case InLoad: + memcpy(inter->cmd, inter->progstart + (inter->PC * 16), 16); + inter->state = InExec; + case InExec: + interexec(inter, flags); + if (inter->state == InExec) inter->state = InLoad; + inter->PC++; + break; + } + if (inter->PC >= ProgMax) { + inter->PC = 0; + inter->state = InHalt; + }; +} + +void +interexec(Inter *inter, int flag) +{ + int i; + for (i = 0; i < 16; i += 4) { + Instruction *I = (Instruction *)(inter->cmd + i); + if (I->m == ModCtl) { + switch (I->op) { + case CtlHalt: + inter->state = InHalt; + break; + case CtlRepeat: + if (I->arg > 0) { + inter->state = InRepeat; + I->arg--; + } else inter->state = InLoad; + break; + case CtlHoldJmp: + if (flag & FHold) inter->PC = I->arg - 1; + break; + case CtlJmp: + inter->PC = I->arg - 1; + break; + default: /* not recognized */ + break; + } + } else interinstr(inter, I); + } +} + +void +interinstr(Inter *inter, Instruction *I) +{ + int m; + switch (I->m) { + default: /* invalid mod index */ + case 0: /* noop */ + return; + case 1: + case 2: + case 3: + case 4: + m = I->m - 1; + break; + } + switch (I->op) { + case ModOpAbs: + inter->mod[m] = I->arg; + break; + case ModOpInc: + inter->mod[m] += I->arg; + break; + case ModOpDec: + inter->mod[m] -= I->arg; + break; + default: /* not recognized */ + return; + } +} diff --git a/src/wave/prog.h b/src/wave/prog.h @@ -0,0 +1,44 @@ +/* mircotracker for synth programming */ + +enum { + InHalt, + InRepeat, + InLoad, + InExec, + + FHold = 1, + + ProgMax = 128, + + ModCtl = 0xff, + CtlRepeat = 0x00, + CtlHoldJmp = 0x01, + CtlJmp = 0x02, + CtlHalt = 0xff, + + ModOpAbs = 0x00, + ModOpInc = 0x01, + ModOpDec = 0xff, +}; + +typedef struct Inter Inter; +typedef struct Instruction Instruction; + +struct Inter { + char *progstart; + int PC; + int state; + char cmd[16]; + s16int mod[4]; +}; + +struct Instruction { + uchar m; + uchar op; + s16int arg; +}; + +void interreset(Inter *); +void interstep(Inter *, int); +void interexec(Inter *, int); +void interinstr(Inter *, Instruction *); diff --git a/src/wave/smoke.c b/src/wave/smoke.c @@ -2,22 +2,33 @@ #include <u.h> #include <libc.h> #include "wavetable.h" +#include "prog.h" + +s16int prog[128 * 8] = { + 0x0001, 0x00ff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xff01, 0x0001, 0x00ff, 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, + + 0x0101, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0101, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xff01, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xff01, 0x0002, 0x01ff, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, + + 0xff01, 0x000f, 0x00ff, 0x000f, 0x0000, 0x0000, 0x0000, 0x0000, + + 0x0001, 0x0000, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +}; void main(void) { - int i, n, t; - double x; - Env e; - e.n = 3; - e.val[0] = 15; - e.val[1] = 5; - e.val[2] = 0; - e.len[0] = 10; - e.len[1] = 10; - for (i = 0; i < 25; i++) { - x = envget(&e, &n, &t, 0); - print("[%d %d] %f\n", n, t, x); - t++; - } + int i, j; + Inter I = {(char *)prog}; + interreset(&I); + i = 0; + while (I.state != InHalt) { + interstep(&I, i < 16); + print("<< [%04d] ", i++); + for (j = 0; j < 4; j++) print("%04d ", I.mod[j]); + print(">>\n"); + }; } diff --git a/src/wave/util.h b/src/wave/util.h @@ -1,8 +1,9 @@ -#define MaxLength (44100 * 10) -/* - * This is 10 seconds in standard PCM samplerate - * TODO: MaxLength should be equal to max size of 9p message - */ +enum { + /* + * This is 10 seconds in standard PCM samplerate, deprecated value + */ + MaxLength = (44100 * 10) +}; typedef struct Frame Frame; typedef struct Aud Aud; @@ -21,3 +22,6 @@ struct Aud{ u32int p[MaxLength]; long n; }; + +int rune2note(Rune); +int note2freq(int n); diff --git a/src/wave/wavetable.c b/src/wave/wavetable.c @@ -3,68 +3,33 @@ #include "wavetable.h" double -envget(Env *env, int *n, int *t, int hold) -{ - double d, v1, v2; - - if ((env->flags & EFHold) && hold && (*n >= env->hold[1])) { - *n = env->hold[0]; - if (env->hold[0] == env->hold[1]) *t = 0; - } - if ((env->flags & EFLoop) && (*n >= env->loop[1])) { - *n = env->loop[0]; - if (env->loop[0] == env->loop[1]) *t = 0; - } - - for (; (*t > env->len[*n]) && (*n < env->n); *t -= env->len[*n]) { - (*n)++; - if ((env->flags & EFHold) && hold && (*n >= env->hold[1])) { - *n = env->hold[0]; - } - if ((env->flags & EFLoop) && (*n >= env->loop[1])) { - *n = env->loop[0]; - } - } - - if (*n >= env->n) { - *n = env->n - 1; - *t = 0; - } - if (*t == 0) return env->val[*n]; - - d = (env->len[*n] == 0) ? 0 : (double)*t / (double)env->len[*n]; - v1 = env->val[*n]; - v2 = env->val[*n + 1]; - if ((env->flags & EFHold) && hold && (*n == env->hold[1])) - v2 = env->val[env->hold[0]]; - if ((env->flags & EFLoop) && (*n == env->loop[1])) - v2 = env->val[env->loop[0]]; - return v1 * (1 - d) + v2 * d; -} - -double wfosc(s16int *wf, double T) { double t = fmod(T, 1) * WFSize; int n = floor(t); - double v1 = wf[n]; - double v2 = (n == WFSize - 1) ? wf[0] : wf[n + 1]; - double d = fmod(t, 1); - return (v1 * (1.0 - d) + v2 * d) / (double)(0x7fff); + return wf[n]; + //double v1 = wf[n]; + //double v2 = (n == WFSize - 1) ? wf[0] : wf[n + 1]; + //double d = fmod(t, 1); + //return v1 * (1.0 - d) + v2 * d; } double -wtosc(s16int *wt, double C, double T) +wtosc(s16int *wt, int C, double T) { int n; double d, v1, v2; - n = floor(C); - d = fmod(C, 1); + if (C < 0) C = 0; + if (C > 0xffff) C = 0xffff; + + n = C >> 8; + d = fmod(C & 0xff, WTSize) / (double)WTSize; + if (n >= WTSize) { n = WTSize - 1; d = 1; - } + v2 = wfosc(wt + (n * WFSize), T); + } else v2 = wfosc(wt + ((n + 1) * WFSize), T); v1 = wfosc(wt + (n * WFSize), T); - v2 = wfosc(wt + ((n + 1) * WFSize), T); return v1 * (1 - d) + v2 * d; } diff --git a/src/wave/wavetable.h b/src/wave/wavetable.h @@ -1,36 +1,7 @@ enum { - EFHold = 1, - EFLoop = 2, WFSize = 2048, WTSize = 256, - EnvSize = 64, -}; - -typedef struct Env Env; -typedef struct Inst Inst; -typedef struct Voice Voice; - -struct Env { - int n; - int flags; - int hold[2]; - int loop[2]; - int val[EnvSize]; - int len[EnvSize]; -}; - -double envget(Env *, int *, int *, int); - -struct Inst { - s16int *wt; - Env wtenv, ampenv; // ??? -}; - -struct Voice { - Inst *inst; - double freq; - double wtet, ampet; // ??? }; double wfosc(s16int *, double); -double wtosc(s16int *, double, double); +double wtosc(s16int *, int, double);