stew

a monorepo of some sort
Log | Files | Refs

commit 8310755c2cddb3be7ab0ef747d573e795632bc0c
parent e81b652a91646b3f46ad03664106a80295571b12
Author: rpa <rpa@laika>
Date:   Sat,  4 Feb 2023 19:48:41 +0000

src/wave: track, a simple tracker

Diffstat:
Msrc/wave/mkfile | 2+-
Asrc/wave/track.c | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/wave/util.c | 10++++++----
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; }