#!/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("