From 1512b164249b63bbe467671397a9fb162c814af6 Mon Sep 17 00:00:00 2001 From: Stanislav Marek Date: Mon, 15 Jun 2026 12:37:29 +0200 Subject: [PATCH] feat: add objective cue sound set --- pk3_build/sound/objectives/rustchain/LICENSE | 8 + .../sound/objectives/rustchain/README.md | 39 +++ .../rustchain/capture_initiated.ogg | Bin 0 -> 9961 bytes .../objectives/rustchain/node_contested.ogg | Bin 0 -> 14920 bytes .../objectives/rustchain/objective_lost.ogg | Bin 0 -> 14283 bytes .../rustchain/reward_route_open.ogg | Bin 0 -> 13600 bytes .../objectives/rustchain/validator_lock.ogg | Bin 0 -> 12114 bytes tests/test_objective_cue_sounds.py | 63 +++++ .../generate_rustchain_objective_cues.py | 241 ++++++++++++++++++ 9 files changed, 351 insertions(+) create mode 100644 pk3_build/sound/objectives/rustchain/LICENSE create mode 100644 pk3_build/sound/objectives/rustchain/README.md create mode 100644 pk3_build/sound/objectives/rustchain/capture_initiated.ogg create mode 100644 pk3_build/sound/objectives/rustchain/node_contested.ogg create mode 100644 pk3_build/sound/objectives/rustchain/objective_lost.ogg create mode 100644 pk3_build/sound/objectives/rustchain/reward_route_open.ogg create mode 100644 pk3_build/sound/objectives/rustchain/validator_lock.ogg create mode 100644 tests/test_objective_cue_sounds.py create mode 100644 tools/audio/generate_rustchain_objective_cues.py diff --git a/pk3_build/sound/objectives/rustchain/LICENSE b/pk3_build/sound/objectives/rustchain/LICENSE new file mode 100644 index 0000000..378b81c --- /dev/null +++ b/pk3_build/sound/objectives/rustchain/LICENSE @@ -0,0 +1,8 @@ +CC0 1.0 Universal + +The RustChain objective control cue sound design, generated audio files, and +deterministic synthesis source in `tools/audio/generate_rustchain_objective_cues.py` +are dedicated to the public domain under Creative Commons CC0 1.0 Universal. + +No external samples, recordings, speech models, or third-party audio assets are +used. diff --git a/pk3_build/sound/objectives/rustchain/README.md b/pk3_build/sound/objectives/rustchain/README.md new file mode 100644 index 0000000..a710247 --- /dev/null +++ b/pk3_build/sound/objectives/rustchain/README.md @@ -0,0 +1,39 @@ +# RustChain objective control cue set + +Five original short nonverbal cues for RustChain Arena objective, capture, and +reward-route moments: + +| File | Cue | +|---|---| +| `capture_initiated.ogg` | Objective capture started / node handshake | +| `node_contested.ogg` | Validator node contested / control conflict | +| `validator_lock.ogg` | Capture lock or validator control secured | +| `reward_route_open.ogg` | Reward route, payout lane, or bonus gate opened | +| `objective_lost.ogg` | Objective lost / control reverted | + +All files are mono OGG Vorbis at 48 kHz and are generated from deterministic +procedural synthesis. No external samples, speech models, or recordings are +used. + +Suggested virtual paths are relative to `sound/`, for example: + +```c +precache_sound("objectives/rustchain/validator_lock.ogg"); +sound(world, CHAN_AUTO, "objectives/rustchain/validator_lock.ogg", VOL_BASE, ATTEN_NORM); +``` + +## Regeneration + +From the repository root: + +```sh +python3 tools/audio/generate_rustchain_objective_cues.py +``` + +The generator uses Python's standard library for synthesis and ffmpeg only for +OGG Vorbis encoding. + +## License + +The sound design, PCM synthesis code, and generated audio are dedicated to the +public domain under CC0 1.0. See `LICENSE`. diff --git a/pk3_build/sound/objectives/rustchain/capture_initiated.ogg b/pk3_build/sound/objectives/rustchain/capture_initiated.ogg new file mode 100644 index 0000000000000000000000000000000000000000..a34c042ca15f5ad0595c48b822deb49c9a7f4505 GIT binary patch literal 9961 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 s1&hj{XUH-3`*6N=l>B0us_KrGhBkaM#A~ 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 literal 0 HcmV?d00001 diff --git a/tests/test_objective_cue_sounds.py b/tests/test_objective_cue_sounds.py new file mode 100644 index 0000000..5352601 --- /dev/null +++ b/tests/test_objective_cue_sounds.py @@ -0,0 +1,63 @@ +import importlib.util +import math +import tempfile +import unittest +import wave +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +MODULE_PATH = ROOT / "tools" / "audio" / "generate_rustchain_objective_cues.py" +OBJECTIVE_DIR = ROOT / "pk3_build" / "sound" / "objectives" / "rustchain" +SPEC = importlib.util.spec_from_file_location("rustchain_objective_generator", MODULE_PATH) +MODULE = importlib.util.module_from_spec(SPEC) +assert SPEC.loader is not None +SPEC.loader.exec_module(MODULE) + + +class ObjectiveCuePackageTests(unittest.TestCase): + def test_required_objective_cues_are_present(self): + self.assertEqual( + set(MODULE.OBJECTIVE_CUES), + { + "capture_initiated", + "node_contested", + "validator_lock", + "reward_route_open", + "objective_lost", + }, + ) + + def test_generated_samples_are_finite_audible_and_short(self): + for name, generator in MODULE.OBJECTIVE_CUES.items(): + samples = generator() + self.assertGreater(len(samples), MODULE.SAMPLE_RATE // 2, name) + self.assertLess(len(samples), MODULE.SAMPLE_RATE * 2, name) + self.assertTrue(all(math.isfinite(sample) for sample in samples), name) + self.assertGreater(max(abs(sample) for sample in samples), 0.05, name) + + def test_wav_output_is_mono_48khz_pcm(self): + with tempfile.TemporaryDirectory() as temp_dir: + output = Path(temp_dir) / "objective.wav" + MODULE.write_wav(output, MODULE.validator_lock()) + with wave.open(str(output), "rb") as audio: + self.assertEqual(audio.getnchannels(), 1) + self.assertEqual(audio.getframerate(), 48_000) + self.assertEqual(audio.getsampwidth(), 2) + self.assertGreater(audio.getnframes(), 0) + + def test_metadata_files_are_packaged(self): + readme = (OBJECTIVE_DIR / "README.md").read_text() + self.assertIn("objective control cue", readme) + self.assertIn("CC0 1.0", (OBJECTIVE_DIR / "LICENSE").read_text()) + + def test_packaged_files_are_ogg_vorbis(self): + for name in MODULE.OBJECTIVE_CUES: + payload = (OBJECTIVE_DIR / f"{name}.ogg").read_bytes() + self.assertTrue(payload.startswith(b"OggS"), name) + self.assertIn(b"\x01vorbis", payload[:256], name) + self.assertGreater(len(payload), 1_000, name) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/audio/generate_rustchain_objective_cues.py b/tools/audio/generate_rustchain_objective_cues.py new file mode 100644 index 0000000..69da8df --- /dev/null +++ b/tools/audio/generate_rustchain_objective_cues.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +"""Generate original RustChain Arena objective/control cue sounds. + +The cues are deterministic procedural synthesis: no samples, speech models, +recordings, or third-party audio are used. ffmpeg is used only for OGG Vorbis +encoding. +""" + +from __future__ import annotations + +import argparse +import math +import shutil +import struct +import subprocess +import tempfile +import wave +from pathlib import Path +from typing import Callable + + +SAMPLE_RATE = 48_000 +TARGET_PEAK = 10 ** (-1.2 / 20.0) + + +class Noise: + def __init__(self, seed: int) -> None: + self.state = seed & 0xFFFFFFFF + + def next(self) -> float: + self.state = (1664525 * self.state + 1013904223) & 0xFFFFFFFF + return ((self.state / 0xFFFFFFFF) * 2.0) - 1.0 + + +def blank(seconds: float) -> list[float]: + return [0.0] * round(seconds * SAMPLE_RATE) + + +def add_tone( + samples: list[float], + start: float, + duration: float, + start_hz: float, + end_hz: float, + gain: float, + *, + attack: float = 0.006, + release_power: float = 2.0, + harmonics: tuple[float, ...] = (1.0, 0.25, 0.08), +) -> None: + start_index = round(start * SAMPLE_RATE) + count = min(round(duration * SAMPLE_RATE), len(samples) - start_index) + phases = [0.0] * len(harmonics) + for offset in range(max(0, count)): + progress = offset / max(1, count - 1) + frequency = start_hz + (end_hz - start_hz) * progress + envelope = min(1.0, offset / max(1, round(attack * SAMPLE_RATE))) + envelope *= (1.0 - progress) ** release_power + value = 0.0 + for harmonic_index, harmonic_gain in enumerate(harmonics, start=1): + phases[harmonic_index - 1] += ( + 2.0 * math.pi * frequency * harmonic_index / SAMPLE_RATE + ) + value += harmonic_gain * math.sin(phases[harmonic_index - 1]) + samples[start_index + offset] += gain * envelope * value + + +def add_noise( + samples: list[float], + start: float, + duration: float, + gain: float, + seed: int, + *, + smoothing: float = 0.68, + release_power: float = 2.8, +) -> None: + start_index = round(start * SAMPLE_RATE) + count = min(round(duration * SAMPLE_RATE), len(samples) - start_index) + noise = Noise(seed) + filtered = 0.0 + for offset in range(max(0, count)): + progress = offset / max(1, count - 1) + filtered = smoothing * filtered + (1.0 - smoothing) * noise.next() + envelope = min(1.0, offset / max(1, round(0.002 * SAMPLE_RATE))) + envelope *= (1.0 - progress) ** release_power + samples[start_index + offset] += gain * envelope * filtered + + +def add_tick(samples: list[float], start: float, pitch: float, seed: int, gain: float = 0.34) -> None: + add_noise(samples, start, 0.045, gain * 0.45, seed, smoothing=0.34, release_power=4.0) + add_tone( + samples, + start, + 0.085, + pitch, + pitch * 0.70, + gain, + attack=0.001, + release_power=4.2, + harmonics=(1.0, 0.34, 0.12), + ) + + +def add_chord(samples: list[float], start: float, duration: float, pitches: tuple[float, ...], gain: float) -> None: + per_tone = gain / math.sqrt(len(pitches)) + for pitch in pitches: + add_tone(samples, start, duration, pitch, pitch * 1.005, per_tone, release_power=1.6) + + +def capture_initiated() -> list[float]: + samples = blank(1.12) + for index, pitch in enumerate((392.0, 493.88, 659.25)): + add_tick(samples, 0.06 + index * 0.13, pitch, 0xC410 + index, 0.34) + add_tone(samples, 0.40, 0.48, 196.0, 329.63, 0.28, release_power=1.4) + add_chord(samples, 0.62, 0.34, (261.63, 329.63, 392.0), 0.46) + return samples + + +def node_contested() -> list[float]: + samples = blank(1.34) + for index, start in enumerate((0.05, 0.25, 0.45, 0.65)): + add_noise(samples, start, 0.12, 0.42, 0xC0A0 + index, smoothing=0.28) + add_tone(samples, start, 0.18, 880 - index * 80, 140, 0.36, attack=0.001, release_power=2.5) + add_tone(samples, 0.86, 0.30, 110.0, 82.41, 0.42, release_power=2.2) + return samples + + +def validator_lock() -> list[float]: + samples = blank(1.26) + for index, pitch in enumerate((220.0, 277.18, 329.63, 440.0)): + add_tick(samples, 0.08 + index * 0.12, pitch, 0x10CC + index, 0.30) + add_chord(samples, 0.58, 0.52, (220.0, 329.63, 440.0, 554.37), 0.62) + add_noise(samples, 0.58, 0.10, 0.18, 0x10CF, smoothing=0.45) + return samples + + +def reward_route_open() -> list[float]: + samples = blank(1.46) + for index in range(6): + add_tick(samples, 0.05 + index * 0.085, 520 + index * 70, 0x7EED + index, 0.24) + add_tone(samples, 0.52, 0.70, 174.61, 698.46, 0.34, release_power=1.2) + add_chord(samples, 0.84, 0.44, (261.63, 392.0, 523.25, 659.25), 0.56) + return samples + + +def objective_lost() -> list[float]: + samples = blank(1.38) + add_noise(samples, 0.04, 0.16, 0.48, 0x1057, smoothing=0.32) + for index, start in enumerate((0.14, 0.36, 0.58)): + add_tone(samples, start, 0.22, 620 - index * 120, 150 - index * 22, 0.42, attack=0.002, release_power=2.6) + add_noise(samples, start, 0.08, 0.26, 0x1060 + index, smoothing=0.42) + add_chord(samples, 0.82, 0.40, (98.0, 146.83, 196.0), 0.44) + return samples + + +OBJECTIVE_CUES: dict[str, Callable[[], list[float]]] = { + "capture_initiated": capture_initiated, + "node_contested": node_contested, + "validator_lock": validator_lock, + "reward_route_open": reward_route_open, + "objective_lost": objective_lost, +} + + +def normalize(samples: list[float]) -> list[float]: + peak = max((abs(sample) for sample in samples), default=0.0) + scale = TARGET_PEAK / peak if peak else 1.0 + normalized = [max(-1.0, min(1.0, sample * scale)) for sample in samples] + fade_samples = min(round(0.008 * SAMPLE_RATE), len(normalized) // 2) + for index in range(fade_samples): + fade = index / max(1, fade_samples) + normalized[index] *= fade + normalized[-index - 1] *= fade + return normalized + + +def write_wav(path: Path, samples: list[float]) -> None: + pcm = bytearray() + for sample in normalize(samples): + pcm.extend(struct.pack(" None: + subprocess.run( + [ + ffmpeg, + "-hide_banner", + "-loglevel", + "error", + "-y", + "-i", + str(wav_path), + "-map_metadata", + "-1", + "-ac", + "1", + "-ar", + str(SAMPLE_RATE), + "-c:a", + "libvorbis", + "-q:a", + "6", + str(ogg_path), + ], + check=True, + ) + + +def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument( + "--output", + type=Path, + default=Path("pk3_build/sound/objectives/rustchain"), + help="Destination directory for generated OGG files.", + ) + parser.add_argument("--ffmpeg", default=shutil.which("ffmpeg")) + args = parser.parse_args() + + if not args.ffmpeg: + raise SystemExit("ffmpeg was not found on PATH") + + args.output.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(prefix="rustchain-objectives-") as temp_dir: + temp_path = Path(temp_dir) + for name, generator in OBJECTIVE_CUES.items(): + wav_path = temp_path / f"{name}.wav" + ogg_path = args.output / f"{name}.ogg" + write_wav(wav_path, generator()) + encode_ogg(wav_path, ogg_path, args.ffmpeg) + print(f"generated {ogg_path}") + + +if __name__ == "__main__": + main()