from machine import Pin, PWM
from utime import sleep
class BuzzerPlayer:
"""Clase para controlar la reproducción de melodías en un buzzer pasivo."""
def __init__(self, pin_num, default_bpm=120):
self.buzzer = PWM(Pin(pin_num))
self.buzzer.duty_u16(0) # Iniciar en silencio
self.bpm = default_bpm
# Diccionario de frecuencias
self.tones = {
'c4': 262, 'cs4': 277, 'd4': 294, 'ds4': 311, 'e4': 330, 'f4': 349,
'fs4': 370, 'g4': 392, 'gs4': 415, 'a4': 440, 'as4': 466, 'b4': 494,
'c5': 523, 'cs5': 554, 'd5': 587, 'ds5': 622, 'e5': 659, 'f5': 698,
'rest': 0
}
def set_bpm(self, new_bpm):
"""Actualiza la velocidad de la canción en tiempo real."""
self.bpm = new_bpm
print(f"[*] Velocidad cambiada a {self.bpm} BPM")
def _get_beat_duration(self):
"""Calcula dinámicamente la duración del beat actual."""
return 60.0 / self.bpm
def play_tone(self, frequency, duration_beats):
"""Reproduce un tono individual basado en el BPM actual."""
if frequency > 0:
self.buzzer.freq(frequency)
self.buzzer.duty_u16(32768) # 50% Volumen
else:
self.buzzer.duty_u16(0) # Silencio
# El tiempo de espera se calcula al momento, respetando el BPM actual
sleep(duration_beats * self._get_beat_duration())
# Pausa staccato entre notas
self.buzzer.duty_u16(0)
sleep(0.02)
def play_melody(self, melody_sequence):
"""
Recorre la lista. Si encuentra una nota, la toca.
Si encuentra el comando 'bpm', actualiza la velocidad.
"""
print(f"Reproduciendo melodía iniciando a {self.bpm} BPM...")
try:
for item in melody_sequence:
command = item[0].lower()
value = item[1]
if command == 'bpm':
# Si el elemento es un cambio de velocidad, actualizamos
self.set_bpm(value)
elif command in self.tones:
# Si es una nota, la reproducimos
self.play_tone(self.tones[command], value)
else:
print(f"Error: Nota o comando '{command}' no reconocido.")
except KeyboardInterrupt:
print("\nDetenido por el usuario.")
finally:
self.stop()
def stop(self):
"""Apaga y libera el hardware del buzzer de forma segura."""
self.buzzer.duty_u16(0)
self.buzzer.deinit()
print("Buzzer apagado.")
# --- EJECUCIÓN PRINCIPAL ---
# Definimos la melodía integrando comandos 'bpm' donde queramos alterar el ritmo
# Formato: ('nota', duracion) o ('bpm', nueva_velocidad)
'''
self.tones = {
'c4': 262, 'cs4': 277, 'd4': 294, 'ds4': 311, 'e4': 330, 'f4': 349,
'fs4': 370, 'g4': 392, 'gs4': 415, 'a4': 440, 'as4': 466, 'b4': 494,
'c5': 523, 'cs5': 554, 'd5': 587, 'ds5': 622, 'e5': 659, 'f5': 698,
'rest': 0
}
'''
melodia_dinamica = [
('a4', 1), ('e5', 1), ('f5', 1), ('c5', 2),
('g4', 1), ('d5', 1), ('e5', 1), ('b4', 2),
('f4', 1), ('c5', 1), ('d5', 1), ('a4', 2),
('e4', 1), ('b4', 1), ('c5', 1), ('g4', 2),
('bpm', 90),
('a4', 0.5), ('c5', 0.5), ('e5', 0.5), ('a5', 1),
('g4', 0.5), ('b4', 0.5), ('d5', 0.5), ('g5', 1),
('f4', 0.5), ('a4', 0.5), ('c5', 0.5), ('f5', 1),
('e4', 0.5), ('g4', 0.5), ('b4', 0.5), ('e5', 1),
('bpm', 120),
('a5', 0.25), ('e5', 0.25), ('c5', 0.25), ('a4', 0.25), ('e5', 1),
('g5', 0.25), ('d5', 0.25), ('b4', 0.25), ('g4', 0.25), ('d5', 1),
('f5', 0.25), ('c5', 0.25), ('a4', 0.25), ('f4', 0.25), ('c5', 1),
('e5', 0.25), ('b4', 0.25), ('g4', 0.25), ('e4', 0.25), ('b4', 1),
('bpm', 60),
('c5', 2), ('b4', 2), ('a4', 4)
]
# 1. Instanciamos nuestro reproductor en el Pin 5 con 120 BPM iniciales
mi_reproductor = BuzzerPlayer(pin_num=5, default_bpm=70)
# 2. Reproducimos la lista
mi_reproductor.play_melody(melodia_dinamica)