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:
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);