Something went wrong. Try again.
a playable wind chime, rung by new records across atproto chimes.waow.tech
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970"""Bisect the tube's feedback loop for the delay that actually produces atarget pitch, to calibrate DC_LEAD in public/chime-processor.js.
The in-loop DC blocker contributes phase LEAD proportional to the period.Deriving it in closed form got both the magnitude and the sign wrong, so it ismeasured here against a direct simulation of the same loop instead.
uv run --no-project scripts/measure-loop-delay.py"""
import math
SR = 48000.0
def run_loop(D, damp=0.80, fb=0.9975, dcp=0.995, n=24000): size = int(SR / 60) + 4 buf = [0.0] * size burst = int(D) for i in range(burst): window = 0.5 - 0.5 * math.cos(2 * math.pi * i / burst) buf[i] = math.sin(2 * math.pi * i / burst) * window w = burst % size prev = x1 = y1 = 0.0 out = [] for _ in range(n): r = (w - D) % size i0 = int(r) frac = r - i0 s = buf[i0] * (1 - frac) + buf[(i0 + 1) % size] * frac filt = damp * s + (1 - damp) * prev prev = s y = filt - x1 + dcp * y1 x1, y1 = filt, y buf[w] = y * fb w = (w + 1) % size out.append(s) return out
def pitch(sig, want): start, win = int(0.25 * SR), int(0.2 * SR) best = (-9.0, 1) for lag in range(int(SR / (want * 1.3)), int(SR / (want * 0.77))): num = ea = eb = 0.0 for i in range(0, win, 4): a, b = sig[start + i], sig[start + i + lag] num += a * b ea += a * a eb += b * b c = num / (math.sqrt(ea * eb) + 1e-12) if c > best[0]: best = (c, lag) return SR / best[1]
for f in (174.61, 261.63, 440.0, 659.25): period = SR / f lo, hi = period - 14, period + 6 for _ in range(22): mid = (lo + hi) / 2 if pitch(run_loop(mid), f) > f: lo = mid else: hi = mid D = (lo + hi) / 2 # D = period * (1 + DC_LEAD) - (1 - damp) lead = (D + (1 - 0.80)) / period - 1 print(f"{f:7.2f} Hz correct delay {D:7.2f} implies DC_LEAD = {lead:.5f}")