#include #include #include #include #include #include enum{ SAW = 0, TRI, SIN, SQ, WAVES, WAV = 128, RATE = 44100, BUF = RATE * 4 / 50, OCTS = 9, NOTES = OCTS * 12, SHARPS = 1354, /* 0b010101001010 */ WWD = 48, WHT = 180, BWD = 28, BHT = 120, }; char *waveforms[] = {"sawtooth", "triangle", "sine", "square", nil}; Menu b2 = {waveforms, nil, 0}; char *name = "CCDDEFFGGAAB"; char *key = "\t1q2we4r5t6yu8i9op-[=]\b\\"; int oct = 4, vol = 63, wav[WAVES][WAV], wavn; char on[NOTES]; u16int θ[NOTES], Δ[NOTES]; Rectangle w[14], b[10]; int woff[14] = {0,2,4,5,7,9,11,12,14,16,17,19,21,23}; int boff[10] = {1,3,6,8,10,13,15,18,20,22}; Image *down; Rune info[128]; Point infop; void redraw(void) { int i; Rune *r; draw(screen, screen->r, down, nil, ZP); for(i = 0; i < nelem(w); i++){ draw(screen, w[i], on[12*oct+woff[i]]?down:display->white, nil, ZP); border(screen, w[i], 1, display->black, ZP); } for(i = 0; i < nelem(b); i++){ draw(screen, b[i], on[12*oct+boff[i]]?down:display->black, nil, ZP); border(screen, b[i], 1, display->black, ZP); } for(i = 0, r = info; i < oct; i++) *r++ = '-'; *r++ = '0' + i++; *r++ = '0' + i; while(++i < OCTS) *r++ = '-'; *r++ = ' '; for(i = 0; i < NOTES; i++) if(on[i]){ *r++ = name[i % 12]; if(SHARPS & 1 << i % 12) *r++ = 0x266e; /* ♮ */ *r++ = i / 12 + '0'; *r++ = ' '; if(r - info > nelem(info) - 5) break; } *r = '\0'; runestring(screen, infop, display->black, ZP, font, info); flushimage(display, 1); } void rthread(void *arg) { Mousectl *m; Point p; int fd, i; for(m = arg;;){ recvul(m->resizec); if(getwindow(display, Refnone) < 0) sysfatal("getwindow: %r"); if((Dx(screen->r) != nelem(w) * WWD || Dy(screen->r) != WHT + font->height) &&(fd = open("/dev/wctl", OWRITE)) >= 0){ fprint(fd, "resize -dx %d -dy %d", nelem(w) * WWD + 2 * Borderwidth, WHT + font->height + 2 * Borderwidth); close(fd); } for(i = 0, p = screen->r.min; i < nelem(w); i++, p.x += WWD) w[i] = rectaddpt(Rect(0, 0, WWD, WHT), p); for(i = nelem(b), p.x -= WWD + BWD / 2; i--; p.x -= WWD){ b[i] = rectaddpt(Rect(0, 0, BWD, BHT), p); if(i % 5 == 0 || i % 5 == 2) p.x -= WWD; } infop = Pt(screen->r.min.x, screen->r.max.y - font->height); redraw(); } } void mthread(void *arg) { Mousectl *m; int i; for(m = arg;;){ recv(m->c, m); switch(m->buttons){ default: continue; case 1: for(i = 0; i < nelem(b); i++) if(ptinrect(m->xy, b[i])){ on[oct*12+boff[i]] |= 2; goto draw; } for(i = 0; i < nelem(w); i++) if(ptinrect(m->xy, w[i])){ on[oct*12+woff[i]] |= 2; break; } break; case 2: i = menuhit(2, m, &b2, nil); if(i >= 0 && i < WAVES) wavn = i; break; case 4: for(i = 0; i < nelem(b); i++) if(ptinrect(m->xy, b[i])){ on[oct*12+boff[i]] &= 1; goto draw; } for(i = 0; i < nelem(w); i++) if(ptinrect(m->xy, w[i])){ on[oct*12+woff[i]] &= 1; break; } break; } draw: redraw(); } } void kproc(void *) { int fd, n, i; char buf[128], *p; if((fd = open("/dev/kbd", OREAD)) < 0) sysfatal("open: %r"); for(;;){ if((n = read(fd, buf, sizeof(buf) - 1)) < 0) sysfatal("read: %r"); buf[n] = '\0'; for(p = buf; p - buf < n;){ if(*p++ != 'c'){ if(strchr(p, Kdel)){ close(fd); threadexitsall(nil); } if(strchr(p, 'a') && oct < OCTS - 2) oct++; if(strchr(p, 'z') && oct) oct--; if(strchr(p, 's') && vol < 126) vol += 7; if(strchr(p, 'x') && vol > 6) vol -= 7; for(i = 0; i < strlen(key); i++){ if(strchr(p, key[i])) on[12 * oct + i] |= 1; else on[12 * oct + i] &= 2; } redraw(); } while(*p++) ; } } } void threadmain(int,char *argv[]) { Mousectl *m; int fd, i, s; uchar buf[BUF], *p; for(i = 0; i < WAV / 2; i++){ wav[SAW][i] = 64 - 2 * 64 * i / WAV; wav[TRI][i] = 64 - 64 * i * 4 / WAV; wav[SIN][i] = 64 * sin(2 * PI * i / WAV); wav[SQ][i] = -64; } for(; i < WAV; i++){ wav[SAW][i] = 64 - 2 * 64 * i / WAV; wav[TRI][i] = 64 * (i - WAV / 2) * 4 / WAV - 64; wav[SIN][i] = 64 * sin(2 * PI * i / WAV); wav[SQ][i] = 64; } for(i = 0; i < NOTES; i++) Δ[i] = (440 << 16) * pow(2, (i - 57) / 12.0) / RATE; if((fd = open("/dev/audio", OWRITE)) < 0) sysfatal("open: %r"); if(initdraw(nil, nil, argv0 = argv[0]) < 0) sysfatal("initdraw: %r"); if((down = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen)) == nil) sysfatal("allocimage: %r"); if((m = initmouse(nil, screen)) == nil) sysfatal("initmouse: %r"); if(threadcreate(rthread, m, mainstacksize) < 0) sysfatal("threadcreate: %r"); if(threadcreate(mthread, m, mainstacksize) < 0) sysfatal("threadcreate: %r"); if(proccreate(kproc, nil, mainstacksize) < 0) sysfatal("proccreate: %r"); sendul(m->resizec, 1); for(;;){ for(p = buf; p - buf < BUF; p += 4){ for(i = 0, s = 0; i < NOTES; i++) if(on[i]) s += wav[wavn][(θ[i] += Δ[i]) >> 9]; s *= vol; p[0] = p[2] = s; p[1] = p[3] = s >> 8; } if(write(fd, buf, BUF) != BUF) break; yield(); } close(fd); }