#include #include #include #include enum{N = 4096, ½N = N >> 1}; int y, barwid; Rectangle r; Image *grad, *mask; void eresized(int i){ int x, f, n; char *buf; Image *paint; if(i && getwindow(display, Refnone) < 0) sysfatal("can't reattach to window: %r"); x = Dx(screen->r); y = Dy(screen->r) - font->height; barwid = x / (16 * 7); if((buf = malloc(x > y ? x : y)) == nil) sysfatal("malloc: %r"); freeimage(grad); grad = allocimage(display, Rect(0, 0, x, y), RGB24, 0, DRed); mask = allocimage(display, Rect(0, 0, 1, y), GREY8, 1, DNofill); paint = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, DGreen); if(grad == nil || mask == nil || paint == nil) sysfatal("allocimage: %r"); for(i = 0; i < y; i++) buf[i] = 255.0 * i / y; loadimage(mask, mask->r, (uchar*)buf, y); draw(grad, grad->r, paint, mask, ZP); freeimage(mask); freeimage(paint); mask = allocimage(display, Rect(0, 0, x, 1), GREY8, 1, DNofill); paint = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, DBlue); if(mask == nil || paint == nil) sysfatal("allocimage: %r"); for(i = 0; i < x; i++) buf[i] = 255.0 * i / 2 / x; loadimage(mask, mask->r, (uchar*)buf, x); draw(grad, grad->r, paint, mask, ZP); freeimage(mask); freeimage(paint); mask = allocimage(display, Rect(0, 0, Dx(screen->r), y), GREY1, 0, DNofill); if(mask == nil) sysfatal("allocimage: %r"); r = screen->r; r.max.y = r.min.y + font->height; draw(screen, r, display->black, nil, ZP); r.max.y = r.min.y; for(f = 1, n = 16; n < ½N; n <<= 1){ r.min.x += 16 * barwid; sprint(buf, "%d Hz|", (f += n) * 22050 / ½N); r.max.x = r.min.x - stringwidth(font, buf); string(screen, r.max, display->white, ZP, font, buf); } free(buf); } void main(int, char**){ char io[N << 2]; int i₀, i₁, i₂, i₃, i₄, fd, ←[N]; double d₀, d₁, re[N], im[N], w[N], costab[½N], sintab[½N]; read(0, io, 0); /* allow pressing Del to exit if forgot to pipe something in */ if((fd = open("/dev/audio", OWRITE)) < 0) sysfatal("open: %r"); for(i₀ = 0; i₀ < N; i₀++){ /* windowing factors: • Goertzel: 1 • Hanning: 0.5 - 0.25 * cos(i₀ * 2.0 * PI / N) • Hamming: 0.54 - 0.46 * cos(i₀ * 2.0 * PI / N) • Blackman: 0.426551 - 0.496561 * cos(i₀ * 2.0 * PI / N) + 0.076848 * cos(i₀ * 4.0 * PI / N) */ w[i₀] = 0.426551 - 0.496561 * cos(i₀ * 2.0 * PI / N) + 0.076848 * cos(i₀ * 4.0 * PI / N); for(←[i₀] = 0, i₁ = i₀, i₂ = ½N; i₂; i₂ >>= 1, i₁ >>= 1) ←[i₀] = ←[i₀] << 1 | i₁ & 1; } for(i₀ = 0; i₀ < ½N; i₀++){ costab[i₀] = cos(PI * i₀ / ½N); sintab[i₀] = sin(PI * i₀ / ½N); } if(initdraw(nil, nil, "frequency analyzer") < 0) sysfatal("initdraw: %r"); einit(Emouse|Ekeyboard); eresized(0); while(readn(0, io, sizeof(io)) == sizeof(io) && write(fd, io, sizeof(io)) == sizeof(io) && (!ecankbd() || ekbd() != 0x7F && ekbd() != 0x7F)){ for(i₀ = i₁ = 0; i₀ < N; i₀++, i₁ += 4){ re[←[i₀]] = w[i₀] * ((s16int)(io[i₁] | io[i₁ + 1] << 8) + (s16int)(io[i₁ + 2] | io[i₁ + 3] << 8)); im[i₀] = 0.0; } for(i₀ = 1, i₁ = ½N; i₁; i₀ <<= 1, i₁ >>= 1) for(i₂ = 0; i₂ < N; i₂ += i₀ << 1) for(i₃ = i₄ = 0; i₃ < i₀; i₃++, i₄ += i₁){ d₀ = costab[i₄] * re[i₀ + i₂ + i₃] - sintab[i₄] * im[i₀ + i₂ + i₃]; d₁ = costab[i₄] * im[i₀ + i₂ + i₃] + sintab[i₄] * re[i₀ + i₂ + i₃]; re[i₀ + i₂ + i₃] = re[i₂ + i₃] - d₀; re[i₂ + i₃] += d₀; im[i₀ + i₂ + i₃] = im[i₂ + i₃] - d₁; im[i₂ + i₃] += d₁; } r = screen->r; r.min.y += font->height; draw(screen, r, grad, nil, ZP); draw(mask, mask->r, display->white, nil, ZP); r = Rect(0, 0, barwid, y); for(i₀ = i₁ = 1; i₀ < ½N; i₁ <<= 1) for(i₂ = 0; i₂ < 16; i₂++){ for(d₁ = 0.0, i₃ = i₀ + i₁; i₀ < i₃; i₀++){ d₀ = re[i₀] * re[i₀] + im[i₀] * im[i₀]; if(d₀ > 0.0 && (d₀ = (log10(d₀) - 10) * 0.25) > d₁) /* ¯\_(ツ)_/¯ */ d₁ = d₀; } r.min.y = y - d₁ * y; draw(mask, r, display->black, nil, ZP); r.min.x = r.max.x; r.max.x += barwid; } draw(screen, screen->r, display->black, mask, Pt(0, -font->height)); flushimage(display, 1); } exits(nil); }