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 }