make-doorbell.py
Files / atom-echo / make-doorbell.py
Files in atom-echo/
Directoryatom-echo/
- atom-echo.yaml 4.9 KB
- flash.sh 5.4 KB
- LICENSE 1.0 KB
- make-doorbell.py 1.5 KB
- secrets.yaml.example 253 bytes
From M5Stack Atom Echo on your own ESPHome build: push to talk, a doorbell, no internet. MIT licence: LICENSE.
#!/usr/bin/env python3"""Make doorbell.wav: a two-note "ding-dong" chime, synthesised (no licence issues).
Each note is a few decaying sine partials, like a struck metal bar.Mono, 22.05 kHz, 16-bit, peak -3 dBFS (the Atom Echo's small speaker rattles near 0 dBFS).
Usage: python3 make-doorbell.py [out.wav]"""import mathimport structimport sysimport wave
RATE = 22050LENGTH = 3.2 # secondsNOTES = [(659.25, 0.0), (523.25, 0.75)] # E5 "ding", then C5 "dong" 0.75 s later# (multiple of the note, loudness, decay per second)PARTIALS = [(1.0, 1.0, 2.5), (2.0, 0.6, 4.0), (3.0, 0.3, 6.0), (4.07, 0.2, 9.0)]ATTACK = 0.004 # seconds; avoids a click at the start of each noteFADE = 0.3 # seconds of fade-out at the endPEAK = 10 ** (-3 / 20)
def note(freq, t): if t < 0: return 0.0 s = sum(a * math.sin(2 * math.pi * freq * m * t) * math.exp(-d * t) for m, a, d in PARTIALS) return s * min(1.0, t / ATTACK)
def main(): out = sys.argv[1] if len(sys.argv) > 1 else "doorbell.wav" n = int(RATE * LENGTH) samples = [] for i in range(n): t = i / RATE v = sum(note(f, t - start) for f, start in NOTES) v *= min(1.0, (LENGTH - t) / FADE) samples.append(v) scale = PEAK / max(abs(v) for v in samples) with wave.open(out, "wb") as w: w.setnchannels(1) w.setsampwidth(2) w.setframerate(RATE) w.writeframes(b"".join(struct.pack("<h", round(v * scale * 32767)) for v in samples)) print(f"🟢 wrote {out} ({LENGTH} s, {RATE} Hz)")
if __name__ == "__main__": main()Text, photos and drawings: CC BY 4.0. Code: MIT. Share and adapt them, crediting HomeLab Guides (blog.jacq.cc). Licence and privacy