5.2.4 Lecteur de musique

5.2.4.1 Aperçu

Img

Ici, nous construisons un lecteur de musique qui génère du son via le buzzer intégré sur la carte micro:bit (ne joue pas de musique vocale). Il dispose d’une bibliothèque de 20 pistes courtes et prend en charge la lecture séquentielle et aléatoire.

En mode séquentiel, l’appui sur C (chanson précédente) ou E (chanson suivante) change les pistes selon une séquence prédéfinie jusqu’à la fin de la liste ; tandis qu’en mode aléatoire, chaque pression sélectionne une piste au hasard parmi les 20 sons avec les voyants colorés qui clignotent, et lorsqu’une chanson se termine elle s’arrête immédiatement.

Pendant ce temps, la matrice LED du micro:bit affiche en temps réel le mode de lecture en cours.

Img

5.2.4.2 Pièces requises

Img

Img

Img

micro:bit V2 board (fourni par l’utilisateur) ×1

micro:bit Smart Gamepad (assemblé) ×1

AAA battery (fourni par l’utilisateur) ×4

5.2.4.3 Flux du code

Img

5.2.4.4 Code de test

Code complet:

# import related libraries
from microbit import *
import music, neopixel, random

# --- Configuration & Data ---
vol = 50
mode = 0  # 0: Manual, 1: Random
idx = 0
last_idx = -1
hue = 0
strip = neopixel.NeoPixel(pin8, 4)
melodies = ["DADADADUM", "ENTERTAINER", "PRELUDE", "ODE", "NYAN", "RINGTONE", "FUNK", "BLUES", 
            "BIRTHDAY", "WEDDING", "FUNERAL", "PUNCHLINE", "BADDY", "CHASE", "BA_DING", 
            "WAWAWAWAA", "JUMP_UP", "JUMP_DOWN", "POWER_UP", "POWER_DOWN"]

# Pin Initialization (P13-P16)
btns = [pin13, pin14, pin15, pin16]
for p in btns: p.set_pull(p.PULL_UP)
set_volume(vol)

def get_rgb(h):
    """ Simplified HSL to RGB logic """
    h %= 360
    pos = h // 60
    f = (h % 60) / 60.0
    v = 76 # 255 * 0.3 (Brightness coefficient)
    up, down = int(v * f), int(v * (1 - f))
    res = [(v, up, 0), (down, v, 0), (0, v, up), (0, down, v), (up, 0, v), (v, 0, down)]
    return res[pos]

# State tracking (for debouncing)
last_states = [1] * 4
last_press_t = 0

while True:
    curr_t = running_time()
    
    # 1. Volume Control (Buttons A/B)
    if button_a.was_pressed(): vol = min(250, vol + 10); set_volume(vol)
    if button_b.was_pressed(): vol = max(20, vol - 10); set_volume(vol)

    # 2. Joystick/Button Input Detection (with debouncing)
    for i, p in enumerate(btns):
        v = p.read_digital()
        if v == 0 and last_states[i] == 1 and (curr_t - last_press_t > 50):
            last_press_t = curr_t
            if i == 3: mode = 0; sleep(500)     # P16: Manual Mode
            elif i == 1: mode = 1; sleep(500)   # P14: Random Mode
            elif i == 2: # P15: Next track / Random track
                idx = random.randint(0, 19) if mode else (idx + 1) % 20
            elif i == 0: # P13: Previous track / Random track
                idx = random.randint(0, 19) if mode else (idx - 1) % 20
        last_states[i] = v

    # 3. Music Playback Logic
    if idx != last_idx:
        music.stop()
        try:
            music.play(getattr(music, melodies[idx]), wait=False)
            last_idx = idx
        except: pass

    # 4. Lighting & Display Updates
    hue = (hue + 1) % 360
    strip.fill(get_rgb(hue))
    strip.show()
    
    # Show Mode Icon: "X" for Random, Arrow for Manual
    display.show(Image("00000:99099:00900:99099:00000") if mode else Image.ARROW_E)
    
    sleep(10)

Img

Brève explication :

① Importer les bibliothèques, configurer les constantes et l’initialisation.

Il importe d’abord la bibliothèque microbit pour accéder aux fonctions de base du Micro:bit, music pour jouer la musique intégrée, neopixel pour contrôler la bande LED NeoPixel, et random pour générer des nombres aléatoires.

Il définit ensuite une série de variables et constantes globales : vol fixe le volume initial à 50 ; mode contrôle le mode de lecture (0 pour sélection manuelle, 1 pour lecture aléatoire) ; idx stocke l’indice musical courant ; une variable suit l’indice de lecture précédent pour éviter les lectures en double ; hue contrôle la couleur de la bande NeoPixel ; strip initialise une bande NeoPixel connectée à pin8 de quatre LEDs ; et melodies énumère tous les titres music de MicroPython.

Ensuite, la liste btns définit les quatre broches de boutons externes de pin13 à pin16, en leur assignant des résistances pull-up internes (p.PULL_UP) dans une boucle — ce qui donne des broches à niveau haut lorsque les boutons sont relâchés et à niveau bas lorsqu’ils sont enfoncés.

set_volume (vol) règle le volume à sa valeur par défaut.

# import related libraries
from microbit import *
import music, neopixel, random

# --- Configuration & Data ---
vol = 50
mode = 0  # 0: Manual, 1: Random
idx = 0
last_idx = -1
hue = 0
strip = neopixel.NeoPixel(pin8, 4)
melodies = ["DADADADUM", "ENTERTAINER", "PRELUDE", "ODE", "NYAN", "RINGTONE", "FUNK", "BLUES", 
            "BIRTHDAY", "WEDDING", "FUNERAL", "PUNCHLINE", "BADDY", "CHASE", "BA_DING", 
            "WAWAWAWAA", "JUMP_UP", "JUMP_DOWN", "POWER_UP", "POWER_DOWN"]

# Pin Initialization (P13-P16)
btns = [pin13, pin14, pin15, pin16]
for p in btns: p.set_pull(p.PULL_UP)
set_volume(vol)

② Fonction de conversion de couleur et variable de stabilisation.

get_rgb(h) est une fonction simplifiée de conversion HSL (Hue, Saturation, Lightness) vers RGB. Elle prend une valeur de teinte h (0–359) et la convertit en triplet RGB. La luminosité v est fixée à 76 (environ 255 × 0,3, correspondant au coefficient BRIGHTNESS). Cette fonction facilite la génération de couleurs arc-en-ciel en fonction de la teinte.

La liste last_states stocke les états précédents des quatre boutons, initialement tous à 1 (niveau haut pour non appuyé). last_press_t enregistre le temps de la dernière pression de bouton. Ces variables implémentent ensemble un anti-rebond logiciel pour éviter plusieurs détections lors d’une seule pression.

def get_rgb(h):
    """ Simplified HSL to RGB logic """
    h %= 360
    pos = h // 60
    f = (h % 60) / 60.0
    v = 76 # 255 * 0.3 (Brightness coefficient)
    up, down = int(v * f), int(v * (1 - f))
    res = [(v, up, 0), (down, v, 0), (0, v, up), (0, down, v), (up, 0, v), (v, 0, down)]
    return res[pos]

# State tracking (for debouncing)
last_states = [1] * 4
last_press_t = 0

③ Boucle principale : Contrôle du volume.

La boucle infinie (while True) récupère le temps d’exécution courant curr_t. Ensuite, elle gère les boutons A et B de la carte Micro:bit :

  • Si button_a est pressé (button_a.was_pressed()), le volume vol augmente de 10, sans dépasser 250. set_volume(vol) met à jour le volume système.

  • Si button_b est pressé (button_b.was_pressed()), vol diminue de 10, sans descendre en dessous de 20. set_volume(vol) met à jour le volume système.

was_pressed() renvoie True une seule fois lors de la transition de non-appuyé à appuyé, fournissant un anti-rebond intrinsèque.

while True:
    curr_t = running_time()
    
    # 1. Volume Control (Buttons A/B)
    if button_a.was_pressed(): vol = min(250, vol + 10); set_volume(vol)
    if button_b.was_pressed(): vol = max(20, vol - 10); set_volume(vol)

④ Boucle principale : détection d’entrée des boutons et changement de mode.

Elle itère sur les quatre boutons externes (pin13 à pin16) dans la liste btns, détectant leurs états. Le bouton n’est pris en compte que lorsqu’il passe de haut (non appuyé) à bas (appuyé) et qu’il s’est écoulé plus de 50 millisecondes depuis la dernière pression valide.

  • Si pin16 est pressé (i == 3), mode = 0 (mode manuel) et pause de 500 ms.

  • Si pin14 est pressé (i == 1), mode = 1 (mode aléatoire) et pause de 500 ms.

  • Si pin15 est pressé (i == 2), mettre à jour l’indice musical idx selon le mode en cours : sélection aléatoire en mode aléatoire ; morceau suivant en mode manuel.

  • Si pin13 est pressé (i == 0), mettre à jour l’indice musical idx selon le mode en cours : sélection aléatoire en mode aléatoire ; morceau précédent en mode manuel.

À la fin de chaque boucle, last_states[i] = v met à jour l’état courant du bouton en préparation du prochain contrôle de stabilisation.

    # 2. Joystick/Button Input Detection (with debouncing)
    for i, p in enumerate(btns):
        v = p.read_digital()
        if v == 0 and last_states[i] == 1 and (curr_t - last_press_t > 50):
            last_press_t = curr_t
            if i == 3: mode = 0; sleep(500)     # P16: Manual Mode
            elif i == 1: mode = 1; sleep(500)   # P14: Random Mode
            elif i == 2: # P15: Next track / Random track
                idx = random.randint(0, 19) if mode else (idx + 1) % 20
            elif i == 0: # P13: Previous track / Random track
                idx = random.randint(0, 19) if mode else (idx - 1) % 20
        last_states[i] = v

⑤ Boucle principale : logique de lecture musicale.

Elle contrôle la lecture en vérifiant si l’indice musical courant idx est différent du précédent last_idx. Si oui, il faut changer la musique :

  1. music.stop() arrête la musique en cours.

  2. music.play(getattr(music, melodies[idx]), wait=False) tente de lancer une nouvelle musique. getattr(music, melodies[idx]) obtient dynamiquement les données musicales du nom correspondant dans music, et wait=False empêche la lecture de bloquer la boucle principale.

  3. Si la lecture est lancée avec succès, mettre à jour last_idx = idx.

  4. Le bloc try...except capture d’éventuelles erreurs ; par exemple, il pourrait y avoir des titres invalides dans la liste melodies.

    # 3. Music Playback Logic
    if idx != last_idx:
        music.stop()
        try:
            music.play(getattr(music, melodies[idx]), wait=False)
            last_idx = idx
        except: pass

⑥ Boucle principale : mises à jour des lumières et de l’affichage.

Mise à jour de la couleur de la bande NeoPixel et de l’affichage de la matrice LED Micro:bit :

  1. hue = (hue + 1) % 360 incrémente continuellement hue pour lui faire parcourir 0 à 359 et obtenir un dégradé arc-en-ciel.

  2. strip.fill(get_rgb(hue)) utilise get_rgb pour générer une couleur basée sur la hue courante et remplit toute la bande NeoPixel avec cette couleur.

  3. strip.show() envoie la couleur mise à jour à la bande NeoPixel pour affichage.

  4. display.show(...) affiche une image selon le mode courant. mode = 1 (aléatoire) : affiche un “X” personnalisé ; mode = 0 (manuel) : affiche une flèche pointant à droite (Image.ARROW_E).

Ensuite, sleep(10) introduit un court délai pour une vitesse d’exécution adaptée, une charge CPU réduite et un effet plus fluide.

    # 4. Lighting & Display Updates
    hue = (hue + 1) % 360
    strip.fill(get_rgb(hue))
    strip.show()
    
    # Show Mode Icon: "X" for Random, Arrow for Manual
    display.show(Image("00000:99099:00900:99099:00000") if mode else Image.ARROW_E)
    
    sleep(10)

5.2.4.5 Résultat du test

Img

Après avoir gravé le code, insérez la carte micro:bit dans la fente du gamepad (piles installées), et positionnez l’interrupteur sur « ON ».

Au démarrage, il est en mode séquentiel par défaut et jouera le morceau numéro 0. Lorsqu’il se termine, vous pouvez appuyer sur C pour le morceau précédent ou sur E pour le suivant.

Appuyez sur F pour passer en mode aléatoire. Et vous pouvez appuyer sur D pour revenir au mode séquentiel. En mode F, une piste aléatoire parmi ces 20 sera jouée si vous appuyez sur C/E. Après la fin, la lecture s’arrête.

Les lumières RGB font toujours un effet “respiration” dès la mise sous tension. Pendant ce temps, la matrice LED du micro:bit affiche “Img” en mode séquentiel et “Img” en mode aléatoire.

Pour le volume, appuyez sur A pour augmenter et B pour diminuer.

Img

Astuce : Si la carte ne répond pas, appuyez sur le bouton reset à l’arrière de la carte micro:bit.

Img