midnight-static / src /midnight_static /audio_placeholder.py
cajpany's picture
Upload folder using huggingface_hub
fc9b7e2 verified
Raw
History Blame Contribute Delete
2.45 kB
"""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