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
|