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