1#!/usr/bin/env python3 2"""Write Whiskers' sound effects as small WAV files. They are synthesised here, not 3taken from anywhere, so they carry no licence. Run again after changing the recipes. 4 5 tools/make-sounds.py android/app/src/main/res/raw 6""" 7import math 8import struct 9import sys 10import wave 11from pathlib import Path 12 13RATE = 22050 14 15 16def write(path, samples): 17 with wave.open(str(path), "wb") as w: 18 w.setnchannels(1) 19 w.setsampwidth(2) 20 w.setframerate(RATE) 21 w.writeframes(b"".join(struct.pack("<h", int(max(-1, min(1, s)) * 32000)) for s in samples)) 22 23 24def tone(freq_of_t, seconds, volume=0.5): 25 n = int(RATE * seconds) 26 out, phase = [], 0.0 27 for i in range(n): 28 t = i / RATE 29 phase += 2 * math.pi * freq_of_t(t) / RATE 30 # Short fade in and out so it does not click. 31 env = min(1.0, i / (0.01 * RATE), (n - i) / (0.03 * RATE)) 32 out.append(math.sin(phase) * volume * env) 33 return out 34 35 36def silence(seconds): 37 return [0.0] * int(RATE * seconds) 38 39 40def giggle(): 41 """Three quick rising chirps: a tickle.""" 42 out = [] 43 for base in (520, 600, 700): 44 out += tone(lambda t, b=base: b + 900 * t, 0.11) 45 out += silence(0.04) 46 return out 47 48 49def dizzy(): 50 """A slow warble sliding down, like a cartoon head-spin.""" 51 return tone(lambda t: 600 - 250 * t + 80 * math.sin(2 * math.pi * 7 * t), 0.9, 0.45) 52 53 54def main(out): 55 out = Path(out) 56 out.mkdir(parents=True, exist_ok=True) 57 write(out / "giggle.wav", giggle()) 58 write(out / "dizzy.wav", dizzy()) 59 60 61if __name__ == "__main__": 62 main(sys.argv[1])