FingerDrum をつくる(keybow2040編)

目次
Pimoroni でセールをしていたので、思い切って購入。
4x4 のキーボードで、RP2040 を内蔵している。 キー毎にRGB LEDがついていて、FingerDrumにぴったり。
透明なキーキャップで準備万端。

仕様

音の雰囲気で色を決めて、キーを押すと光る。
音(note)
- BD = 36 # バスドラム
- SN = 38 # スネア1
- RM = 40 # リムタム=スネア2
- CH = 42 # クローズドハイハット
- OH = 46 # オープンハイハット
- HT = 50 # ハイタム
- LT = 45 # ロータム
- FT = 43 # フロアタム
- HC = 49 # クラッシュド・シンバル(ハイ)
- LC = 49 # クラッシュド・シンバル(ロウ)
- RC = 51 # ライドシンバル
BD(バスドラム)、RC(ライドシンバル)、CH(クローズハイハット)は、左右2個づつ。
LIGHT
キーボードの RGB LED は基本的に、消灯している。
押したときだけ点灯。
しかし、全部点灯しているところも見たいので、 LIGHT キーで全てのキーを点灯させる。

ROTATE
USB-Cコネクタは、LT と FT の間あたりにある。
USBケーブルの取り回しによっては、 CH と RC の間辺りになって欲しいこともある。その場合は、ROTATE キーでレイアウトを変更。
DISCO
長押しで、LEDが虹色にうねうね。
program
finger_drum.py · Gistpython
import time
import math
import board
from keybow2040 import Keybow2040, number_to_xy, hsv_to_rgb
import usb_midi
import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
#
# KEY TYPE
#
BD = 36 # バスドラム
SN = 38 # スネア1
RM = 40 # リムタム=スネア2
CH = 42 # クローズドハイハット
OH = 46 # オープンハイハット
HT = 50 # ハイタム
LT = 45 # ロータム
FT = 43 # フロアタム
HC = 49 # クラッシュド・シンバル(ハイ)
LC = 49 # クラッシュド・シンバル(ロウ)
RC = 51 # ライドシンバ
ROTATE=100
ALL=200
#
# COLOR DEFINITON
#
red = (255, 0, 50)
brown = (55, 0, 0)
green = (10, 155, 10)
purple = (80, 5, 80)
blue = (0, 0, 255)
yellow = (50, 50, 0)
hyellow = (80, 80, 10)
black = (5, 5, 5)
white = (109, 100, 100)
class FingerDrum():
def __init__(self):
# keymap
self.default_notes_ = [
[LC, LT, FT, HC],
[CH, HT, OH, CH],
[RC, SN, RM, RC],
[ROTATE, BD, BD, ALL],
]
# keymap and note table
self.notes_ = []
# for rotate
self.current_layout_ = 0
self.rotate_keylayout(default=True)
# disco mode counter
self.disco_step_ = 0
# drum rooling mode
self.roll_on_ = 1
self.prev_time_ = 0
# FingerDrum Mode
self.is_disco_mode_ = 0
self.disco_key_no_ = 0
self.is_hold_key_ = None # for rolling
self.next_disco_led_time_ = 0
self.colors_ = {
BD : brown, # バスドラム
SN : blue, # スネア1
RM : blue, # リムタム=スネア2
CH : yellow, # クローズドハイハット
OH : yellow, # オープンハイハット
HT : purple, # ハイタム
LT : purple, # ロータム
FT : purple, # フロアタム
HC : hyellow, # クラッシュド・シンバル(ハイ)
LC : hyellow, # クラッシュド・シンバル(ロウ)
RC : yellow, # ライドシンバ
ROTATE: black,
ALL: red,
}
# Set USB MIDI up on channel 0.
self.midi_ = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)
def update(self, keys):
now = time.monotonic_ns()
if self.is_hold_key_ is not None:
self.roll()
if self.is_disco_mode_:
if now > self.next_disco_led_time_:
self.disco(keys)
self.next_disco_led_time_ = now + 99_000_000
def disco(self, keys):
# key_no for current color changing
self.disco_key_no_ += 1
if self.disco_key_no_ > 15:
self.disco_step_ += 10
self.disco_key_no_ = 0
x, y = number_to_xy(self.disco_key_no_)
hue = (x + y + (self.disco_step_ / 20)) / 8
hue = hue - int(hue)
hue = hue - math.floor(hue)
r, g, b = hsv_to_rgb(hue, 1, 1)
keys[self.disco_key_no_].set_led(r, g, b)
def get_note(self, key):
note = self.notes_[key.number//4][key.number%4]
color = self.colors_[note]
return [note, color]
def send_midi(self, note, velocity=100):
if note < 100:
if velocity > 0:
self.midi_.send(NoteOn(note, velocity))
else:
self.midi_.send(NoteOff(note, 0))
def rotate_keylayout(self, default=False):
#
# 0 1 2 3 12 8 4 0
# 4 5 6 7 13 9 5 1
# 8 9 10 11 -> 14 10 6 2
# 12 13 14 15 15 11 7 3
#
self.notes_ = []
for row in range(4):
cols = []
for col in range(4):
cols.append(self.default_notes_[row][col])
self.notes_.append(cols)
if default:
self.current_layout_ = 0
return
if self.current_layout_ == 1:
for n in range(4):
self.notes_[n][3] = self.default_notes_[0][n]
self.notes_[n][2] = self.default_notes_[1][n]
self.notes_[n][1] = self.default_notes_[2][n]
self.notes_[n][0] = self.default_notes_[3][n]
self.current_layout_ = 1 - self.current_layout_
return
def roll(self):
# Drum rolling!
[note, color] = drum.get_note(drum.is_hold_key_)
now = time.monotonic_ns()
past_time = now - self.prev_time_
if past_time > 33_333_333:
if self.roll_on_:
drum.send_midi(note)
drum.is_hold_key_.set_led(*white)
else:
drum.send_midi(note, 0)
drum.is_hold_key_.set_led(*black)
self.roll_on_ = 1 - self.roll_on_
self.prev_time_ = now
def switch_disco_mode(self):
self.disco_step_ = 0
self.is_disco_mode_ = 1 - drum.is_disco_mode_
def switch_rolling_mode(self, key):
self.is_hold_key_ = key
if __name__ == "__main__":
# Set up Keybow
i2c = board.I2C()
keybow = Keybow2040(i2c)
keys = keybow.keys
drum = FingerDrum()
start = time.monotonic_ns()
# Loop through keys and attach decorators.
for key in keys:
# If pressed, send a MIDI note on command and light key.
@keybow.on_press(key)
def press_handler(key):
[note, color] = drum.get_note(key)
if note == ROTATE:
# ROTATE KEY LAYOUT
drum.rotate_keylayout()
if note in [ ROTATE, ALL ]:
# ALL LIGHT
for k in keys:
[note, color] = drum.get_note(k)
k.set_led(*color)
else:
drum.send_midi(note)
key.set_led(*color)
@keybow.on_release(key)
def release_handler(key):
[note, color] = drum.get_note(key)
drum.send_midi(note, 0)
if not drum.is_disco_mode_:
key.set_led(*black)
drum.switch_rolling_mode(None)
@keybow.on_hold(key)
def on_hold_handler(key):
[note, color] = drum.get_note(key)
if note == ALL:
# DISCO MODE SWITCH
drum.switch_disco_mode()
elif note != ROTATE:
drum.switch_rolling_mode(key)
while True:
# Always remember to call keybow.update()!
keybow.update()
drum.update(keys)install
keybow2040 は、はじめから circuitpython がinstall済みで、MIDI driverもある。 上記のプログラムを自動起動用のファイルとして、keybow2040にcopyするだけ。
Macならこんな感じ。
bash
cp 上記のファイル /Volumes/CIRCUITPY/code.py勝手にresetされるので、これでおしまい。 GarageBand を起動して、ドラムに設定すればすぐ使える。 iPad Proなら、USB-Cケーブルで繋いで、GarageBandを起動し、ドラムセットを設定する。
demo
おまけ
キーキャップを白に変更。こっちのほうがいいかも。

