#!/usr/bin/env python3 """Generate PeerSpeak's default notification chimes as small mono 16-bit WAVs. Stdlib only (wave/struct/math) so there's no build-time dependency. Re-run to regenerate the committed assets: python3 assets/sounds/generate_chimes.py Each chime is a short sequence of sine notes with a soft attack (no click) and an exponential decay (a gentle bell-like tail), plus a quiet second harmonic so it reads as a "chime" rather than a flat beep. Distinct melodic shapes make the events recognizable without looking: ascending = arrival, descending = leaving. """ import math import os import struct import wave RATE = 48000 # match PeerSpeak's audio graph AMPLITUDE = 0.38 # clearly audible, not harsh (fraction of full scale) ATTACK_S = 0.006 # short fade-in to kill the start click DECAY = 7.0 # exponential decay rate (higher = shorter tail) # Note frequencies (Hz) G4, C5, E5, G5 = 392.00, 523.25, 659.25, 783.99 def note(freq, dur): """One enveloped note: fundamental + soft 2nd harmonic, attack + decay.""" n = int(RATE * dur) out = [] for i in range(n): t = i / RATE env = math.exp(-DECAY * t) if t < ATTACK_S: env *= t / ATTACK_S sample = math.sin(2 * math.pi * freq * t) + 0.3 * math.sin(2 * math.pi * 2 * freq * t) out.append(AMPLITUDE * env * sample / 1.3) return out def chime(notes): """Concatenate (freq, dur) notes into one sample buffer.""" buf = [] for freq, dur in notes: buf.extend(note(freq, dur)) return buf def write_wav(path, samples): with wave.open(path, "w") as w: w.setnchannels(1) w.setsampwidth(2) w.setframerate(RATE) frames = b"".join( struct.pack(" G5). "reconnected.wav": [(C5, 0.10), (G5, 0.25)], # Left a room: descending triad. "self-leave.wav": [(G5, 0.10), (E5, 0.10), (C5, 0.25)], # Mute/unmute toggle: tiny clean blip. "mic-toggle.wav": [(E5, 0.08)], # Reconnect gave up: disappointing low two-note fall. "reconnect-failed.wav": [(C5, 0.15), (349.23, 0.30)], } def main(): here = os.path.dirname(os.path.abspath(__file__)) for name, notes in CHIMES.items(): write_wav(os.path.join(here, name), chime(notes)) if __name__ == "__main__": main()