commit 8310755c2cddb3be7ab0ef747d573e795632bc0c
parent e81b652a91646b3f46ad03664106a80295571b12
Author: rpa <rpa@laika>
Date: Sat, 4 Feb 2023 19:48:41 +0000
src/wave: track, a simple tracker
Diffstat:
3 files changed, 314 insertions(+), 5 deletions(-)
diff --git a/src/wave/mkfile b/src/wave/mkfile
@@ -1,6 +1,6 @@
</$objtype/mkfile
-TARG=draw loop fade piano sampler harm smoke inst wfedit
+TARG=draw loop fade piano sampler harm smoke inst wfedit track
BIN=/$objtype/bin
HFILES=util.h wavetable.h prog.h
OFILES=wavetable.$O prog.$O util.$O
diff --git a/src/wave/track.c b/src/wave/track.c
@@ -0,0 +1,307 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <thread.h>
+
+#include "wavetable.h"
+#include "util.h"
+
+enum {
+ Octave = 12,
+ MaxPattern = 128,
+ PatternLength = 64,
+ SeqLength = 256,
+ ChanMax = 8,
+ BufSize = 256,
+ OutSize = 1024,
+ WTBufSize = sizeof(s16int) * WTSize * WFSize,
+
+ NoteNop = 0x00,
+ NoteOff = 0x81,
+ NoteErr = 0xff,
+};
+
+typedef struct Pattern Pattern;
+typedef struct Step Step;
+typedef struct Synthctl Synthctl;
+
+struct Step {
+ uchar note;
+ uchar volume;
+ s16int cmd[3];
+};
+
+struct Pattern {
+ int length;
+ Step *chan[ChanMax];
+};
+
+struct Synthctl {
+ int play;
+ int pattern;
+ int PC;
+ Synth synth;
+ Phasor phasor;
+};
+
+Step pattern[MaxPattern][PatternLength];
+
+char *notestr[Octave] = {
+ "C-", "C#", "D-", "D#", "E-",
+ "F-", "F#", "G-", "G#", "A-",
+ "A#", "B-",
+};
+
+int n;
+char cbuf[BufSize], *args[64];
+s16int *wt;
+Synthctl sctl;
+
+void usage(void);
+void trackedit(void);
+void loadwt(char *);
+void patternedit(char *);
+void printpattern(Step *);
+char * note2str(int);
+void patternset(Step *, int, char **);
+int str2note(char *);
+void threadsynth(void *);
+
+void
+threadmain(int argc, char **argv)
+{
+ ARGBEGIN{
+ default:
+ usage();
+ } ARGEND;
+ wt = sbrk(WTBufSize);
+ memset(wt, 0, WTBufSize);
+ sctl.synth.table[0] = wt;
+ sctl.synth.mod.wtsel = 0;
+ proccreate(threadsynth, &sctl, 64 * 1024);
+ trackedit();
+ threadexitsall(nil);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [file]\n", argv0);
+ threadexitsall("usage");
+}
+
+void
+trackedit(void)
+{
+ char *ps = "% ";
+ memset(cbuf, 0, BufSize);
+ print("%s", ps);
+ while (read(0, cbuf, BufSize) > 0) {
+ n = tokenize(cbuf, args, 64);
+ if (n == 0) continue;
+ switch (args[0][0]) {
+ case 'p':
+ if (n != 2) goto err;
+ patternedit(args[1]);
+ break;
+ case 't':
+ if (n != 2) goto err;
+ else loadwt(args[1]);
+ break;
+ case 'h':
+ print("(h)elp, (q)uit, wave(t)able, (p)attern\n");
+ break;
+ case 'q':
+ return;
+ default:
+ err:
+ print("?\n");
+ }
+ print("%s", ps);
+ memset(cbuf, 0, BufSize);
+ }
+}
+
+void
+loadwt(char *path)
+{
+ int fd;
+ memset(wt, 0, WTBufSize);
+ fd = open(path, OREAD);
+ if (fd < 0) {
+ fprint(2, "%r\n");
+ return;
+ }
+ read(fd, wt, WTBufSize);
+ close(fd);
+ print("ok\n");
+}
+
+void
+patternedit(char *arg)
+{
+ long l;
+ Step *ptrn;
+ char *p;
+ char *ps = "p% ";
+ l = strtol(arg, &p, 16);
+ if (p == arg) {
+ print("?\n");
+ return;
+ }
+ if ((l < 0) || (l >= MaxPattern)) {
+ print("?\n");
+ return;
+ }
+ ptrn = pattern[l];
+ memset(cbuf, 0, BufSize);
+ print("%s", ps);
+ while (read(0, cbuf, BufSize) > 0) {
+ n = tokenize(cbuf, args, 64);
+ if (n == 0) continue;
+ switch(args[0][0]) {
+ case 'p':
+ printpattern(ptrn);
+ break;
+ case 'h':
+ print("(p)rint, (h)elp, (q)uit\n");
+ break;
+ case 'q':
+ return;
+ case 'P':
+ sctl.play = 1;
+ break;
+ case 'S':
+ sctl.play = 0;
+ sctl.PC = 0;
+ break;
+ default:
+ if (n == 1) goto err;
+ patternset(ptrn, n, args);
+ break;
+ err:
+ print("?\n");
+ }
+ print("%s", ps);
+ memset(cbuf, 0, BufSize);
+ }
+}
+
+void
+printpattern(Step *ptrn)
+{
+ int i;
+ for (i = 0; i < PatternLength; i++) {
+ print("%02uhhx: %s %02uhhX - %04uhX %04uhX %04uhX\n",
+ i + 1, note2str(ptrn[i].note), ptrn[i].volume,
+ ptrn[i].cmd[0], ptrn[i].cmd[1], ptrn[i].cmd[2]);
+ }
+}
+
+char *
+note2str(int note)
+{
+ static char buf[4];
+ switch (note){
+ case NoteNop: return "...";
+ case NoteOff: return "OFF";
+ case NoteErr: return "ERR";
+ default:
+ snprint(buf, 4, "%s%d\0", notestr[note%Octave], note/Octave);
+ }
+ return buf;
+}
+
+void
+patternset(Step *ptrn, int n, char **args)
+{
+ long l;
+ int i, note;
+ char *p;
+ if (n > 2) {
+ print("?\n");
+ return;
+ }
+ l = strtol(args[0], &p, 16);
+ if (p == args[0]) {
+ print("?\n");
+ return;
+ }
+ if ((l < 1) || (l >= PatternLength)) {
+ print("?\n");
+ return;
+ }
+ i = l;
+ note = str2note(args[1]);
+ if (note == NoteErr) {
+ print("?\n");
+ return;
+ }
+ ptrn[i-1].note = note;
+}
+
+int
+str2note(char *str)
+{
+ int i, note;
+ if (strncmp(str, "OFF", 3) == 0) return NoteOff;
+ if (strncmp(str, "off", 3) == 0) return NoteOff;
+ if (strncmp(str, "---", 3) == 0) return NoteNop;
+ str[0] = toupper(str[0]);
+ for (i = 0; i < Octave; i++) {
+ if (strncmp(str, notestr[i], 2) == 0) {
+ if (isdigit(str[2])) {
+ note = i + 12 * (str[2] - '0');
+ print("note %d, freq %d\n", note, note2freq(note));
+ return note;
+ }
+ else return NoteErr;
+ }
+ }
+ return NoteErr;
+}
+
+void
+threadsynth(void *v)
+{
+ u32int *buf;
+ Synthctl *sctl = v;
+ int i, k;
+ int fd = open("/dev/audio", OWRITE);
+ if (fd < 0) {
+ fprint(2, "%r\n");
+ return;
+ }
+ buf = malloc(sizeof(u32int) * OutSize);
+
+ for(i = 0, k = 44100; ; i++, k++) {
+ if (i == OutSize) {
+ i = 0;
+ if (write(fd, buf, OutSize * sizeof(u32int)) < 0) {
+ close(fd);
+ return;
+ }
+ }
+
+ if (sctl->play != 0) {
+ if (k >= 44100 / (12)) {
+ Step s = pattern[sctl->pattern][sctl->PC];
+ switch(s.note) {
+ case NoteOff:
+ sctl->synth.mod.freq = 0;
+ sctl->synth.mod.vol = 0;
+ case NoteNop:
+ case NoteErr:
+ break;
+ default:
+ sctl->synth.mod.freq = note2freq(s.note);
+ sctl->synth.mod.vol = 0x7f;
+ }
+ k = 0;
+ sctl->PC++;
+ if (sctl->PC > PatternLength) sctl->PC = 0;
+ }
+ buf[i] = wtstep(&sctl->synth, &sctl->phasor);
+ } else buf[i] = 0;
+ }
+}
diff --git a/src/wave/util.c b/src/wave/util.c
@@ -17,6 +17,9 @@ rune2note(Rune r)
int note2freq(int n)
{
+ // TODO: convert to proper caclucations
+ // instead of table lookup and messy pows
+
/*
* Frequencies for equal-tempered scale
* from https://pages.mtu.edu/~suits/notefreqs.html
@@ -55,9 +58,8 @@ int note2freq(int n)
1396.91 // F-6
};
- int s = sizeof(freq) / sizeof(double);
- // int fo = 1 + (n / s);
- int fn = n % s;
- double f = freq[fn];// / 16 * fo;
+ int fo = 1 + (n / 12);
+ int fn = n % 12;
+ double f = freq[fn] / pow(2, 4) * pow(2, fo);
return f;
}