Nu er een MIDI-bibliotheek voor de Raspberry Pi Pico beschikbaar is, kun je vrij gemakkelijk een kleine MIDI-“piano” maken. De MacroPad die ik vorige maand bouwde, is het perfecte toestelletje om hiermee te experimenteren. Wil je weten hoe je knoppen en leds met een Pi Pico verbindt, bekijk dan dat artikel!
Een MIDI-controller gedraagt zich anders dan een USB-toetsenbord. Zodra je een toets indrukt, genereert hij een MIDI-signaal dat een compatibel instrument of programma kan opvangen om geluid voort te brengen. Het signaal bevat de noot die gespeeld moet worden, maar kan ook de velocity bevatten (hoe snel of hard een toets wordt ingedrukt). Bovendien kun je signalen versturen om noten te buigen, het volume van een instrument in te stellen, … Toetsen gaan ook niet herhalen wanneer je ze langer ingedrukt houdt. Er moeten dus heel wat zaken in de code veranderen.
Aan de slag
Als je exact wilt volgen, moet je eerst mijn MacroPad bouwen, al volstaat zowat elke Raspberry Pi Pico waaraan enkele knoppen bevestigd zijn. Pas de onderstaande code wel aan zodat de pinnen met jouw hardware overeenkomen.
De Raspberry Pi Pico moet hiervoor CircuitPython draaien. Op mijn MacroPad stond dat al, maar als je van nul begint,
moet je de juiste firmware naar de Pico flashen. Verbind de Pico vervolgens met een computer; hij verschijnt als een
USB-schijf. Download de AdaFruit MIDI-bibliotheek en kopieer de map adafruit_midi (uit de repository) naar de
map lib op de Pico (maak die laatste indien nodig aan).
Je moet ook een programma installeren dat de signalen van een MIDI-apparaat in muziek omzet. Voor Windows bestaat het gratis programma Piano 10, dat prima is om mee te beginnen. Je vindt het in de Microsoft Store.
Maak ten slotte een bestand code.py in de hoofdmap van de Pico-schijf en voeg de onderstaande code toe. Start de
Pico opnieuw op en klaar! Je hebt nu een werkend MIDI-apparaat. Laten we de code wel even overlopen, zodat je de nodige
onderdelen aan jouw project kunt aanpassen als je geen exacte kopie van mijn MacroPad gebruikt.
Van de Pico een MIDI-apparaat maken
De volledige code staat hieronder. Daarna nemen we ze stap voor stap door.
import board
import digitalio
import pwmio
import time
import usb_midi
import adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)
print("MacroPad MIDI Board")
# Configuration, which LED pins are used, which buttons, how buttons map to notes
led_pins = [board.GP18,board.GP17,board.GP16,board.GP21,board.GP20,board.GP19, board.GP27, board.GP26,board.GP22]
button_pins = [board.GP13,board.GP14,board.GP15, board.GP10,board.GP11,board.GP12,board.GP7,board.GP8,board.GP9]
note_mapping = [
["C3", "C2"],
["D3", "D2"],
["E3", "E2"],
["F3", "F2"],
["G3", "G2"],
["A3", "A2"],
["B3", "B2"],
["C4", "C3"],
["G2", "G1"]
]
# Set up buttons
buttons = [digitalio.DigitalInOut(bp) for bp in button_pins]
for btn in buttons:
btn.direction = digitalio.Direction.INPUT
btn.pull = digitalio.Pull.UP
# Set up LEDs
leds = [pwmio.PWMOut(lp, frequency=1000, duty_cycle=0) for lp in led_pins]
# Set Initial Duty Cycles to 0 for each LED
duty_cycles = [0 for _ in led_pins]
# Key and trigger states
pressed_keys = [False for _ in button_pins]
triggered_keys = [False for _ in button_pins]
while True:
for ix, btn in enumerate(buttons):
pressed_keys[ix] = not btn.value
if not btn.value:
duty_cycles[ix] = 65025
for ix, (pk, tk) in enumerate(zip(pressed_keys, triggered_keys)):
if pk and not tk:
print("note %d started" % ix)
midi.send([NoteOn(a, 60) for a in note_mapping[ix]])
triggered_keys[ix] = True
elif not pk and tk:
print("note %d stopped" % ix)
triggered_keys[ix] = False
midi.send([NoteOff(a, 0) for a in note_mapping[ix]])
for ix, led in enumerate(leds):
led.duty_cycle = duty_cycles[ix]
duty_cycles[ix] = max(duty_cycles[ix] - 900, 0)
time.sleep(0.01)
Nadat alle bibliotheken geladen zijn, wordt met adafruit_midi.MIDI een MIDI-object aangemaakt. Die ene regel
maakt van de Pico ook een USB-MIDI-instrument. Daarna kunnen we met midi.send() MIDI-signalen versturen.
Vervolgens zijn er lijsten die bepalen welke GPIO-pinnen met leds en knoppen verbonden zijn. Zo heb ik mijn toestel bedraad. Tenzij je een exacte kopie bouwt, moet je dit aanpassen aan jouw schakeling. Er is ook een lijst met noten die worden gespeeld wanneer een knop wordt ingedrukt. Deze lijsten moeten even lang zijn en hun elementen horen bij elkaar. De led op pin GP18 hoort bijvoorbeeld onder de knop op pin GP13 te zitten. Wanneer je die knop indrukt, speelt hij de noten “C3” en “C2”. Voor elke knop wordt hier een lijst gebruikt, zodat meerdere noten tegelijk kunnen worden geactiveerd: in dit geval een noot en dezelfde noot een octaaf hoger. Op dezelfde manier kun je ook een volledig akkoord tegelijk spelen.
led_pins = [board.GP18,board.GP17,board.GP16,board.GP21,board.GP20,board.GP19, board.GP27, board.GP26,board.GP22]
button_pins = [board.GP13,board.GP14,board.GP15, board.GP10,board.GP11,board.GP12,board.GP7,board.GP8,board.GP9]
note_mapping = [
["C3", "C2"],
["D3", "D2"],
["E3", "E2"],
["F3", "F2"],
["G3", "G2"],
["A3", "A2"],
["B3", "B2"],
["C4", "C3"],
["G2", "G1"]
]
Nadat deze lijsten gedefinieerd zijn, volgt er wat standaardcode om de leds en knoppen te initialiseren. Die is identiek aan de code in de MacroPad.
Voor een MIDI-apparaat moeten toetsaanslagen anders worden verwerkt dan voor een computertoetsenbord. Als je een toets
op een piano indrukt en vasthoudt, begint die na een kort moment niet te herhalen zoals een lettertoets op je
computertoetsenbord. Daarom volgen we de toestand van de toetsaanslagen in twee lijsten. Zodra een knop wordt ingedrukt,
wordt de overeenkomstige positie in pressed_keys op True gezet. Zodra je hem loslaat, wordt die op false
gezet. De lijst triggered_keys houdt bij voor welke toetsen al een signaal verstuurd is.
# Key and trigger states
pressed_keys = [False for _ in button_pins]
triggered_keys = [False for _ in button_pins]
De hoofdlus luistert naar toetsaanslagen en verwerkt ze. Merk op dat de leds op exact dezelfde manier worden aangestuurd
als in het vorige artikel, dus dat bespreken we hier niet opnieuw (voor de leesbaarheid is dat deel uit het onderstaande
blok verwijderd). Eerst doorlopen we alle knoppen om te controleren of ze ingedrukt zijn. Als dat zo is, zetten we
pressed_keys op True. Vervolgens doorlopen we pressed_keys en triggered_keys en zoeken we naar
toetsen die ingedrukt zijn maar nog geen MIDI-signaal hebben geactiveerd. In dat geval wordt het Note On-signaal
verstuurd en wordt triggered_keys op True gezet. De regel
midi.send([NoteOn(a, 60) for a in note_mapping[ix]]) verstuurt de gebeurtenis voor alle noten die aan die knop gekoppeld zijn.
De velocity, hier het
getal 60, is constant omdat we niet kunnen meten hoe hard of snel een knop werd ingedrukt.
Voor toetsen die niet langer ingedrukt zijn maar nog wel geactiveerd staan, moet een Note Off-signaal worden verstuurd.
Dat gebeurt op dezelfde manier als het Note On-signaal, met midi.send([NoteOff(a, 0) for a in note_mapping[ix]]).
Hier wordt triggered_keys voor de losgelaten knop op False gezet.
Omdat er niets hoeft te gebeuren wanneer pressed_keys en triggered_keys dezelfde toestand hebben, wachten
we even met time.sleep(0.01) voordat de lus het volledige blok opnieuw uitvoert.
while True:
for ix, btn in enumerate(buttons):
pressed_keys[ix] = not btn.value
for ix, (pk, tk) in enumerate(zip(pressed_keys, triggered_keys)):
if pk and not tk:
print("note %d started" % ix)
midi.send([NoteOn(a, 60) for a in note_mapping[ix]])
triggered_keys[ix] = True
elif not pk and tk:
print("note %d stopped" % ix)
triggered_keys[ix] = False
midi.send([NoteOff(a, 0) for a in note_mapping[ix]])
time.sleep(0.01)
Verbinden met MIDI-software
Als alles correct werkt, zou de Pi Pico nu als een MIDI-apparaat moeten verschijnen in software die met een MIDI-controller overweg kan. Klik in Piano10 op de MIDI-knop en selecteer de Pico als invoerapparaat, zoals hieronder. Zodra je toetsen indrukt, markeert Piano10 de gespeelde noten op het toetsenbord op het scherm en speelt het geluid af.
Liedjes die je met negen toetsen kunt spelen
Omdat je de knoppen kunt herprogrammeren, kun je in principe liedjes met maximaal negen noten spelen. Met een eenvoudige diatonische toonladder van één octaaf kun je heel wat kinderliedjes spelen: “Frère Jacques”, “Twinkle Twinkle Little Star”, “Row Row Row Your Boat”, “Oh Susanna”, “Oh, When The Saints”, …
Als je enkele knoppen herprogrammeert, kun je ook “Happy Birthday”, “7 Nation Army” (The White Stripes) en waarschijnlijk nog veel meer spelen.
Je kunt elke knop ook een volledig gitaarakkoord laten “aanslaan”, zodat je enkele meezingers kunt spelen. Zonder aanslagpatronen klinkt dat misschien wel wat vlak en weinig interessant.
Conclusie
Een erg “clicky” toetsenblok als MIDI-controller is niet ideaal; je wilt waarschijnlijk liever stillere knoppen. Als proof of concept om te tonen hoe je een Raspberry Pi Pico als MIDI-controller instelt, is dit wel een prima demo! Als je serieus muziek wilt maken, ontgroei je de negen knoppen van dit keyboard waarschijnlijk snel. Toch is het een leuke manier om kennis te maken met het maken van en werken met MIDI-apparaten, en onderweg misschien enkele kinderliedjes te leren spelen.
Update: Vond je dit artikel leuk, bekijk dan ook een recentere versie van de MIDIpad-code. Die verwerkt akkoorden beter en bevat twee modificatorknoppen die het aantal speelbare noten aanzienlijk uitbreiden.
Vond je dit artikel interessant? Trakteer me op een koffie