"""Fur texture and two small "bork"s for the pup plushie (for a friend). Run with local/hear-venv python. fur.png: tileable soft short fur, neutral light grey so prim colour tints it, 256 px. bork1.wav / bork2.wav: a small dog's bark, rounded towards "bork": rough glottal pulses ~520-600 Hz that jump up and fall, /o/-ish formants, a lip-closed "b" start, breath noise, ~0.2 s. 44.1 kHz mono. Claude Opus 5.5 (Tessel), 2026-10-10.""" import wave from pathlib import Path import numpy as np from PIL import Image, ImageFilter HERE = Path(__file__).parent rng = np.random.default_rng(11) # ---- fur: many short soft strokes, mostly one direction, wrapped so it tiles ---- S = 256 img = np.full((S, S), 0.55, np.float32) for _ in range(5000): x, y = rng.random() * S, rng.random() * S ang = np.pi / 2 + rng.normal(0, 0.35) L = 4 + rng.random() * 7 v = rng.random() for k in range(int(L * 2)): px = int(x + np.cos(ang) * k * 0.5) % S py = int(y + np.sin(ang) * k * 0.5) % S img[py, px] = img[py, px] * 0.4 + (0.35 + 0.65 * v) * 0.6 img = np.asarray(Image.fromarray((img * 255).astype(np.uint8)).filter(ImageFilter.GaussianBlur(0.7)), np.float32) / 255 img = 0.82 + 0.18 * (img - img.mean()) / (img.std() * 2.5) # gentle: mostly the prim colour Image.fromarray((np.clip(img, 0, 1) * 255).astype(np.uint8)).convert("RGB").save(HERE / "fur.png") print(HERE / "fur.png") # ---- bork ---- SR = 44100 def formant(x, f, bw): r = np.exp(-np.pi * bw / SR) a1, a2 = -2 * r * np.cos(2 * np.pi * f / SR), r * r y = np.zeros_like(x) for i in range(len(x)): y[i] = x[i] - a1 * y[i - 1] - a2 * y[i - 2] return y def bork(f_peak, D, seed): r = np.random.default_rng(seed) t = np.arange(int(SR * D)) / SR u = t / D # pitch: jumps up fast, then falls: the bark's arc f0 = f_peak * (0.75 + 0.25 * np.clip(u / 0.15, 0, 1)) * (1 - 0.35 * np.clip((u - 0.2) / 0.8, 0, 1)) f0 *= 1 + 0.04 * r.standard_normal(len(t)).cumsum() / np.sqrt(np.arange(1, len(t) + 1)) # jitter: rough voice ph = np.cumsum(f0) / SR src = (ph % 1.0) ** 4 - 0.2 src -= src.mean() noise = r.standard_normal(len(t)) * 0.15 # breathy, a bit growly src = src + noise * (0.4 + 0.6 * u) open_ = np.clip((t - 0.012) / 0.03, 0, 1) # "b": lips open # "o" moving towards "r": formants slide a little f1 = 560 - 80 * u f2 = 1000 + 200 * u vowel = formant(src, float(f1.mean()), 120) + 0.7 * formant(src, float(f2.mean()), 160) \ + 0.3 * formant(src, 2500, 300) closed = formant(src, 300, 90) sig = closed * (1 - open_) * 0.5 + vowel * open_ env = np.clip(t / 0.006, 0, 1) * np.exp(-np.clip(u - 0.25, 0, 1) * 5) * np.clip((D - t) / 0.03, 0, 1) sig *= env # "k": a tiny click of noise at the end k0 = int(SR * (D - 0.035)) click = formant(r.standard_normal(600), 2200, 900) sig[k0:k0 + 600] += click / np.abs(click).max() * np.hanning(600) * 0.12 * np.abs(sig).max() return sig / np.abs(sig).max() for name, fp, D, seed in (("bork1", 560, 0.2, 1), ("bork2", 620, 0.17, 2)): sig = bork(fp, D, seed) pad = np.zeros(int(SR * 0.02)) sig = np.concatenate([sig, pad]) with wave.open(str(HERE / f"{name}.wav"), "wb") as w: w.setnchannels(1); w.setsampwidth(2); w.setframerate(SR) w.writeframes((sig * 0.7 * 32767).astype("