#include #include #include #include "/sys/src/games/catback.p" enum{ SHIFT = 16, MASK = 1023, /* Δφ = (MASK + 1 << SHIFT) * Hz / 44100 */ ♮C = 398128UL, ♯C = 421801UL, ♭D = ♯C, ♮D = 446883UL, ♯D = 473455UL, ♭E = ♯D, ♮E = 501609UL, ♮F = 531435UL, ♯F = 563036UL, ♭G = ♯F, ♮G = 596516UL, ♯G = 631987UL, ♭A = ♯G, ♮A = 669567UL, ♯A = 709382UL, ♭B = ♯A, ♮B = 751563UL }; double tab[MASK + 1]; uint t; uchar buf[0x8000]; /* 2 × 16bit samps × ♪ duration @ ~80bpm */ ulong noise = 0x70f4f853UL; void makenoise(void){ static ulong shiftreg = 0x70f4f854UL; noise += shiftreg; shiftreg ^= noise; } double hatenv; double hihat(void){ static ulong φ; static double lastin, out, m; double in; φ += 0xE999999UL; in = (long)(φ & 0x40000000 | noise & 0x3fffffff) - 0x40000000; out += m - lastin + in; m = m * 0.9 - out * 0.76; /* hpf: knobs are resonance & cutoff */ lastin = in; hatenv *= 0.99991; return out * hatenv; } int shook = 999999999; double shaker(void){ static double amp, targ, curve, lastin, out, m; double in; if(--shook == 0){ amp = 7e-8; targ = 7e-12 * (0x200 | noise & 0xffff); curve = 1.0008; } if(amp > targ) curve = 0.9996; amp *= curve; in = noise * amp; out += m - lastin + in; m = m * 0.3 - out * 0.86; /* hpf: knobs are resonance & cutoff */ lastin = in; return out; } double clapenv[2]; double clap(void){ static double low, band; low += 0.2 * band; band = 0.2 * ((clapenv[0] + clapenv[1]) * noise - low - 0.4 * band) + band; clapenv[1] *= 0.99992; clapenv[0] *= 0.999; return band; } int chordi; double kickenv; double kick(void){ static ulong φ₀, Δφ₀[] = {♮C * 0.250, ♮F * 0.250, ♮C * 0.250, ♮G * 0.250}; static ulong φ₁, Δφ₁[] = {♮C * 0.246, ♮F * 0.246, ♮C * 0.246, ♮G * 0.246}; ulong mod; kickenv *= 0.9991; φ₀ += Δφ₀[chordi] + kickenv; φ₁ += Δφ₁[chordi]; mod = (6e4 + kickenv) * 1111.1 * tab[φ₁ >> SHIFT & MASK]; mod = 4e5 * tab[φ₁ + mod >> SHIFT & MASK]; return tab[φ₀ + mod >> SHIFT & MASK] * 9000.0; } double chordamp, chordmod; double chords(void){ static ulong Δφ[4][5] = { {♮C/2*1.01, ♮G/2*1.01, ♮D*1.01, ♭B*1.01, ♮F*2*1.01}, /* Cmin11 */ {♮C/2*1.01, ♮F/2*1.01, ♭E*1.01, ♭A*1.01, ♮G*2*1.01}, /* Fmin9 */ {♮C/2*1.01, ♮G/2*1.01, ♭E*1.01, ♭B*1.01, ♮F*2*1.01}, /* Cmin11 */ {♮B/4*1.01, ♮G/2*1.01, ♯D*1.01, ♮B*1.01, ♮F*2*1.01} /* G7♯5 */ }; static ulong detuneΔφ[4][5] = { {♮C/2*0.99, ♮G/2*0.99, ♮D*0.99, ♭B*0.99, ♮F*2*0.99}, {♮C/2*0.99, ♮F/2*0.99, ♭E*0.99, ♭A*0.99, ♮G*2*0.99}, {♮C/2*0.99, ♮G/2*0.99, ♭E*0.99, ♭B*0.99, ♮F*2*0.99}, {♮B/4*0.99, ♮G/2*0.99, ♯D*0.99, ♮B*0.99, ♮F*2*0.99} }; static ulong modΔφ[4][5] = { {♮C/2*3, ♮G/2*3, ♮D*3, ♭B*3, ♮F*2*3}, {♮C/2*3, ♮F/2*3, ♭E*3, ♭A*3, ♮G*2*3}, {♮C/2*3, ♮G/2*3, ♭E*3, ♭B*3, ♮F*2*3}, {♮B/4*3, ♮G/2*3, ♯D*3, ♮B*3, ♮F*2*3} }; static ulong φ[5], detuneφ[5], modφ[5]; ulong mod; double out; int i; chordamp *= 0.99999; chordmod *= 0.999994; for(out = i = 0; i < 5; i++){ φ[i] += Δφ[chordi][i]; detuneφ[i] += detuneΔφ[chordi][i]; modφ[i] += modΔφ[chordi][i]; mod = (1e7 - chordmod) * tab[modφ[i] >> SHIFT & MASK]; out += tab[φ[i] + mod >> SHIFT & MASK] + tab[detuneφ[i] + mod >> SHIFT & MASK]; } return out * chordamp; } double plink₀env; ulong plink₀carΔφ, plink₀modΔφ; void trigplink₀(void){ static ulong n[] = { ♭B, ♮G/2, ♮F*2, ♭A, ♮F/2, ♮G*2, ♭B, ♮G/2, ♮F*2, ♮B, ♮G/2, ♮F*2 }; static int i, j; plink₀modΔφ = n[j | i] * 7; plink₀carΔφ = n[j | i] * 7 >> 1; if(i == 3){ if((t & 3) == 3) j = j + 4 & 12; i = 0; }else i++; plink₀env = 999.9; } double plink₀(void){ static ulong carφ, modφ; ulong mod; modφ += plink₀modΔφ; carφ += plink₀carΔφ; plink₀env *= 0.9999; mod = 4e4 * plink₀env * tab[modφ >> SHIFT & MASK]; return tab[carφ + mod >> SHIFT & MASK] * plink₀env; } double plink₁env; ulong plink₁carΔφ, plink₁modΔφ; void trigplink₁(void){ static ulong n[] = { ♮F*2, ♮D, ♮C/2, ♮D, ♮G*2, ♭E, ♮C/2, ♭E, ♮F*2, ♮D, ♮C/2, ♮D, ♮F*2, ♯D, ♮B/4, ♯D }; static int i, j; plink₁modΔφ = n[j | i] * 7; plink₁carΔφ = n[j | i] * 7 >> 1; if(i == 7){ if((t & 3) == 3) j = j + 4 & 12; i = 0; }else i++; plink₁env = 999.9; } double plink₁(void){ static ulong carφ, modφ; ulong mod; modφ += plink₁modΔφ; carφ += plink₁carΔφ; plink₁env *= 0.9999; mod = 4e4 * plink₁env * tab[modφ >> SHIFT & MASK]; return tab[carφ + mod >> SHIFT & MASK] * plink₁env; } double stutteramp; double stutter(void){ static ulong Δφ[] = {♮F*2, ♮G*2, ♮C*2, ♯D*2}; static ulong modΔφ[] = {♮F*2*5, ♮G*2*5, ♮F*2*5, ♮F*2*5}; static ulong φ, modφ, lfoφ; ulong mod; φ += Δφ[chordi]; modφ += modΔφ[chordi]; lfoφ += 999; mod = 3e8 * tab[modφ >> SHIFT & MASK] * tab[lfoφ >> SHIFT & MASK]; stutteramp *= 0.99993; return t >> 11 & 9 ? tab[φ + mod >> SHIFT & MASK] * stutteramp : 0.0; } uint onpat[] = {0x280A800A, 0x80070000, 0xA02900A, 0x800E0842}; uint offpat[] = {0x10450205, 0x00200100, 0x4812845, 0x00201081}; int note = 24, decaying = 999999999; double tang, wah; void triglead(void){ note = note >= 24 ? 0 : note + 1; decaying = noise & 4095; /* groove */ wah = 3e7; tang = 4e7; } double lead(void){ static ulong Δφ1[] = { ♮C/2, ♭B/2, ♮C/2, ♮G/2, ♭E/2, ♭E/2, ♮G/2, ♮F/2, ♭E/2, ♮C/2, ♮F/4, ♮C/2, ♭B/2, ♮C/2, ♮G/2, ♭E/2, ♭E/2, ♮F/2, ♮G/2, ♭A/2, ♮G/2, ♮F/2, ♮B/4, ♭E/2, ♮C/2 }; static ulong Δφ2[] = { ♮C, ♭B, ♮C, ♮G, ♭E, ♭E, ♮G, ♮F, ♭E, ♮C, ♮F/2, ♮C, ♭B, ♮C, ♮G, ♭E, ♭E, ♮F, ♮G, ♭A, ♮G, ♮F, ♮B/2, ♭E, ♮C }; static ulong φ1, φ2, lfoφ; static double amp; ulong mod; φ1 += Δφ1[note] + 4999.9 * tab[lfoφ >> SHIFT & MASK]; lfoφ += 321; φ2 += Δφ2[note]; amp = --decaying > 0 ? amp * 0.99995 : 3333.3 - 0.9999 * (3333.3 - amp); wah *= 0.999964; tang *= 0.99997; mod = (5e7 - wah) * tab[φ1 >> SHIFT & MASK]; mod = (4e7 - tang) * tab[φ2 + mod >> SHIFT & MASK]; return tab[φ1 + mod >> SHIFT & MASK] * amp; } Image *weave, *bg; void carpet(void){ static Rectangle r; static int i; switch(i++ & 7){ case 0: r = screen->r; r.max.x = Dx(r) >> 2; break; case 1: case 2: case 3: r.min.x = r.max.x; r.max.x = r.min.x + Dx(screen->r) / 4; break; case 4: r = screen->r; r.max.y = Dy(r) >> 2; break; case 5: case 6: case 7: r.min.y = r.max.y; r.max.y = r.min.y + Dy(screen->r) / 4; break; } loadimage(weave, weave->r, buf, sizeof(buf)); loadimage(bg, bg->r, buf + (noise & 0xfff), 3); draw(screen, r, bg, weave, ZP); } Image *head[32], *fg; Point max; int wfd; void roll(void){ enum{RD = 192 + 2 * Borderwidth, → = 20, ↓ = 70, ← = -28, ↑ = -56}; static int x, y, dx, dy; static uchar b[3] = {0, 255, 255}, f[3]; if(x + dx <= 0){ dx = →; x = 0; }else if(x + dx + RD >= max.x){ dx = ←; x = max.x - RD; }else x += dx; if(y + dy <= 0){ dy = ↓; y = 0; }else if(y + dy + RD >= max.y){ dy = ↑; y = max.y - RD; }else y += dy; b[0] = (t ^ !(t & 0x80000) - 1) >> 11; f[0] = b[0] ^ 0xFF; loadimage(bg, bg->r, b, 3); loadimage(fg, fg->r, f, 3); fprint(wfd, "resize -r %d %d %d %d", x, y, x + RD, y + RD); getwindow(display, Refnone); draw(screen, screen->r, bg, nil, ZP); draw(screen, screen->r, fg, head[(t ^ (dx == → ^ dy == ↓) -1) >> 13 & 31], ZP); } Image *clock[64]; void bounce(void){ enum{ BW = 224 + 2 * Borderwidth, BH = 556 + 2 * Borderwidth, → = 5, ↓ = 3, ← = -4, ↑ = -1 }; static int x, y, dx, dy; static uchar b[3] = {255, 0, 255}, f[3]; if(x + dx <= 0){ dx = →; x = 0; }else if(x + dx + BW >= max.x){ dx = ←; x = max.x - BW; }else x += dx; if(y + dy <= 0){ dy = ↓; y = 0; }else if(y + dy + BH >= max.y){ dy = ↑; y = max.y - BH; }else y += dy; b[1] = (t ^ !(t & 0x80000) - 1) >> 11; f[0] = f[2] = b[1] ^ 0xFF; loadimage(bg, bg->r, b, 3); loadimage(fg, fg->r, f, 3); fprint(wfd, "resize -r %d %d %d %d", x, y, x + BW, y + BH); getwindow(display, Refnone); draw(screen, screen->r, bg, nil, ZP); draw(screen, screen->r, fg, clock[t >> 13 & 63], ZP); } Image* eallocimage(int x, int y, ulong chan, int repl, ulong fill){ Image *i; i = allocimage(display, Rect(0, 0, x, y), chan, repl, fill); if(i == nil) sysfatal("allocimage: %r"); return i; } void draweyes(int i, Image *dst, Image *src, Point o, int r){ Point p[28]; double θ, φ, cosφ, x₀, x₁, y, z, z₀; θ = 0.7 * sin(PIO2 - PI * i / 31.0) + PIO2; /* 0 = left, 31 = right */ x₀ = sin(θ + PIO2 + PI / 7.0); x₁ = sin(θ + PIO2 - PI / 7.0); z₀ = sin(θ); p[0] = Pt(o.x, o.y - r); p[nelem(p) / 2] = Pt(o.x, o.y + r); for(i = 1, φ = -PIO2; i <= nelem(p) >> 2; i++){ cosφ = cos(φ += 2.0 * PI / nelem(p)); z = r / (1.0 + cosφ * z₀); y = sin(φ) * z; p[i].x = p[nelem(p) / 2 - i].x = cosφ * x₀ * z + o.x; p[nelem(p) - i].x = p[nelem(p) / 2 + i].x = cosφ * x₁ * z + o.x; p[i].y = p[nelem(p) - i].y = o.y + y; p[nelem(p) / 2 + i].y = p[nelem(p) / 2 - i].y = o.y - y; } fillpoly(dst, p, nelem(p), 1, src, ZP); r *= 2.69565217391; /* magic spacing */ for(i = 0; i < nelem(p); i++) p[i].x += r; fillpoly(dst, p, nelem(p), 1, src, ZP); } void drawtail(int i, Image *dst, Image *src, Point o, double l){ Point p[7]; double θ, s, c; θ = 0.4 * sin(PIO2 - PI * i / 31.0) - 0.145728397239; /* 0 = right, 31 = left */ s = l * sin(θ); c = l * cos(θ); p[0] = o; p[1] = addpt(o, Pt(76 * s, 76 * c)); p[2] = addpt(o, Pt(3 * c + 82 * s, 82 * c - 3 * s)); p[3] = addpt(o, Pt(10 * c + 84 * s, 84 * c - 10 * s)); p[4] = addpt(o, Pt(18 * c + 82 * s, 82 * c - 18 * s)); p[5] = addpt(o, Pt(21 * c + 76 * s, 76 * c - 21 * s)); p[6] = addpt(o, Pt(21 * c + 70 * s, 70 * c - 21 * s)); fillpoly(dst, p, nelem(p), 1, src, ZP); } void drawhand(int i, Image *dst, Image *src, Point o, int l, int w){ Point p[4]; double θ, c, s; θ = PI + PI * i / 32.0; /* 0 = noon */ s = sin(θ); c = cos(θ); p[0] = addpt(o, Pt(l * s, l * c)); s *= w; c *= w; p[1] = addpt(o, Pt(s + c, c - s)); p[2] = addpt(o, Pt(s - c, c + s)); p[3] = p[0]; poly(dst, p, 4, Endsquare, Endsquare, 0, src, ZP); } void ↻(uchar *s, uchar *d){ uchar *b; int i, shift; for(i = 0; i < 24; i++) for(shift = 0; shift < 8; shift++) for(b = s + 184 * 24 + i; b >= s; b -= 8 * 24) *d++ = b[0] << shift >> 7 & 1 | b[24] << shift >> 6 & 2 | b[48] << shift >> 5 & 4 | b[72] << shift >> 4 & 8 | b[96] << shift >> 3 & 16 | b[120] << shift >> 2 & 32 | b[144] << shift >> 1 & 64 | b[168] << shift & 128; } void main(int, char **){ uchar magtab[] = { 0x00, 0x03, 0x0C, 0x0F, 0x30, 0x33, 0x3C, 0x3F, 0xC0, 0xC3, 0xCC, 0xCF, 0xF0, 0xF3, 0xFC, 0xFF }; char *w = "resize -r ................................................"; Image *eyes[32], *tail[32]; int fd, l, r, i; double samp, pan; uint bar; if((fd = open("/dev/audio", OWRITE)) < 0) sysfatal("open: %r"); if((wfd = open("/dev/wctl", ORDWR)) < 0) sysfatal("open: %r"); read(wfd, w + 10, 48); write(wfd, "resize -r 0 0 9999 9999", 23); if(initdraw(nil, nil, "here, kitty kitty") < 0) sysfatal("initdraw: %r"); max = Pt(screen->r.max.x + Borderwidth, screen->r.max.y + Borderwidth); for(i = 0; i < nelem(tab); i++) tab[i] = sin(2.0 * PI * i / nelem(tab)); weave = eallocimage(sizeof(buf) / 32, 256, GREY1, 1, DNofill); bg = eallocimage(1, 1, RGB24, 1, DNofill); fg = eallocimage(1, 1, RGB24, 1, DNofill); for(i = 0; i < 202; i++) for(l = 0; l < 14; l++){ buf[56 * i + 1 + 2 * l] = buf[56 * i + 28 + 2 * l] = magtab[catback_bits[(i + 9) * 19 + 2 + l] & 15]; buf[56 * i + 1 + 2 * l + 1] = buf[56 * i + 28 + 2 * l + 1] = magtab[catback_bits[(i + 9) * 19 + 3 + l] >> 4]; } for(i = 0; i < 64; i++){ clock[i] = eallocimage(224, 556, GREY1, 0, DNofill); loadimage(clock[i], clock[i]->r, buf, 556 * 28); draweyes(6 * i, clock[i], display->white, Pt(84, 63), 24); drawtail(i, clock[i], display->white, Pt(112, 382), 2.0); drawhand(i, clock[i], display->white, Pt(112, 281), 35, 6); drawhand(12 * i, clock[i], display->white, Pt(110, 281), 60, 4); } for(i = 0; i < 96; i++) for(l = 0; l < 12; l++){ buf[48 * i + 2 * l] = buf[48 * i + 24 + 2 * l] = magtab[catback_bits[(i + 10) * 19 + 3 + l] & 15]; buf[48 * i + 2 * l + 1] = buf[48 * i + 24 + 2 * l + 1] = magtab[catback_bits[(i + 10) * 19 + 4 + l] >> 4]; } l = Dx(screen->r); r = Dy(screen->r); for(i = 0; i < 32; i++){ eyes[i] = eallocimage(Dx(screen->r), Dy(screen->r), GREY1, 0, DBlack); draweyes(i, eyes[i], display->white, Pt(0.138602271463 * l, r / 2), 0.268133798592 * l); head[i] = eallocimage(192, 192, GREY1, 0, DNofill); loadimage(head[i], head[i]->r, buf, 4608); draweyes(i, head[i], display->white, Pt(64, 63), 24); tail[i] = eallocimage(l, r, GREY1, 0, DBlack); drawtail(i, tail[i], display->white, Pt(l >> 1, r / -12), r / 84.0); } for(i = 1; i < 32; i += 4){ unloadimage(head[i], head[0]->r, buf, 4608); ↻(buf, buf + 4608); loadimage(head[i], head[i]->r, buf + 4608, 4608); } for(i = 2; i < 32; i += 4){ unloadimage(head[i], head[0]->r, buf, 4608); ↻(buf, buf + 4608); ↻(buf + 4608, buf); loadimage(head[i], head[i]->r, buf, 4608); } for(i = 3; i < 32; i += 4){ unloadimage(head[i], head[0]->r, buf, 4608); ↻(buf, buf + 4608); ↻(buf + 4608, buf); ↻(buf, buf + 4608); loadimage(head[i], head[i]->r, buf + 4608, 4608); } if((i = open("/dev/random", OREAD)) >= 0){ readn(i, buf, sizeof(buf)); close(i); } while(t < 0x800000){ /* sequence */ /* per bar triggers */ chordi = t >> 18 & 3; if(t >= 0x100000) chordamp = 800.0, chordmod = 1e7; /* step through a bar */ for(bar = 0x80000000; bar; bar >>= 1){ /* gfx */ if(t >= 0x400000 && t < 0x600000) bounce(); else if(t >= 0x200000 && t < 0x300000 || t >= 0x700000) roll(); else{ if(t == 0x300000 || t == 0x600000){ write(wfd, "resize -r 0 0 9999 9999", 23); getwindow(display, Refnone); } if(bar & 0x9ADA9ADE) carpet(); i = (t ^ !(t & 0x40000) -1) >> 13 & 31; if(t >= 0x300000 && t < 0x680000) draw(screen, screen->r, bg, eyes[i], ZP); else if(t >= 0x100000) draw(screen, screen->r, bg, tail[i], ZP); } flushimage(display, 1); /* triggers */ if(t >= 0x60000 && (t < 0x400000 || t >= 0x500000) && t < 0x700000 && bar & 0x008000C0){ clapenv[0] = (4092 | noise & 2047) * 3e-9; clapenv[1] = (512 | noise & 1023) * 3e-9; } if((t < 0x400000 || t >= 0x500000) && t < 0x700000 && bar & 0x9ADA9ADE) hatenv = (25 + (noise & 127)) * 1e-8; if(t >= 0x80000 && (t < 0x400000 || t >= 0x500000) && t < 0x700000 && bar & 0xA219A229) kickenv = 2.2e5; if(t & 0x200000 && bar & 0xAAAAAAAA) trigplink₀(); if(t & 0x200000 && bar & 0x55555555) trigplink₁(); if(t >= 0x400000 && (t < 0x500000 || t >= 0x700000) && bar & 0xD9DAD9DE) shook = noise & 511 | 1; /* jazz */ if(t >= 0x300000 && (t < 0x400000 || t >= 0x700000) && t < 0x80000 && bar & 0x32423142) stutteramp = 1000.0; if(t >= 0x400000 && t < 0x700000 && bar & onpat[chordi]) triglead(); if(t >= 0x400000 && t < 0x700000 && bar & offpat[chordi]) decaying = 999999999; /* play a ♪ */ for(i = 0; i < sizeof(buf); t++){ /* everything noises the same… */ makenoise(); /* mono stuff */ l = r = 0 + stutter() + shaker() + hihat() + kick() + clap() + chords() ; /* panned stuff */ samp = lead(); pan = ((t ^ !(t & 0x40000) - 1) & 0x3ffff) / 262144.0; l += samp * (1.8 - pan); r += samp * (0.8 + pan); samp = plink₀(); l += samp; r += samp * 0.5; samp = plink₁(); l += samp * 0.5; r += samp; /* rinse & repeat */ buf[i++] = l; buf[i++] = l >> 8; buf[i++] = r; buf[i++] = r >> 8; } /* please excuse me the outflow */ if(write(fd, buf, sizeof(buf)) != sizeof(buf)) sysfatal("write: %r"); } } closedisplay(display); write(wfd, w, 58); close(wfd); r = 0; while(t < 0x8C0000){ chordi = t >> 18 & 3; chordamp = 800.0, chordmod = 1e7; for(bar = 0x80000000; bar; bar >>= 1){ if(bar & 0xD9DAD9DE){ shook = noise & 511 | 1; write(1, "happy new year... twenty twenty two...\n" ".......from umbraticus\n" + r++, 1); } if(t < 0x880000 && bar & 0x32423142) stutteramp = 1000.0; for(i = 0; i < sizeof(buf); t++){ makenoise(); l = 0 + stutter() + shaker() + chords() ; buf[i++] = l; buf[i++] = l >> 8; buf[i++] = l; buf[i++] = l >> 8; } if(write(fd, buf, sizeof(buf)) != sizeof(buf)) sysfatal("write: %r"); } } close(fd); exits(nil); }