5.2.4 Reproductor de Música

5.2.4.1 Resumen

Img

Aquí construimos un reproductor de música que genera sonido a través del zumbador incorporado en la placa micro:bit (no reproduce música vocal). Cuenta con una biblioteca de 20 pistas cortas y admite la reproducción secuencial y aleatoria.

En el modo secuencial, al presionar el botón C (Canción anterior) o E (Canción siguiente) se cambian las pistas según una secuencia preestablecida hasta llegar al final de la lista; mientras que en el modo aleatorio, cada pulsación selecciona una pista al azar entre los 20 sonidos con las luces de colores parpadeando, y cuando una canción termina, se detiene inmediatamente.

Mientras tanto, la matriz de LED de la micro:bit muestra el modo de reproducción actual en tiempo real.

Img

5.2.4.2 Piezas Requeridas

Img

Img

Img

Placa micro:bit V2 (suministrada por el usuario) ×1

Smart Gamepad micro:bit (ensamblado) ×1

Pila AAA (suministrada por el usuario) ×4

5.2.4.3 Flujo del Código

Img

5.2.4.4 Código de Prueba

Código completo:

from microbit import *
import music
import neopixel
import random
import utime

# ===================== Global Configuration & Variables =====================
# Disable micro:bit's default LED display to avoid conflicts
display.set_display_mode(display.DISPLAY_MODE_BLACK_AND_WHITE)

# LED strip configuration
NUM_LEDS = 4
LED_PIN = pin8
strip = neopixel.NeoPixel(LED_PIN, NUM_LEDS)

# Button debounce time (ms)
BTN_DEBOUNCE_TIME = 20

# Music volume (0-255)
music_volume = 128

# Music list (20 short tracks)
# Example: music.DADADADUM, music.ENTERTAINER, music.PRELUDE, etc.
# For brevity, using placeholders. In a real scenario, these would be actual music sequences.
music_list = [
    music.DADADADUM, music.ENTERTAINER, music.PRELUDE, music.ODEON,
    music.NYAN, music.RINGTONE, music.FUNK, music.BLUES,
    music.BIRTHDAY, music.WEDDING, music.FUNERAL, music.PUNCHLINE,
    music.PYTHON, music.BADDY, music.CHASE, music.WAWAWAWAA,
    music.JUMP_UP, music.JUMP_DOWN, music.POWER_UP, music.POWER_DOWN
]

# Game state variables
play_mode = 0  # 0: sequential, 1: random
current_track_index = 0
last_button_press_time = 0
last_music_play_time = 0

# Initialize pins with pull-up (PULL_UP: pressed=low level 0, released=high level 1)
pin13.set_pull(pin13.PULL_UP)  # Button C
pin15.set_pull(pin15.PULL_UP)  # Button D
pin16.set_pull(pin16.PULL_UP)  # Button E
pin14.set_pull(pin14.PULL_UP)  # Button F
pin_a = pin5 # Button A
pin_b = pin11 # Button B
pin_a.set_pull(pin_a.PULL_UP)
pin_b.set_pull(pin_b.PULL_UP)

# Function to set all LEDs to a specific color
def set_all_leds(r, g, b):
    for i in range(NUM_LEDS):
        strip[i] = (r, g, b)
    strip.show()

# Function to clear all LEDs
def clear_leds():
    set_all_leds(0, 0, 0)

# Function to convert HSL to RGB for breathing light effect
def hsl_to_rgb(h, s, l):
    h /= 360.0
    s /= 100.0
    l /= 100.0
    if s == 0:
        r = g = b = l
    else:
        def hue2rgb(p, q, t):
            if t < 0: t += 1
            if t > 1: t -= 1
            if t < 1/6: return p + (q - p) * 6 * t
            if t < 1/2: return q
            if t < 2/3: return p + (q - p) * (2/3 - t) * 6
            return p
        q = l * (1 + s) if l < 0.5 else l + s - l * s
        p = 2 * l - q
        r = hue2rgb(p, q, h + 1/3)
        g = hue2rgb(p, q, h)
        b = hue2rgb(p, q, h - 1/3)
    return (int(r * 255), int(g * 255), int(b * 255))

# ===================== Main Loop =====================
while True:
    current_time = utime.ticks_ms()

    # Change play mode (D for sequential, F for random)
    if not pin15.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button D
        play_mode = 0
        display.show(Image.ASLEEP)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)
    elif not pin14.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button F
        play_mode = 1
        display.show(Image.HAPPY)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)

    # Play music based on mode
    if play_mode == 0: # Sequential
        if not music.is_playing():
            music.play(music_list[current_track_index], wait=False, loop=False)
            last_music_play_time = current_time

        if not pin13.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button C (Previous)
            music.stop()
            current_track_index = (current_track_index - 1 + len(music_list)) % len(music_list)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)
        elif not pin16.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button E (Next)
            music.stop()
            current_track_index = (current_track_index + 1) % len(music_list)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)

    elif play_mode == 1: # Random
        if not pin13.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button C (Random)
            music.stop()
            current_track_index = random.randint(0, len(music_list) - 1)
            music.play(music_list[current_track_index], wait=False, loop=False)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)
        elif not pin16.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button E (Random)
            music.stop()
            current_track_index = random.randint(0, len(music_list) - 1)
            music.play(music_list[current_track_index], wait=False, loop=False)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)

    # Volume control (A for up, B for down)
    if not pin_a.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button A
        music_volume = min(255, music_volume + 10)
        music.set_volume(music_volume)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)
    elif not pin_b.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button B
        music_volume = max(0, music_volume - 10)
        music.set_volume(music_volume)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)

    # Breathing light effect on RGB LEDs
    hue = (current_time // 10) % 360 # Cycle hue every 3.6 seconds
    r, g, b = hsl_to_rgb(hue, 99, 20) # High saturation, low brightness
    set_all_leds(r, g, b)

    utime.sleep_ms(10) # Small delay for main loop

Img

Breve explicación:

① Inicialice la matriz de LED y el volumen del sonido, conecte el pin RGB a P8 y establezca el número de RGB en 4.

from microbit import *
import music
import neopixel
import random
import utime

# ===================== Global Configuration & Variables =====================
# Disable micro:bit's default LED display to avoid conflicts
display.set_display_mode(display.DISPLAY_MODE_BLACK_AND_WHITE)

# LED strip configuration
NUM_LEDS = 4
LED_PIN = pin8
strip = neopixel.NeoPixel(LED_PIN, NUM_LEDS)

# Button debounce time (ms)
BTN_DEBOUNCE_TIME = 20

# Music volume (0-255)
music_volume = 128

# Music list (20 short tracks)
# Example: music.DADADADUM, music.ENTERTAINER, music.PRELUDE, etc.
# For brevity, using placeholders. In a real scenario, these would be actual music sequences.
music_list = [
    music.DADADADUM, music.ENTERTAINER, music.PRELUDE, music.ODEON,
    music.NYAN, music.RINGTONE, music.FUNK, music.BLUES,
    music.BIRTHDAY, music.WEDDING, music.FUNERAL, music.PUNCHLINE,
    music.PYTHON, music.BADDY, music.CHASE, music.WAWAWAWAA,
    music.JUMP_UP, music.JUMP_DOWN, music.POWER_UP, music.POWER_DOWN
]

# Game state variables
play_mode = 0  # 0: sequential, 1: random
current_track_index = 0
last_button_press_time = 0
last_music_play_time = 0

# Initialize pins with pull-up (PULL_UP: pressed=low level 0, released=high level 1)
pin13.set_pull(pin13.PULL_UP)  # Button C
pin15.set_pull(pin15.PULL_UP)  # Button D
pin16.set_pull(pin16.PULL_UP)  # Button E
pin14.set_pull(pin14.PULL_UP)  # Button F
pin_a = pin5 # Button A
pin_b = pin11 # Button B
pin_a.set_pull(pin_a.PULL_UP)
pin_b.set_pull(pin_b.PULL_UP)

# Function to set all LEDs to a specific color
def set_all_leds(r, g, b):
    for i in range(NUM_LEDS):
        strip[i] = (r, g, b)
    strip.show()

# Function to clear all LEDs
def clear_leds():
    set_all_leds(0, 0, 0)

# Function to convert HSL to RGB for breathing light effect
def hsl_to_rgb(h, s, l):
    h /= 360.0
    s /= 100.0
    l /= 100.0
    if s == 0:
        r = g = b = l
    else:
        def hue2rgb(p, q, t):
            if t < 0: t += 1
            if t > 1: t -= 1
            if t < 1/6: return p + (q - p) * 6 * t
            if t < 1/2: return q
            if t < 2/3: return p + (q - p) * (2/3 - t) * 6
            return p
        q = l * (1 + s) if l < 0.5 else l + s - l * s
        p = 2 * l - q
        r = hue2rgb(p, q, h + 1/3)
        g = hue2rgb(p, q, h)
        b = hue2rgb(p, q, h - 1/3)
    return (int(r * 255), int(g * 255), int(b * 255))

② Determine si se presiona el botón D o F. Presione D para ‘0-reproducción secuencial’, F para ‘1-reproducción aleatoria’.

# ===================== Main Loop =====================
while True:
    current_time = utime.ticks_ms()

    # Change play mode (D for sequential, F for random)
    if not pin15.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button D
        play_mode = 0
        display.show(Image.ASLEEP)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)
    elif not pin14.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button F
        play_mode = 1
        display.show(Image.HAPPY)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)

③ En modo secuencial, presione C para reproducir la canción anterior, E para saltar a la siguiente canción.

    # Play music based on mode
    if play_mode == 0: # Sequential
        if not music.is_playing():
            music.play(music_list[current_track_index], wait=False, loop=False)
            last_music_play_time = current_time

        if not pin13.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button C (Previous)
            music.stop()
            current_track_index = (current_track_index - 1 + len(music_list)) % len(music_list)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)
        elif not pin16.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button E (Next)
            music.stop()
            current_track_index = (current_track_index + 1) % len(music_list)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)

Sin embargo, en modo aleatorio, presione C/E para mezclar todas estas 20 canciones.

    elif play_mode == 1: # Random
        if not pin13.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button C (Random)
            music.stop()
            current_track_index = random.randint(0, len(music_list) - 1)
            music.play(music_list[current_track_index], wait=False, loop=False)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)
        elif not pin16.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button E (Random)
            music.stop()
            current_track_index = random.randint(0, len(music_list) - 1)
            music.play(music_list[current_track_index], wait=False, loop=False)
            last_button_press_time = current_time
            utime.sleep_ms(BTN_DEBOUNCE_TIME)

④ Presione A para subir el volumen (+10); presione B para bajarlo (-10). El volumen del zumbador de la micro:bit se decide por el voltaje de salida del pin interno conectado. Podemos controlar el volumen convirtiendo valores digitales 0~255 en analógicos a través de DAC.

    # Volume control (A for up, B for down)
    if not pin_a.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button A
        music_volume = min(255, music_volume + 10)
        music.set_volume(music_volume)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)
    elif not pin_b.read_digital() and (current_time - last_button_press_time) > BTN_DEBOUNCE_TIME: # Button B
        music_volume = max(0, music_volume - 10)
        music.set_volume(music_volume)
        last_button_press_time = current_time
        utime.sleep_ms(BTN_DEBOUNCE_TIME)

⑤ Haga que las luces RGB respiren en segundo plano.

    # Breathing light effect on RGB LEDs
    hue = (current_time // 10) % 360 # Cycle hue every 3.6 seconds
    r, g, b = hsl_to_rgb(hue, 99, 20) # High saturation, low brightness
    set_all_leds(r, g, b)

    utime.sleep_ms(10) # Small delay for main loop

5.2.4.5 Resultado de la Prueba

Img

Después de grabar el código, inserte la placa micro:bit en la ranura del gamepad (pilas instaladas), y active el interruptor a “ON”.

Después de encender, está en modo secuencial por defecto, y reproducirá la canción en el N.º “0”. Cuando termine, puede presionar C para la última canción o E para la siguiente.

Presione F para cambiar al modo aleatorio. Y puede presionar D para volver al secuencial. En el modo F, se reproducirá una pista aleatoria de estas 20 si presiona C/E. Después de terminar, se detiene.

Las luces RGB siempre están respirando desde el momento de encender. Mientras tanto, la matriz de LED de la micro:bit muestra “Img” en modo secuencial y “Img” en modo aleatorio.

Para el volumen, presione A para subir y B para bajar.

Img

Consejo: Si no hay respuesta en la placa, presione el botón de reinicio en la parte posterior de la placa micro:bit.

Img