commit e06ef51dbdc7c641031859d911127f963175f810
parent 33a4d7ac6a987fb463649f51cdeb144f7d8cea5d
Author: rpa <rpa@laika>
Date: Sun, 15 Jan 2023 00:18:22 +0000
wave: wfedit display samples and some simple commands
Diffstat:
3 files changed, 334 insertions(+), 66 deletions(-)
diff --git a/src/wave/wavetable.c b/src/wave/wavetable.c
@@ -2,34 +2,43 @@
#include <libc.h>
#include "wavetable.h"
-double
-wfosc(s16int *wf, double T)
+int
+pstep(Phasor *p, int freq, int phase, int linear)
{
- double t = fmod(T, 1) * WFSize;
- int n = floor(t);
- 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;
+ int t = (p->t + phase) * WFSize / 44100;
+ p->t += freq;
+ if (linear == 0) return t % WFSize;
+ else return t;
}
-double
-wtosc(s16int *wt, int C, double T)
+u32int
+wtfunc(s16int *wtp, int phase, int mod, int vol, int pan)
{
- int n;
- double d, v1, v2;
- if (C < 0) C = 0;
- if (C > 0xffff) C = 0xffff;
-
- n = C >> 8;
- d = fmod(C & 0xff, WTSize) / (double)WTSize;
+ int m1, m2, x, y, d, v, mono;
+ union {
+ u32int i;
+ s16int s[2];
+ } out;
+ d = mod % WTSize;
+ m1 = phase + (mod / WTSize) * WFSize;
+ m2 = m1 + WFSize;
+ x = wtp[m1 % (WTSize * WFSize)];
+ y = wtp[m2 % (WTSize * WFSize)];
+ v = (x * (WTSize - d) + y * d) / WTSize;
+ mono = v * vol / 0x7fff;
+ out.s[0] = mono;
+ out.s[1] = mono;
+ if (pan > 0) out.s[0] = mono * (0x7fff - pan) / 0x7fff;
+ else if (pan < 0) out.s[1] = mono * (0x7fff + pan) / 0x7fff;
+ return out.i;
+}
- 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);
- return v1 * (1 - d) + v2 * d;
+u32int
+wtstep(Synth *s, Phasor *p)
+{
+ s16int *wtp;
+ int phase;
+ wtp = s->table[s->mod.wtsel % 64];
+ phase = pstep(p, s->mod.freq, s->mod.phase, s->flag);
+ return wtfunc(wtp, phase, s->mod.mod, s->mod.vol, s->mod.pan);
}
diff --git a/src/wave/wavetable.h b/src/wave/wavetable.h
@@ -28,6 +28,6 @@ struct Synth {
int flag;
};
-u32int wtfunc(s16int *wtp, int phase, int mod, int vol, int pan);
int pstep(Phasor *, int, int, int);
+u32int wtfunc(s16int *wtp, int phase, int mod, int vol, int pan);
u32int wtstep(Synth *, Phasor *);
diff --git a/src/wave/wfedit.c b/src/wave/wfedit.c
@@ -4,6 +4,7 @@
#include <u.h>
#include <libc.h>
+#include <bio.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
@@ -13,33 +14,71 @@
#include "wavetable.h"
enum {
- BufMax = 8,
+ BufMax = 6,
BufSize = sizeof(s16int) * WFSize,
- CmdBufSize = 256,
+ MsgSize = 256,
};
-typedef struct CmdCtl CmdCtl;
-typedef struct Command Command;
+typedef struct Cmdctl Cmdctl;
+typedef struct Uictl Uictl;
-struct CmdCtl {
+typedef int (*cmdfunc)(Cmdctl *, int, char **);
+
+struct Cmdctl {
Channel *c;
+ Channel *updatec;
+};
+
+struct Uictl {
+ Mousectl *mctl;
+ Rectangle grid[BufMax];
+ Image *winbg, *wfbg, *hi, *lo;
+ Channel *updatec;
+};
+
+#define CMD(name) int name(Cmdctl *cmd, int argc, char **argv)
+CMD(cmdexit);
+CMD(cmdecho);
+CMD(cmdload);
+CMD(cmdsave);
+CMD(cmdnoise);
+CMD(cmdcos);
+CMD(cmdmove);
+CMD(cmdampmod);
+CMD(cmdphasemod);
+
+struct Dict{
+ const char *name;
+ cmdfunc cmd;
};
-struct Command {
- int in;
- int out;
- void (*op)(void *);
- char **args;
+struct Dict dict[] = {
+ {"q", cmdexit},
+ {"exit", cmdexit},
+ {"quit", cmdexit},
+ {"bye", cmdexit},
+ {"echo", cmdecho},
+ {"load", cmdload},
+ {"save", cmdsave},
+ {"noise", cmdnoise},
+ {"cos", cmdcos},
+ {nil, nil},
};
+
void usage(void);
void threadcmd(void *);
-int getinput(char *);
-int getoutput(char *);
-void* getop(char *);
-void bufcpy(int, int);
-
s16int *buf[BufMax];
+cmdfunc getcmd(char *);
+
+Uictl *initui(void);
+void threadui(void *);
+void resize(Uictl *);
+void drawwaveform(Image *, Rectangle, s16int *, Image *, Image *, Image *);
+void drawgrid(Uictl *ui);
+
+Cmdctl cmdctl;
+Uictl *uictl;
void
threadmain(int argc, char **argv)
@@ -50,7 +89,27 @@ threadmain(int argc, char **argv)
default:
usage();
} ARGEND;
+ if (argc > 1) usage();
+
+ uictl = initui();
+
+ cmdctl.c = chancreate(MsgSize, 0);
+ cmdctl.updatec = uictl->updatec;
+ proccreate(threadcmd, &cmdctl, 64 * 1024);
+ if (argc == 1) {
+ cmdload(&cmdctl, 1, argv);
+ }
+
+ Biobuf *bfd = Bfdopen(0, OREAD);
+ char *s;
+ char cmdbuf[MsgSize];
+ while((s = Brdstr(bfd, '\n', 1)) != nil) {
+ memset(cmdbuf, 0, MsgSize);
+ strncpy(cmdbuf, s, MsgSize);
+ send(cmdctl.c, cmdbuf);
+ free(s);
+ }
}
void
@@ -64,47 +123,247 @@ void
threadcmd(void *v)
{
int n;
- Command cmd;
- char cmdbuf[CmdBufSize], *args[64];
- CmdCtl *rctl = v;
- while (recv(v,cmdbuf) > 0) {
+ char cmdbuf[MsgSize], *args[64];
+ Cmdctl *rctl = v;
+ while (recv(rctl->c, cmdbuf) > 0) {
n = tokenize(cmdbuf, args, 64);
- cmd = (Command) {
- getinput(args[0]),
- getoutput(args[n-1]),
- getop(args[1]),
- args + 2,
- };
- args[n - 1] = nil;
- if ((cmd.in >= 0) && (cmd.out >= 0) && (cmd.op != nil)) {
- bufcpy(cmd.in, 0);
- cmd.op(args);
- bufcpy(0, cmd.out);
- } else {
- // TODO: output error message here?
- }
+ if (n <= 0) continue;
+ cmdfunc cmd = getcmd(args[0]);
+ if (cmd != nil) {
+ cmd(rctl, n - 1, args + 1);
+ } else fprint(2, "?\n");
}
}
+cmdfunc
+getcmd(char *name)
+{
+ struct Dict *dp;
+ for (dp = dict; dp->name != nil; dp++) {
+ if (strcmp(dp->name, name) == 0) return dp->cmd;
+ }
+ return nil;
+}
+
int
-getinput(char *)
+cmdexit(Cmdctl *, int, char**)
{
+ threadexitsall(nil);
return 0;
}
-int
-getoutput(char *)
+CMD(cmdecho)
{
+ int i;
+ for (i = 0; i < argc; i++) {
+ if (i > 0) print(" ");
+ print("%s", argv[i]);
+ }
+ print("\n");
return 0;
}
-void *
-getop(char *)
+CMD(cmdload)
{
- return nil;
+ int fd;
+ if (argc == 0) {
+ fprint(2, "usage: load file\n");
+ return 1;
+ }
+ fd = open(argv[0], OREAD);
+ if (fd < 0) {
+ fprint(2, "cmdload: %r\n");
+ return 1;
+ }
+ read(fd, buf[0], WFSize * sizeof(s16int));
+ close(fd);
+ if (cmd->updatec != nil) nbsendul(cmd->updatec, 0);
+ return 0;
+}
+
+CMD(cmdsave)
+{
+ int fd;
+ if (argc == 0) {
+ fprint(2, "usage: save file\n");
+ return 1;
+ }
+ fd = create(argv[0], OWRITE, 0666);
+ if (fd < 0) {
+ fprint(2, "cmdsave: %r\n");
+ return 1;
+ }
+ write(fd, buf[0], WFSize * sizeof(s16int));
+ close(fd);
+ if (cmd->updatec != nil) nbsendul(cmd->updatec, 0);
+ return 0;
+}
+
+CMD(cmdnoise)
+{
+ int i;
+ ulong targ = 0;
+ if (argc > 1) {
+ fprint(2, "usage: noise [id]\n");
+ return 1;
+ }
+ if (argc == 1) targ = atoi(argv[0]);
+ if (targ > BufMax) {
+ fprint(2, "invalid id\n");
+ return 1;
+ }
+ for (i = 0; i < WFSize; i++) {
+ buf[targ][i] = truerand();
+ }
+ if (cmd->updatec != nil) nbsendul(cmd->updatec, targ);
+ return 0;
+}
+
+CMD(cmdcos)
+{
+ int i;
+ ulong targ = 0;
+ if (argc > 1) {
+ fprint(2, "usage: noise [id]\n");
+ return 1;
+ }
+ if (argc == 1) targ = atoi(argv[0]);
+ if (targ > BufMax) {
+ fprint(2, "invalid id\n");
+ return 1;
+ }
+ for (i = 0; i < WFSize; i++) {
+ buf[targ][i] = cos((double)i / WFSize * PI * 2) * 0x7fff;
+ }
+ if (cmd->updatec != nil) nbsendul(cmd->updatec, targ);
+ return 0;
+}
+
+CMD(cmdmove)
+{
+ if (cmd->updatec != nil) nbsendul(cmd->updatec, 0);
+ return 0;
+}
+
+CMD(cmdampmod)
+{
+ if (cmd->updatec != nil) nbsendul(cmd->updatec, 0);
+ return 0;
+}
+
+CMD(cmdphasemod)
+{
+ if (cmd->updatec != nil) nbsendul(cmd->updatec, 0);
+ return 0;
+}
+
+
+
+Uictl *
+initui(void)
+{
+ Uictl *ui = mallocz(sizeof(Uictl), 1);
+ newwindow(nil);
+ if (initdraw(nil, nil, "wfedit") < 0) sysfatal("%r");
+ if ((ui->mctl = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r");
+ ui->winbg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1 , DWhite);
+ ui->wfbg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1 , DBlack);
+ ui->hi = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1 , DRed);
+ ui->lo = allocimagemix(display, DRed, DBlack);
+ ui->updatec = chancreate(sizeof(ulong), BufMax);
+ resize(ui);
+ proccreate(threadui, ui, 64 * 1024);
+ return ui;
}
void
-bufcpy(int, int)
+threadui(void *v)
{
+ ulong upd;
+ Uictl *ui = v;
+
+ draw(screen, screen->r, ui->winbg, nil, ZP);
+ nbsendul(ui->updatec, -1);
+
+ Alt alts[] = {
+ {ui->mctl->resizec, nil, CHANRCV},
+ {ui->mctl->c, nil, CHANRCV},
+ {ui->updatec, &upd, CHANRCV},
+ {nil, nil, CHANEND},
+ };
+
+ for (;;) {
+ switch (alt(alts)) {
+ case 0: /* resize */
+ if (getwindow(display, Refnone) < 0) sysfatal("resize: %r");
+ resize(ui);
+ draw(screen, screen->r, ui->winbg, nil, ZP);
+ nbsendul(ui->updatec, -1);
+ break;
+ case 1: /* mouse */
+ break;
+ case 2: /* update */
+ if (upd >= BufMax) drawgrid(ui);
+ else {
+ drawwaveform(screen, ui->grid[upd], buf[upd], ui->wfbg, ui->hi, ui->lo);
+ };
+ flushimage(display, 1);
+ break;
+ }
+ }
+}
+
+void
+resize(Uictl *ui)
+{
+ int i;
+ Point min, max, mid;
+ Rectangle l, r, scr;
+ scr = insetrect(screen->r, 1);
+ min = scr.min;
+ max = scr.max;
+ mid = addpt(min, Pt(Dx(scr)/2, Dy(scr)/(BufMax/2)));
+ l = Rpt(min, mid);
+ r = Rect(mid.x, min.y, max.x, mid.y);
+ for (i = 0; i < BufMax; i+=2) {
+ ui->grid[i] = insetrect(l, 1);
+ ui->grid[i + 1] = insetrect(r, 1);
+ l = rectaddpt(l, Pt(0, Dy(l)));
+ r = rectaddpt(r, Pt(0, Dy(r)));
+ }
+}
+
+void
+drawwaveform(Image *screen, Rectangle r, s16int *wf, Image *bg, Image *hi, Image *lo)
+{
+ Rectangle bar;
+ int i, w, h, mid, s, e, m1, m2;
+ w = Dx(r);
+ h = Dy(r);
+ mid = h/2;
+ draw(screen, r, bg, nil, ZP);
+ for (i = 0; i < w; i++) {
+ s = i * WFSize / w;
+ e = (i == w - 1) ? 0 : (i + 1) * WFSize / w;
+ m1 = wf[s] * mid / 0x7fff;
+ m2 = wf[e] * mid / 0x7fff;
+ if (m1 == m2) m1 += (m1 > 0) ? -1 : +1;
+ bar = Rpt(Pt(i, mid), Pt(i + 1, mid + m1));
+ bar = canonrect(bar);
+ bar = rectaddpt(bar, r.min);
+ draw(screen, bar, lo, nil, ZP);
+ bar = Rpt(Pt(i, mid + m2), Pt(i + 1, mid + m1));
+ bar = canonrect(bar);
+ bar = rectaddpt(bar, r.min);
+ draw(screen, bar, hi, nil, ZP);
+ }
+}
+
+void
+drawgrid(Uictl *ui)
+{
+ int i;
+ for (i = 0; i < BufMax; i++) {
+ drawwaveform(screen, ui->grid[i], buf[i], ui->wfbg, ui->hi, ui->lo);
+ }
}