inst.c (9859B)
1 /* 2 visual instrument editor 3 loads/modifies/saves envelopes 4 allows previewing settings by playing notes 5 6 LINE: 7 cmd param loop 8 [b] [2b] X 5 + [b] 9 10 or 11 12 cmd param 13 [2b] [2b] X 4 14 15 PROG: 16 128 lines 17 18 cmd selects modifier and if value is absolute or relative 19 might also use for control commands? 20 modifiers: volume, frequency, wavetable, panoram 21 might also use extra mods for filter? 22 param is s16int 23 loop - negative values are jump 24 25 TOTAL: 26 2048 bytes per instrument's program, nice 27 */ 28 29 #include <u.h> 30 #include <libc.h> 31 #include <ctype.h> 32 #include <thread.h> 33 #include <draw.h> 34 #include <mouse.h> 35 #include <cursor.h> 36 #include <keyboard.h> 37 38 #include "wavetable.h" 39 #include "prog.h" 40 #include "util.h" 41 42 enum { 43 NameBufSize = 256, 44 OutBufSize = 882, 45 KbdBufSize = 64, 46 }; 47 48 typedef struct FBuf FBuf; 49 typedef struct ProgEd ProgEd; 50 typedef struct Widget Widget; 51 typedef struct UI UI; 52 53 struct FBuf { 54 char path[NameBufSize]; 55 long n; 56 char *p; 57 }; 58 59 struct ProgEd { 60 char *p; 61 int curl, curc; 62 }; 63 64 struct UI { 65 Mousectl *mctl; 66 Mouse mv; 67 int rv[2]; 68 Keyboardctl *kctl; 69 Rune kv; 70 int env_n; 71 int env_node; 72 Widget *hot; 73 Widget **wp; 74 }; 75 76 struct Widget { 77 Rectangle r; 78 Image **palette; 79 void *aux; 80 void (*draw)(UI *, void *); 81 void (*activate)(UI *, void *); 82 }; 83 84 void usage(void); 85 void threadsynth(void *); 86 void threadkbd(void *); 87 void handlemouse(UI *); 88 void handlekeyboard(UI *); 89 int loadfile(char *, void *, long); 90 void rcalc(UI *); 91 Widget * findhot(UI *); 92 void drawwidgets(UI *); 93 void drawprogmask(void); 94 void wxfiledraw(UI *, void *); 95 void wxfileactivate(UI *, void *); 96 void wxprogdraw(UI *, void *); 97 98 const Rectangle ZR = {0, 0, 1, 1}; 99 100 FBuf wavetable, prog; 101 UI ui; 102 ProgEd ped; 103 Widget wxf_wt, wxf_prog, wx_prog; 104 Image *progmask, *palette[8]; 105 Channel *synthc; 106 int cw, ch; /* character width and height */ 107 108 void 109 threadmain(int argc, char **argv) 110 { 111 112 ARGBEGIN{ 113 default: 114 usage(); 115 } ARGEND 116 if (argc != 2) usage(); 117 118 wavetable.n = sizeof(s16int) * WFSize * WTSize; 119 wavetable.p = mallocz(wavetable.n, 1); 120 if (loadfile(argv[0], wavetable.p, wavetable.n) != 0) { 121 fprint(2, "loading %s\n", argv[0]); 122 snprint(wavetable.path, NameBufSize, "%s", argv[0]); 123 } else wavetable.path[0] = '\0'; 124 125 prog.n = ProgMax * 16; 126 prog.p = mallocz(prog.n, 1); 127 if (loadfile(argv[1], prog.p, prog.n) != 0) { 128 fprint(2, "loading %s\n", argv[1]); 129 snprint(prog.path, NameBufSize, "%s", argv[1]); 130 } else prog.path[0] = '\0'; 131 ped.p = prog.p; 132 133 if (initdraw(nil, nil, "inst") == 0) sysfatal("%r"); 134 if ((ui.mctl = initmouse(nil, screen)) == nil) sysfatal("%r"); 135 if ((ui.kctl = initkeyboard(nil)) == nil) sysfatal("%r"); 136 synthc = chancreate(sizeof(Rune), 8); 137 proccreate(threadsynth, nil, 64 * 1024); 138 // proccreate(threadkbd, nil, 64 * 1024); 139 140 palette[0] = allocimage(display, ZR, XRGB32, 1, 0x7f0000ff); 141 palette[1] = allocimage(display, ZR, XRGB32, 1, 0xffff00ff); 142 palette[2] = allocimage(display, ZR, XRGB32, 1, 0xff0000ff); 143 palette[3] = allocimage(display, ZR, XRGB32, 1, 0x1f0000ff); 144 145 const char barsample[] = "00|00 ff|01 ff|02 ff|03 ff|04 ff|05 ff|06 ff|07 ff"; 146 cw = stringwidth(font, "0"); 147 ch = font->height; 148 149 progmask = allocimage(display, 150 Rect(0, 0, strlen(barsample) * cw, 256 * ch), GREY1, 0, DBlack); 151 152 drawprogmask(); 153 154 wxf_wt = (Widget) { 155 ZR, palette, &wavetable, wxfiledraw, wxfileactivate, 156 }; 157 wxf_prog = (Widget) { 158 ZR, palette, &prog, wxfiledraw, wxfileactivate, 159 }; 160 wx_prog = (Widget) { 161 ZR, palette, &ped, wxprogdraw, nil, 162 }; 163 164 Widget *wp[] = {&wxf_wt, &wxf_prog, &wx_prog, nil}; 165 ui.wp = wp; 166 167 rcalc(&ui); 168 169 draw(screen, screen->r, palette[3], nil, ZP); 170 drawwidgets(&ui); 171 flushimage(display, 1); 172 173 Alt alts[] = { 174 {ui.mctl->resizec, ui.rv, CHANRCV}, 175 {ui.mctl->c, &(ui.mv), CHANRCV}, 176 {ui.kctl->c, &(ui.kv), CHANRCV}, 177 {nil, nil, CHANEND}, 178 }; 179 180 for (;;) switch (alt(alts)) { 181 case 0: /* resize */ 182 if(getwindow(display, Refnone) < 0) sysfatal("resize failed: %r"); 183 draw(screen, screen->r, palette[3], nil, ZP); 184 rcalc(&ui); 185 drawwidgets(&ui); 186 flushimage(display, 1); 187 break; 188 case 1: /* mouse */ 189 handlemouse(&ui); 190 break; 191 case 2: /* keyboard */ 192 handlekeyboard(&ui); 193 break; 194 } 195 } 196 197 void 198 usage(void) 199 { 200 fprint(2, "usage: %s wavetable prog\n", argv0); 201 threadexitsall("usage"); 202 } 203 204 void 205 threadsynth(void *) 206 { 207 208 Phasor phasor; 209 Synth synth; 210 synth.table[0] = (s16int *)wavetable.p; 211 synth.flag = 0; 212 Channel *c = synthc; 213 Inter inter; 214 inter.progstart = prog.p; 215 int audiofd, note = 0, newnote, freq = 0, flag = 0; 216 Rune r; 217 s16int *buf; 218 audiofd = open("/dev/audio", OWRITE); 219 if (audiofd < 0) { 220 fprint(2, "threadsynth: %r\n"); 221 return; 222 } 223 buf = mallocz(OutBufSize * sizeof(s16int), 1); 224 if (buf == nil) { 225 sysfatal("threadsynth failed to allocate buffer: %r"); 226 } 227 228 long n = 1; 229 while (n > 0) { 230 231 if (nbrecv(c, &r) != 0) { 232 if (r == L'\0') { 233 flag = 0; 234 } else { 235 newnote = rune2note(r); 236 if ((newnote >=0) && 237 ((newnote != note) || (flag == 0))) { 238 interreset(&inter); 239 flag |= FHold; 240 note = newnote; 241 phasor.t = 0; 242 freq = note2freq(note); 243 } 244 } 245 } 246 247 interstep(&inter, flag); 248 249 synth.mod.wtsel = 0; 250 synth.mod.freq = freq; 251 synth.mod.phase = 0; 252 synth.mod.mod = inter.mod[0]; 253 synth.mod.vol = inter.mod[1]; 254 synth.mod.pan = 0; 255 256 int i; 257 for (i = 0; i < OutBufSize - 1; i += 2) { 258 union { u32int i; s16int s[2]; } out; 259 out.i = wtstep(&synth, &phasor); 260 buf[i] = out.s[0]; 261 buf[i + 1] = out.s[1]; 262 } 263 n = write(audiofd, buf, OutBufSize * sizeof(s16int)); 264 } 265 free(buf); 266 } 267 268 void 269 threadkbd(void *) 270 { 271 int kbd; 272 Rune r; 273 long n; 274 char kbuf[KbdBufSize]; 275 Rune rbuf[KbdBufSize]; 276 kbd = open("/dev/kbd", OREAD); 277 if (kbd < 0) { 278 fprint(2, "threadkbd: %r\n"); 279 return; 280 } 281 memset(kbuf, 0, KbdBufSize); 282 283 while ((read(kbd, kbuf, KbdBufSize)) > 0) { 284 285 286 n = runesnprint(rbuf, KbdBufSize, "%s", kbuf + 1); 287 288 if (rbuf[n-1] == Kdel) break; 289 290 switch (kbuf[0]) { 291 case 'K': 292 case 'k': 293 if (n == 0) r = L'\0'; 294 else r = rbuf[n - 1]; 295 nbsend(synthc, &r); 296 break; 297 case 'c': 298 /* hacks! */ 299 // nbsend(ui.kctl->c, &rbuf[0]); 300 break; 301 } 302 memset(kbuf, 0, KbdBufSize); 303 } 304 close(kbd); 305 } 306 307 void 308 handlemouse(UI *ui) 309 { 310 311 enum { MFree, }; 312 static state = MFree; 313 switch (state) { 314 case MFree: 315 ui->hot = findhot(ui); 316 if ((ui->mv.buttons == 1) && (ui->hot != nil)) { 317 if (ui->hot->activate) ui->hot->activate(ui, ui->hot); 318 drawwidgets(ui); 319 flushimage(display, 1); 320 } 321 if (ui->mv.buttons == 4) threadexitsall(nil); 322 break; 323 } 324 } 325 326 void 327 handlekeyboard(UI *ui) 328 { 329 char buf[256]; 330 switch (ui->kv) { 331 case ' ': 332 proccreate(threadkbd, nil, 64 * 1024); 333 break; 334 case Kup: 335 ped.curl--; 336 if (ped.curl < 0) ped.curl = 127; 337 break; 338 case Kdown: 339 ped.curl++; 340 if (ped.curl >= 128) ped.curl = 0; 341 break; 342 case Kleft: 343 ped.curc--; 344 if (ped.curc < 0) ped.curc = 31; 345 break; 346 case Kright: 347 ped.curc++; 348 if (ped.curc >= 32) ped.curc = 0; 349 break; 350 default: 351 if (isxdigit(ui->kv)) { 352 sprint(buf, "%C\0", ui->kv); 353 int n, i; 354 i = ped.curl * 32 + ped.curc; 355 n = strtol(buf, nil, 16); 356 if ((i & 1) != 0) { 357 ped.p[i>>1] = ped.p[i>>1] & 0xf0 | n; 358 } else { 359 ped.p[i>>1] = ped.p[i>>1] & 0x0f | (n << 4); 360 } 361 drawprogmask(); 362 if ((ped.curc & 1) == 0) ped.curc++; 363 else { 364 ped.curc--; 365 ped.curl = (ped.curl + 1) % ProgMax; 366 } 367 } 368 } 369 drawwidgets(ui); 370 flushimage(display, 1); 371 } 372 373 int 374 loadfile(char *path, void *p, long size) 375 { 376 int fd; 377 long n; 378 fd = open(path, OREAD); 379 if (fd < 0) { 380 fprint(2, "%r\n"); 381 return 0; 382 }; 383 n = read(fd, p, size); 384 close(fd); 385 return n > 0; 386 } 387 388 int 389 loadwavetable(void *p, char *f) 390 { 391 return loadfile(f, p, sizeof(s16int) * WFSize * WTSize); 392 } 393 394 void 395 rcalc(UI *ui) 396 { 397 Point min = screen->r.min; 398 Point max = screen->r.max; 399 400 Rectangle r1 = Rpt(min, Pt(max.x, min.y + ch)); 401 ui->wp[0]->r = r1; 402 Rectangle r2 = Rpt(Pt(min.x, r1.max.y), Pt(max.x, r1.max.y + ch)); 403 ui->wp[1]->r = r2; 404 Rectangle r3 = Rpt(Pt(min.x, r2.max.y), max); 405 ui->wp[2]->r = r3; 406 407 } 408 409 Widget * 410 findhot(UI *ui) 411 { 412 Widget **w = ui->wp; 413 Point xy = ui->mv.xy; 414 for (; *w != nil; w++) { 415 if (ptinrect(xy, (*w)->r) != 0) return *w; 416 } 417 return nil; 418 } 419 420 void 421 drawwidgets(UI *ui) 422 { 423 Widget **wp = ui->wp; 424 while (*wp != nil) { 425 if ((*wp)->draw) (*wp)->draw(ui, *wp); 426 wp++; 427 }; 428 } 429 430 void 431 drawprogmask(void) 432 { 433 int i; 434 draw(progmask, progmask->r, display->black, nil, ZP); 435 for (i = 0; i < 128; i++) { 436 Point pt = Pt(0, i * ch); 437 char buf[256]; 438 char *prp = prog.p + i * 16; 439 snprint(buf, 256, 440 "%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX %02uhhX:%02uhhX\0", 441 i, 442 prp[0], prp[1], prp[2], prp[3], prp[4], prp[5], prp[6], prp[7], 443 prp[8], prp[9], prp[10], prp[11], prp[12], prp[13], prp[14], prp[15]); 444 445 string(progmask, pt, display->white, ZP, font, buf); 446 } 447 } 448 449 void 450 wxfiledraw(UI *, void *arg) 451 { 452 Widget *w = (Widget *) arg; 453 FBuf *fb = w->aux; 454 static char buf[NameBufSize]; 455 snprint(buf, NameBufSize, "path: %s", fb->path); 456 draw(screen, w->r, w->palette[0], nil, ZP); 457 string(screen, w->r.min, w->palette[1], ZP, font, buf); 458 } 459 460 void 461 wxfileactivate(UI *ui, void *arg) 462 { 463 Widget *w = (Widget *) arg; 464 FBuf *fb = w->aux; 465 if (enter("path:", fb->path, NameBufSize, ui->mctl, ui->kctl, nil) != 0) { 466 loadfile(fb->path, fb->p, fb->n); 467 } 468 drawprogmask(); 469 } 470 471 void 472 wxprogdraw(UI *, void *arg) 473 { 474 int n; 475 Rectangle r; 476 Widget *w = (Widget *) arg; 477 ProgEd *ped = w->aux; 478 479 draw(screen, w->r, w->palette[3], nil, ZP); 480 481 r = w->r; 482 r.min.y += ped->curl * ch; 483 r.max.y = r.min.y + ch; 484 draw(screen, r, w->palette[0], nil, ZP); 485 486 487 draw(screen, w->r, w->palette[2], progmask, ZP); 488 489 n = ped->curc + 3 + (ped->curc)/2; 490 491 r = w->r; 492 r.min.x += n * cw; 493 r.max.x = r.min.x + cw; 494 r.min.y += ped->curl * ch; 495 r.max.y = r.min.y + ch; 496 draw(screen, r, w->palette[1], progmask, subpt(r.min, w->r.min)); 497 }