"""Dependency-light audio placeholder for Day 1 pipeline proof.""" from __future__ import annotations import math import wave from pathlib import Path from .schema import Delivery, Script SAMPLE_RATE = 24_000 _DELIVERY_FREQ = { Delivery.NEUTRAL: 330, Delivery.SLOW: 262, Delivery.URGENT: 494, Delivery.WHISPER: 220, Delivery.BOOMING: 165, Delivery.DEADPAN: 294, Delivery.AGITATED: 440, } def render_placeholder_wav(script: Script, output_path: Path) -> Path: """Render a rough tone timeline so orchestration has a real file artifact.""" script.validate() output_path.parent.mkdir(parents=True, exist_ok=True) samples: list[int] = [] samples.extend(_tone(196, 0.5, 0.28)) samples.extend(_silence(0.15)) for scene in script.scenes: samples.extend(_noise_burst(0.18)) samples.extend(_silence(0.15)) for line in scene.lines: duration = min(max(len(line.text) / 34.0, 0.65), 2.4) samples.extend(_tone(_DELIVERY_FREQ[line.delivery], duration, 0.22)) samples.extend(_silence(0.18)) samples.extend(_silence(0.35)) samples.extend(_tone(147, 0.8, 0.24)) with wave.open(str(output_path), "wb") as wav: wav.setnchannels(1) wav.setsampwidth(2) wav.setframerate(SAMPLE_RATE) wav.writeframes(b"".join(sample.to_bytes(2, "little", signed=True) for sample in samples)) return output_path def _tone(freq: float, seconds: float, gain: float) -> list[int]: count = int(SAMPLE_RATE * seconds) attack = max(1, int(SAMPLE_RATE * 0.02)) release = max(1, int(SAMPLE_RATE * 0.03)) out = [] for i in range(count): envelope = 1.0 if i < attack: envelope = i / attack elif i > count - release: envelope = max(0.0, (count - i) / release) value = math.sin(2 * math.pi * freq * (i / SAMPLE_RATE)) out.append(int(32767 * gain * envelope * value)) return out def _silence(seconds: float) -> list[int]: return [0] * int(SAMPLE_RATE * seconds) def _noise_burst(seconds: float) -> list[int]: count = int(SAMPLE_RATE * seconds) out = [] seed = 17 for i in range(count): seed = (1103515245 * seed + 12345) & 0x7FFFFFFF envelope = max(0.0, 1.0 - i / count) value = ((seed / 0x7FFFFFFF) * 2.0) - 1.0 out.append(int(32767 * 0.08 * envelope * value)) return out