5.2.4 Musik-Player

5.2.4.1 Übersicht

Img

Hier bauen wir einen Musik-Player, der über den eingebauten Summer auf dem micro:bit-Board Töne erzeugt (spielt keine Vokalmusik ab). Er verfügt über eine Bibliothek von 20 kurzen Titeln und unterstützt sowohl die sequentielle als auch die zufällige Wiedergabe.

Im sequentiellen Modus wechseln die Tasten C (vorheriger Titel) oder E (nächster Titel) die Titel gemäß einer voreingestellten Reihenfolge, bis das Ende der Liste erreicht ist; im Zufallsmodus wählt jeder Tastendruck einen Titel zufällig aus den 20 Klängen aus, wobei die Farblichter blinken, und wenn ein Lied beendet ist, stoppt es sofort.

In der Zwischenzeit zeigt die micro:bit-LED-Matrix den aktuellen Wiedergabemodus in Echtzeit an.

Img

5.2.4.2 Benötigte Teile

Img

Img

Img

micro:bit V2 Board (selbst mitgebracht) ×1

micro:bit Smart Gamepad (montiert) ×1

AAA Batterie (selbst mitgebracht) ×4

5.2.4.3 Codeablauf

Img

5.2.4.4 Testcode

Vollständiger Code:

# 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):
        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

Kurze Erklärung:

① Bibliotheken importieren, Konstanten konfigurieren und initialisieren.

Es importiert zuerst die microbit-Bibliothek, um auf die Kernfunktionen des Micro:bit zuzugreifen, music zum Abspielen eingebauter Musik, neopixel zur Steuerung des NeoPixel-LED-Streifens und random zur Generierung von Zufallszahlen.

Anschließend werden eine Reihe globaler Variablen und Konstanten definiert: vol setzt die anfängliche Lautstärke auf 50; mode steuert den Musikwiedergabemodus (0 für manuelle Auswahl, 1 für zufällige Wiedergabe); idx speichert den aktuellen Musikindex; verfolgt den vorherigen Wiedergabeindex, um doppelte Wiedergaben zu vermeiden; hue steuert die Farbe des NeoPixel-Streifens; strip initialisiert einen NeoPixel-Streifen, der mit pin8 von vier LEDs verbunden ist; und melodies listet alle MicroPython music-Titel auf.

Als Nächstes definiert die btns-Liste die vier externen Tasten-Pins von pin13 bis pin16 und weist ihnen in einer Schleife interne Pull-up-Widerstände (p.PULL_UP) zu – was zu High-Level-Pins führt, wenn die Tasten losgelassen werden, und Low-Level-Pins, wenn sie gedrückt werden.

set_volume (vol) setzt die Lautstärke auf ihren Standardwert.

# 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)

② Farbkonvertierungsfunktion und Stabilisierungsvariable.

get_rgb(h) ist eine vereinfachte HSL-zu-RGB-Farbkonvertierungsfunktion (Farbton, Sättigung, Helligkeit). Sie akzeptiert einen Farbtonwert h (0–359) und konvertiert ihn in ein RGB-Triplett. Die Helligkeit v ist auf 76 festgelegt (ungefähr 255 × 0,3, entsprechend dem BRIGHTNESS-Koeffizienten). Diese Funktion erleichtert die Erzeugung von Regenbogenfarben basierend auf dem Farbtonwert.

Die last_states-Liste speichert die vorherigen Zustände der vier Tasten, die anfänglich alle auf 1 (High-Level für nicht gedrückt) gesetzt sind. last_press_t zeichnet die Zeit des letzten Tastendrucks auf. Zusammen implementieren diese Variablen eine Software-Entprellung, um mehrere Erkennungen eines einzelnen Tastendrucks zu verhindern.

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

③ Hauptschleife: Lautstärkeregelung.

Es gibt eine Endlosschleife (while True), die die aktuelle Laufzeit curr_t abruft. Dann verarbeitet sie die Tasten A und B auf dem Micro:bit-Board:

  • Wenn button_a gedrückt wird (button_a.was_pressed()), erhöht sich die Lautstärke vol um 10, überschreitet aber nicht 250. set_volume(vol) wird anschließend aufgerufen, um die Systemlautstärke zu aktualisieren.

  • Wenn button_b gedrückt wird (button_b.was_pressed()), verringert sich vol um 10, bleibt aber nicht unter 20. set_volume(vol) wird anschließend aufgerufen, um die Systemlautstärke zu aktualisieren.

was_pressed() gibt nur einmal True zurück, wenn die Taste von einem nicht gedrückten in einen gedrückten Zustand übergeht, was eine inhärente Entprellung bietet.

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)

④ Hauptschleife: Tasten-Eingabeerkennung und Moduswechsel.

Es durchläuft die vier externen Tasten (pin13 bis pin16) in der btns-Liste und erkennt deren gedrückte Zustände. Die Taste wird nur dann reagiert, wenn sie von High (nicht gedrückt) auf Low (gedrückt) wechselt und seit dem letzten gültigen Tastendruck mehr als 50 Millisekunden vergangen sind.

  • Wenn pin16 gedrückt wird (i == 3), wird mode = 0 (manueller Modus) und pausiert für 500 ms.

  • Wenn pin14 gedrückt wird (i == 1), wird mode = 1 (Zufallsmodus) und pausiert für 500 ms.

  • Wenn pin15 gedrückt wird (i == 2), wird der Musikindex idx entsprechend dem aktuellen Muster aktualisiert: Im Zufallsmodus wird eine Musik zufällig ausgewählt; im manuellen Modus wird die nächste Musik abgespielt.

  • Wenn pin13 gedrückt wird (i == 0), wird der Musikindex idx entsprechend dem aktuellen Muster aktualisiert: Im Zufallsmodus wird eine Musik zufällig ausgewählt; im manuellen Modus wird die vorherige Musik abgespielt.

Am Ende jeder Schleife aktualisiert last_states[i] = v den aktuellen Status der Taste zur Vorbereitung auf die nächste Stabilisierungsprüfung.

    # 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

⑤ Hauptschleife: Musikwiedergabelogik.

Es steuert die Wiedergabe der Musik, indem es überprüft, ob der aktuelle Musikindex idx sich vom letzten last_idx unterscheidet. Wenn ja, muss die Musik gewechselt werden:

  1. music.stop() stoppt die aktuell spielende Musik.

  2. music.play(getattr(music, melodies[idx]), wait=False) versucht, eine neue Musik abzuspielen. getattr(music, melodies[idx]) ruft dynamisch die Musikdaten des entsprechenden Namens in music ab, und wait=False stellt sicher, dass die Musikwiedergabe die Hauptschleife nicht blockiert.

  3. Wenn die Wiedergabe erfolgreich ist, aktualisieren Sie last_idx = idx.

  4. try...except fängt potenzielle Fehler ab; zum Beispiel könnten ungültige Musiktitel in der melodies-Liste vorhanden sein.

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

⑥ Hauptschleife: Licht- und Display-Updates.

Hier ist ein Update der Farbe des NeoPixel-Streifens und der Anzeige der Micro:bit-LED-Matrix:

  1. hue = (hue + 1) % 360 erhöht hue kontinuierlich, um es zwischen 0 und 359 zu zyklisieren, für ein Regenbogen-Verlaufslicht.

  2. strip.fill(get_rgb(hue)) verwendet get_rgb, um eine Farbe basierend auf dem aktuellen hue zu erzeugen und den gesamten NeoPixel-Streifen mit dieser Farbe zu füllen.

  3. strip.show() sendet die aktualisierte Farbe zur Anzeige an den NeoPixel-Streifen.

  4. display.show(...) zeigt die Anzeige je nach aktuellem mode an. mode = 1 (zufällig): zeigt ein benutzerdefiniertes „X“; mode = 0 (manuell), zeigt einen Pfeil nach rechts (Image.ARROW_E).

Dann führt sleep(10) eine kurze Verzögerung für eine geeignete Ausführungsgeschwindigkeit, eine geringere CPU-Last und einen flüssigeren Effekt ein.

    # 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 Testergebnis

Img

Nach dem Brennen des Codes stecken Sie das micro:bit-Board in den Steckplatz des Gamepads (Batterien eingelegt) und schalten Sie den Schalter auf „ON“.

Nach dem Einschalten befindet es sich standardmäßig im sequentiellen Modus und spielt das Lied unter N.O. „0“ ab. Wenn es beendet ist, können Sie C für das letzte Lied oder E für das nächste drücken.

Drücken Sie F, um in den Zufallsmodus zu wechseln. Und Sie können D drücken, um zum sequentiellen Modus zurückzukehren. Im F-Modus wird ein zufälliger Titel aus diesen 20 abgespielt, wenn Sie C/E drücken. Nach Beendigung stoppt es.

Die RGB-Lichter atmen immer vom Einschalten an. In der Zwischenzeit zeigt die micro:bit-LED-Matrix im sequentiellen Modus „Img“ und im Zufallsmodus „Img“ an.

Für die Lautstärke drücken Sie A zum Erhöhen und B zum Verringern.

Img

Tipp: Wenn das Board nicht reagiert, drücken Sie bitte die Reset-Taste auf der Rückseite des micro:bit-Boards.

Img