rpi-pico-candle/music.py

130 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SPDX-License-Identifier: MIT
#
# Little christmas candle
#
# Author: Nils Freydank <nils.freydank@datenschutz-ist-voll-doof.de>
# Year: 2022
# Copyright: MIT
import json
import machine
import time
pin_piezo = machine.Pin(12)
def _note_to_freq(note_name: str) -> int:
"""
Translate note names from strings into frequency values.
"""
freq: int = 0
# frequencies, see e.g.
# https://en.wikipedia.org/wiki/Piano_key_frequencies
note_names: dict = {
"c4": 262,
"cis4": 277,
"d4": 294,
"e4": 330,
"f4": 349,
"fis4": 370,
"g4": 392,
"a4": 440,
"b4": 466,
"h4": 494,
"c5": 523,
"cis5": 554,
"d5": 587,
"e5": 659,
"f5": 698,
"fis5": 740,
"g5": 784,
"a5": 880,
"b5": 923,
"h5": 988,
"c6": 1047,
}
try:
freq = note_names[note_name]
except KeyError:
freq = -1
print(f"exc in _note_to_freq() for {note_name}.")
return freq
def _len_to_second(len_name: str, len_scaling: float = 0.6) -> float:
time: float = 0.0
len_names = {
"_t": 0.125 * len_scaling, # thirty-second note or pause
"_s": 0.25 * len_scaling, # sixteenth note or pause
"_e": 0.5 * len_scaling, # eight note or pause
"_q": 1.0 * len_scaling, # quarter note or pause
"_h": 2.0 * len_scaling, # half note or pause
"_f": 4.0 * len_scaling, # whole note or pause
}
try:
for elem in len_name.split("+"):
time += len_names[elem.replace(" ", "")]
except KeyError:
time = 0.0
print(f"exc in _len_to_second() for {elem}.")
print("Do you have a typo there, e.g. 'a' + 'b' instead of 'a + b'?")
return time
def play_note(note_name: str, duration: str, loud: bool = False) -> int:
if loud:
duty_cycle: int = 16_000
else:
duty_cycle: int = 400
real_pwm_freq: int = 0
pwm_piezo = machine.PWM(pin_piezo)
pwm_piezo.freq(_note_to_freq(note_name))
real_pwm_freq = pwm_piezo.freq()
pwm_piezo.duty_u16(duty_cycle)
time.sleep(_len_to_second(duration))
pwm_piezo.duty_u16(0)
time.sleep(0.05)
return real_pwm_freq
def pause(duration: str) -> None:
time.sleep(_len_to_second(duration))
time.sleep(0.05)
def play_music(file_name: str = "o-tannenbaum.json") -> None:
data: dict = {}
try:
with open(file_name, "r") as f:
data = json.load(f)
try:
if data["metadata"]["format"] == "json-0.1.0":
for notes in data["music"]:
for note in notes.keys():
if note == "0":
pause(notes[note])
else:
play_note(note, notes[note])
elif data["metadata"]["format"] == "json-0.2.0":
pass
except:
print(f"play_oh_tannnebaum() failed to parse {data}.")
except FileNotFoundError:
print("Could not load the JSON file containing the song's details.")
# vim:fileencoding=utf-8:ts=4:syntax=python:expandtab