4x4 のステキなkeypad ができた。

GarageBandに繋いで演奏できるようにする。 MIDI制御の基本は同じ。

## GarageBand のドラムセットのnote

ドレミを押すとなんかドラムセットのどっかの音が鳴る。

その程度の認識だったけど、ちゃんと調べてくれてるひとが! ありがとう。

python
BD=36   # バスドラム
SN=38   # スネア1
RM=40   # リムタムはないので、スネア2
CH=42   # クローズドハイハット
OH=46   # オープンハイハット
HT=50   # ハイタム
LT=45   # ロータム
FT=43   # フロアタム
HC=49   # クラッシュド・シンバル(ハイ)
LC=49   # クラッシュド・シンバル(ロウ)
RC=51   # ライドシンバル

キーと音(楽器)の配置

4x4 のどこを押したらどの音がなるべきか?

なんとなくドラムセットの配置と似ればいいのかと思っていた。 が、スパンキーさんの本と動画でなるほどと感心したので、その通りに決定。

python
        self.notes_ = [
            [ LC, LT, FT, HC ],
            [ CH, HT, OH, CH ],
            [ RC, SN, RM, RC ],
            [ BD, BD, BD, BD ], ]

配線

  • GP2 ... COL A
  • GP3 ... COL B
  • GP4 ... COL C
  • GP5 ... COL D
  • GP6 ... ROW 1
  • GP7 ... ROW 2
  • GP8 ... ROW 3
  • GP9 ... ROW 4

プログラム

CircuitPythonで、簡単。

finger_drum.py · Gistpython
import board
import busio
import digitalio
import time

import usb_midi

import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn

NROW=4
NCOL=4

# Garageband notes for drum
BD=36   # バスドラム
SN=38   # スネア1
RM=40   # リムタム=スネア2
CH=42   # クローズドハイハット
OH=46   # オープンハイハット
HT=50   # ハイタム
LT=45   # ロータム
FT=43   # フロアタム
HC=49   # クラッシュド・シンバル(ハイ)
LC=49   # クラッシュド・シンバル(ロウ)
RC=51   # ライドシンバル

class MidiDrum:
    def __init__(self):
        self.was_presses_ = []
        self.was_releases_ = []

        # keymap
        self.notes_ = [
            [ LC, LT, FT, HC ],
            [ CH, HT, OH, CH ],
            [ RC, SN, RM, RC ],
            [ BD, BD, BD, BD ],
        ]
        # key press status
        self.presses_ = [
            [ False, False, False, False ],
            [ False, False, False, False ],
            [ False, False, False, False ],
            [ False, False, False, False ],
        ]
        # COLS(for scan key press & relase)
        self.cols_ = []
        for pin in (board.GP2, board.GP3, board.GP4, board.GP5):
            key = digitalio.DigitalInOut(pin)
            key.switch_to_input(pull=digitalio.Pull.UP)
            self.cols_.append(key)

        # ROWS(for V or GND output)
        self.rows_ = []
        for pin in (board.GP6, board.GP7, board.GP8, board.GP9):
            key = digitalio.DigitalInOut(pin)
            key.direction = digitalio.Direction.OUTPUT
            self.rows_.append(key)

    def scan_keymap(self):
        #
        # scan row from 0 to (NROW-1)
        #
        # value=False is PRESS

        is_presses = []
        is_releases = []

        for row_no in range(NROW):
            # change HIGH and LOW for scan
            for r in range(NROW):
                if row_no == r:
                    self.rows_[r].value = 0   # OFF
                else:
                    self.rows_[r].value = 1   # ON

            for col_no in range(NCOL):
                # check key press
                value = self.cols_[col_no].value    # get ON/OFF

                if value != self.presses_[row_no][col_no]:
                    # 変化があった
                    # print("change ({},{}) {}->{}".format(row_no, col_no, self.presses_[row_no][col_no], value))
                    self.presses_[row_no][col_no] = value
                    note = self.notes_[row_no][col_no]
                    if not value:
                        # pressed
                        is_presses.append(note)
                    else:
                        # released
                        is_releases.append(note)

        return [ is_presses, is_releases ]

if __name__ == "__main__":

    # MIDI
    midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)

    print("START")

    # key press LED
    led = digitalio.DigitalInOut(board.GP25)
    led.direction = digitalio.Direction.OUTPUT

    md = MidiDrum()

    next_time = time.monotonic_ns()
    while True:

        # get current key press or release
        now = time.monotonic_ns()
        if next_time < now:
            [is_presses, is_releases] = md.scan_keymap()

            if len(is_presses) > 0:
                for note in is_presses:
                    # print("press note {}".format(note))
                    midi.send(NoteOn(note, 127))
                led.value = True    # ON
            if len(is_releases) > 0:
                for note in is_releases:
                    # print("release note {}".format(note))
                    midi.send(NoteOff(note, 0))
                led.value = False   # OFF
            next_time = now + 10_000_000    # 0.01sec

        time.sleep(0.001)

midi ライブラリの設定は、こっち。