130 lines
3.1 KiB
Python
130 lines
3.1 KiB
Python
# 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
|