8 changed files with 98 additions and 96 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3 |
||||
from __future__ import annotations |
||||
|
||||
import math |
||||
import wave |
||||
from pathlib import Path |
||||
|
||||
SAMPLE_RATE = 44100 |
||||
|
||||
PROFILES = { |
||||
"confirm": { |
||||
"amplitude": 0.28, |
||||
"segments": [ |
||||
(80, [660, 990]), |
||||
(35, []), |
||||
(95, [880, 1320]), |
||||
(35, []), |
||||
(130, [1174, 1568]), |
||||
], |
||||
}, |
||||
"block": { |
||||
"amplitude": 0.42, |
||||
"segments": [ |
||||
(120, [392, 587]), |
||||
(36, []), |
||||
(130, [294, 440]), |
||||
(36, []), |
||||
(190, [196, 294]), |
||||
], |
||||
}, |
||||
"refine": { |
||||
"amplitude": 0.30, |
||||
"segments": [ |
||||
(95, [587, 784]), |
||||
(32, []), |
||||
(95, [659, 880]), |
||||
(32, []), |
||||
(95, [587, 784]), |
||||
], |
||||
}, |
||||
} |
||||
|
||||
|
||||
def synth_segment(duration_ms: int, freqs: list[int], amplitude: float) -> list[int]: |
||||
samples = round((duration_ms / 1000) * SAMPLE_RATE) |
||||
data: list[int] = [] |
||||
for i in range(samples): |
||||
sample = 0.0 |
||||
if freqs: |
||||
t = i / SAMPLE_RATE |
||||
attack = min(1.0, i / max(1, int(samples * 0.1))) |
||||
release = min(1.0, (samples - i) / max(1, int(samples * 0.12))) |
||||
envelope = min(attack, release) |
||||
sample = sum(math.sin(2 * math.pi * freq * t) for freq in freqs) / len(freqs) |
||||
sample *= amplitude * envelope |
||||
data.append(int(max(-1.0, min(1.0, sample)) * 32767)) |
||||
return data |
||||
|
||||
|
||||
def write_wave(path: Path, profile: dict[str, object]) -> None: |
||||
amplitude = float(profile["amplitude"]) |
||||
segments = profile["segments"] |
||||
pcm: list[int] = [] |
||||
for duration_ms, freqs in segments: # type: ignore[misc] |
||||
pcm.extend(synth_segment(duration_ms, list(freqs), amplitude)) |
||||
|
||||
path.parent.mkdir(parents=True, exist_ok=True) |
||||
with wave.open(str(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 pcm)) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
root = Path(__file__).resolve().parent.parent |
||||
assets = root / "assets" |
||||
for name, profile in PROFILES.items(): |
||||
write_wave(assets / f"{name}.wav", profile) |
||||
print(f"wrote {assets / f'{name}.wav'}") |
||||
Loading…
Reference in new issue