stew

a monorepo of some sort
git clone git://git.nsmpr.xyz/stew.git
Log | Files | Refs

track.c (6436B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <ctype.h>
      4 #include <thread.h>
      5 
      6 #include "wavetable.h"
      7 #include "util.h"
      8 
      9 enum {
     10 	Octave = 12,
     11 	MaxWT = 16,
     12 	MaxPattern = 128,
     13 	PatternLength = 64,
     14 	SeqLength = 256,
     15 	ChanMax = 16,
     16 	BufSize = 256,
     17 	OutSize = 1024,
     18 	WTBufSize = sizeof(s16int) * WTSize * WFSize,
     19 
     20 	NoteNop = 0x00,
     21 	NoteOff = 0x81,
     22 	NoteErr = 0xff,
     23 };
     24 
     25 typedef struct Track Track;
     26 typedef struct Editor Editor;
     27 typedef struct Sequence Sequence;
     28 typedef struct SeqChan SeqChan;
     29 typedef struct Step Step;
     30 typedef struct Synthctl Synthctl;
     31 
     32 struct Step {
     33 	uchar note;
     34 	uchar volume;
     35 	u16int cmd[3];
     36 };
     37 
     38 struct Sequence {
     39 	int cn;
     40 	int n;
     41 	SeqChan *chan;
     42 };
     43 
     44 struct SeqChan {
     45 	// options such as volume, pan
     46 	int *p;
     47 };
     48 
     49 struct Track {
     50 	int bpm, spb, tps;
     51 	s16int *wt[MaxWT];
     52 	Step ptrn[MaxPattern][PatternLength];
     53 	Sequence seq;
     54 };
     55 
     56 struct Editor {
     57 	int pattern;
     58 	int channel;
     59 };
     60 
     61 struct Synthctl {
     62 	int play;
     63 	int PC;
     64 	Synth synth;
     65 	Phasor phasor;
     66 };
     67 
     68 char *notestr[Octave] = {
     69 	"C-", "C#", "D-", "D#", "E-",
     70 	"F-", "F#", "G-", "G#", "A-",
     71 	"A#", "B-",
     72 };
     73 
     74 int n;
     75 char cbuf[BufSize], *args[64];
     76 
     77 Synthctl sctl;
     78 Track track;
     79 Editor editor;
     80 
     81 char * note2str(int);
     82 int str2note(char *);
     83 void repl(void);
     84 void threadsynth(void *);
     85 void usage(void);
     86 
     87 void
     88 threadmain(int argc, char **argv)
     89 {
     90 	track.bpm = 120;
     91 	track.spb = 4;
     92 	track.tps = 3;
     93 	track.wt[0] = sbrk(WTBufSize);
     94 	memset(track.wt[0], 0, WTBufSize);
     95 	sctl.synth.table[0] = track.wt[0];
     96 	sctl.synth.mod.wtsel = 0;
     97 
     98 	ARGBEGIN{
     99 	default:
    100 		usage();
    101 	} ARGEND;
    102 	if (argc > 1) usage();
    103 
    104 	proccreate(threadsynth, &sctl, 64 * 1024);
    105 	repl();
    106 	threadexitsall(nil);
    107 }
    108 
    109 void
    110 usage(void)
    111 {
    112 	fprint(2, "usage: %s [file]\n", argv0);
    113 	threadexitsall("usage");
    114 }
    115 
    116 char *
    117 note2str(int note)
    118 {
    119 	static char buf[4];
    120 	switch (note){
    121 	case NoteNop: return "...";
    122 	case NoteOff: return "OFF";
    123 	case NoteErr: return "ERR";
    124 	default:
    125 		snprint(buf, 4, "%s%d\0", notestr[note%Octave], note/Octave);
    126 	}
    127 	return buf;
    128 }
    129 
    130 int
    131 str2note(char *str)
    132 {
    133 	int i, note;
    134 	if (strncmp(str, "OFF", 4) == 0) return NoteOff;
    135 	if (strncmp(str, "off", 4) == 0) return NoteOff;
    136 	if (strncmp(str, "...", 4) == 0) return NoteNop;
    137 	str[0] = toupper(str[0]);
    138 	for (i = 0; i < Octave; i++) {
    139 		if (strncmp(str, notestr[i], 2) == 0) {
    140 			if (isdigit(str[2])) {
    141 				note = i + 12 * (str[2] - '0');
    142 				return note;
    143 			}
    144 			else return NoteErr;
    145 		}
    146 	}
    147 	return NoteErr;
    148 }
    149 
    150 void
    151 threadsynth(void *v)
    152 {
    153 	u32int *buf;
    154 	Synthctl *sctl = v;
    155 	int i, k;
    156 	int fd = open("/dev/audio", OWRITE);
    157 	if (fd < 0) {
    158 		fprint(2, "%r\n");
    159 		return;
    160 	}
    161 	buf = malloc(sizeof(u32int) * OutSize);
    162 	
    163 	for(i = 0, k = 44100; ; i++, k++) {
    164 		if (i == OutSize) {
    165 			i = 0;
    166 			if (write(fd, buf, OutSize * sizeof(u32int)) < 0) {
    167 				close(fd);
    168 				return;
    169 			}
    170 		}
    171 
    172 		if (sctl->play != 0) {
    173 			if (k >= 44100 / (12)) {
    174 				Step s = track.ptrn[0][sctl->PC];
    175 				switch(s.note) {
    176 					case NoteOff:
    177 						sctl->synth.mod.freq = 0;
    178 						sctl->synth.mod.vol = 0;
    179 					case NoteNop:
    180 					case NoteErr:
    181 						break;
    182 					default:
    183 						sctl->synth.mod.freq = note2freq(s.note);
    184 						sctl->synth.mod.vol = 0x7f00;
    185 				}
    186 				if ((s.volume > 0) && (s.volume <= 0x80)) {
    187 					sctl->synth.mod.vol = (s.volume - 1) * 0x100;
    188 				}
    189 				k = 0;
    190 				sctl->PC++;
    191 				if (sctl->PC > PatternLength) sctl->PC = 0;
    192 			}
    193 			buf[i] = wtstep(&sctl->synth, &sctl->phasor);
    194 		} else buf[i] = 0;
    195 	}
    196 }
    197 
    198 struct Dict {
    199 	char *name;
    200 	char *help;
    201 	void (*func)(int n, char **args);
    202 };
    203 
    204 void wrd_exit(int, char **);
    205 void wrd_help(int, char **);
    206 void wrd_play(int, char **);
    207 void wrd_printpattern(int, char **);
    208 void wrd_stop(int, char **);
    209 void wrd_storestep(int, char **);
    210 void wrd_wtload(int, char **);
    211 
    212 struct Dict dict[] = {
    213 	{"exit", " - leave program", wrd_exit},
    214 	{"help", " - print help", wrd_help},
    215 	{"play", nil, wrd_play},
    216 	{"pp", "   - print pattern", wrd_printpattern},
    217 	{"ss", " step_n note [vol] [cmd1] [cmd2] [cmd3] - store step data", wrd_storestep},
    218 	{"stop", nil, wrd_stop},
    219 	{"wtload", " path - load wavetable", wrd_wtload},
    220 	{nil, nil, nil},
    221 };
    222 
    223 void
    224 repl(void)
    225 {
    226 	int i;
    227 	char *ps = "% ";
    228 
    229 	memset(cbuf, 0, BufSize);
    230 	args[0] = cbuf;
    231 	print("%s", ps);
    232 	while (read(0, cbuf, BufSize) > 0) {
    233 		n = tokenize(cbuf, args, 64);
    234 		for (i = 0; dict[i].name != nil; i++) {
    235 			if (strcmp(dict[i].name, args[0]) == 0) {
    236 				dict[i].func(n, args);
    237 				break;
    238 			}
    239 		}
    240 		if (dict[i].name == nil) print("?\n");
    241 		print("%s", ps);
    242 		memset(cbuf, 0, BufSize);
    243 	}
    244 }
    245 
    246 void
    247 wrd_help(int, char **)
    248 {
    249 	int i;
    250 	for (i = 0; dict[i].name != nil; i++) {
    251 		print("%s", dict[i].name);
    252 		if (dict[i].help != nil) print("%s", dict[i].help);
    253 		print("\n");
    254 	}
    255 }
    256 
    257 void
    258 wrd_exit(int, char **)
    259 {
    260 	threadexitsall(nil);
    261 }
    262 
    263 void
    264 wrd_printpattern(int, char **)
    265 {
    266 	Step *ptrn = track.ptrn[editor.pattern];
    267 
    268 	int i;
    269 	print("Pattern %02uhhx:\n", editor.pattern);
    270 	for (i = 0; i < PatternLength; i++) {
    271 		print("%02uhhx: %s ", i, note2str(ptrn[i].note));
    272 		if (ptrn[i].volume == 0) print(".. - ");
    273 		else print("%02uhhX - ", ptrn[i].volume - 1);
    274 		print("%04uhX %04uhX %04uhX\n",
    275 		  ptrn[i].cmd[0], ptrn[i].cmd[1], ptrn[i].cmd[2]);
    276 	}
    277 }
    278 
    279 void
    280 wrd_wtload(int n, char **args)
    281 {
    282 	if (n != 2) {
    283 		print("?\n");
    284 		return;
    285 	}
    286 	char *path = args[1];
    287 	int fd;
    288 	memset(track.wt[0], 0, WTBufSize);
    289 	fd = open(path, OREAD);
    290 	if (fd < 0) {
    291 		fprint(2, "%r\n");
    292 		print("?\n");
    293 		return;
    294 	}
    295 	read(fd, track.wt[0], WTBufSize);
    296 	close(fd);
    297 	print("ok\n");
    298 }
    299 
    300 void wrd_play(int, char **)
    301 {
    302 	sctl.play = 1;
    303 }
    304 
    305 void wrd_stop(int, char **)
    306 {
    307 	sctl.play = 0;
    308 	sctl.PC = 0;
    309 }
    310 
    311 void
    312 wrd_storestep(int n, char **args)
    313 {
    314 	long l;
    315 	int i, a, note;
    316 	char *p;
    317 	Step *ptrn = track.ptrn[editor.pattern];
    318 
    319 	/* Address */
    320 	args++, n--;
    321 	if (n <= 0) goto err;
    322 	l = strtol(args[0], &p, 16);
    323 	if (p == args[0]) goto err;
    324 	if ((l < 0) || (l >= PatternLength)) goto err;
    325 	a = l;
    326 
    327 	/* Note */
    328 	args++, n--;
    329 	if (n <= 0) goto err;
    330 	if (strcmp(args[0], "-") != 0) {
    331 		note = str2note(args[0]);
    332 		if (note == NoteErr) goto err;
    333 		ptrn[a].note = note;
    334 	}
    335 
    336 	/* Volume */
    337 	args++, n--;
    338 	if (n <= 0) return;
    339 	if (strcmp(args[0], "-") != 0) {
    340 		if (strcmp(args[0], "..") == 0) l = 0;
    341 		else {
    342 			l = strtol(args[0], &p, 16) + 1;
    343 			if (p == args[0]) goto err;
    344 		}
    345 		if ((l < 0) || (l > 0x80)) goto err;
    346 		ptrn[a].volume = l;
    347 	}
    348 	for (i = 0; i < 3; i++) {
    349 		args++, n--;
    350 		if (n <= 0) return;
    351 		if (strcmp(args[0], "-") == 0) continue;
    352 		l = strtol(args[0], &p, 16);
    353 		if (p == args[0]) goto err;
    354 		ptrn[a].cmd[i] = l;
    355 	}
    356 	return;
    357 	err:
    358 		print("?\n");
    359 }