Trellis で beat machine をつくる(完成編)

目次
Trellisの制御 とControlPanelができた。
midi 制御も過去にやった。
全部組み合わせて高機能なメトロノーム、beat machine もどきをつくる。
配線
- sda … GP0
- scl …GP1
- play_btn_pin … GP2
- sel_btn_pin …GP4(BPM変更)
I2Cのアドレスは、以下の通り。
- 0x70 ... 8x8 3色LED Matrix
- 0x77, 0x72, 0x73, 0x71 ... Trellis
プログラム
ControlPanel のinstall
前回つくった ControlPanelを利用する。 実際には、 control_panel.py をRPi Pico の CIRCUITPY Volumeのlibフォルダに copyしておく。
downloadして、Macならこんな感じでcopy。
bash
cp control_panel.py /Volume/CIRCUITPY/lib制御
- 初期化
- MIDI 初期化
- I2C 初期化
- Trellis 初期化
- ControlPanel 初期化
- 8x8 3色Matrix に初期BPMの数値を表示
- 主制御 ... BPMの1拍を1/8づつ刻んで、そのタイミングで操作や表示処理を実行
- 現在時刻を取得
- ControlPanel操作を検知
playevent なら演奏開始stopevent なら演奏停止selevent ならBPMの数値を変更(50,55,60,100,110,120 をloop)
- 1拍の1/8毎に、Trellis ON/OFF を検知して、MIDI データを送信
souce code
beat_trellis.py · Gistpython
import time
import busio
import board
import digitalio
from adafruit_trellis import Trellis
import usb_midi
import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from control_panel import ControlPanel
# 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 BeatTrellis:
"""
board
+---+---+
| A | B |
+---+---+
| C | D |
+---+---+
keymap
0 1 2 3 16 17 18 19
4 5 6 7 21 22 23 24
8 9 10 11 25 26 27 28
12 13 14 15 29 30 31 32
32 33 34 35 48 49 50 51
36 37 38 39 52 53 54 55
40 41 42 43 56 57 58 59
44 45 46 47 60 61 62 63
"""
def __init__(self, midi, i2c):
self.pressed_buttons_ = set()
self.on_notes_ = []
# Trellis size
self.ncol = 8
self.nrow = 8
# Trellis address
# [113, 114, 115, 119] [ 0x71, 0x72, 0x73, 0x77 ]
self.trellis_ = Trellis(i2c, [0x77, 0x72, 0x73, 0x71])
self.trellis_.auto_show = False
#
# midi
#
self.midi_ = midi
#
# (x, y) to keyno
#
self.rowcols_ = [
[ 0, 1, 2, 3, 16, 17, 18, 19 ],
[ 4, 5, 6, 7, 20, 21, 22, 23 ],
[ 8, 9, 10, 11, 24, 25, 26, 27 ],
[ 12, 13, 14, 15, 28, 29, 30, 31 ],
[ 32, 33, 34, 35, 48, 49, 50, 51 ],
[ 36, 37, 38, 39, 52, 53, 54, 55 ],
[ 40, 41, 42, 43, 56, 57, 58, 59 ],
[ 44, 45, 46, 47, 60, 61, 62, 63 ]
]
"""
Finger Drum
[ LC, LT, FT, HC ],
[ CH, HT, OH, CH ],
[ RC, SN, RM, RC ],
[ XX, BD, BD, XX ],
"""
self.notes_ = [
HC, # 0
LC, # 0
CH, # 2
HT, # 3
LT, # 4
RM, # 5
SN, # 6
BD, # 7
]
#
# keyno to (x, y)
#
self.keymap_ = []
# board A
for y in range(0, 4):
for x in range(0, 4):
self.keymap_.append([x, y])
# board B
for y in range(0, 4):
for x in range(0, 4):
self.keymap_.append([x+4, y])
# board C
for y in range(0, 4):
for x in range(0, 4):
self.keymap_.append([x, y+4])
# board D
for y in range(0, 4):
for x in range(0, 4):
self.keymap_.append([x+4, y+4])
# all off keypad led
for keyno in range(self.nrow* self.ncol):
self.light2(keyno, False)
self.show()
#
# midi
#
def midi_send(self, note, vol):
if vol == 0:
self.midi_.send(NoteOff(note, 0))
else:
self.midi_.send(NoteOn(note, vol))
def stroke(self, tune_no):
# note off prev on
for note in self.on_notes_:
self.midi_send(note, 0)
self.on_notes_ = []
# note on, light on key
for y in range(0, 8): # skip top line
keyno = self.point2keyno(tune_no, y)
if self.trellis_.led[keyno]:
# on
note = self.notes_[y]
self.midi_send(note, 100)
# save on notes
self.on_notes_.append(note)
def show(self):
self.trellis_.show()
def keyno2point(self, keyno):
return self.keymap_[keyno]
def point2keyno(self, x, y):
return self.rowcols_[y][x]
def light(self, x, y, val):
keyno = self.point2keyno(x, y)
self.trellis_.led[keyno] = val
def light2(self, keyno, val):
point = self.keyno2point(keyno)
self.trellis_.led[keyno] = val
def get_presses(self):
just_pressed, released = self.trellis_.read_buttons()
for keyno in just_pressed:
# print('pressed:', b)
if self.trellis_.led[keyno]:
# on -> off
bt.light2(keyno, False)
else:
# off -> on
bt.light2(keyno, True)
if len(just_pressed):
return True
return False
if __name__ == "__main__":
#
# MIDI
#
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)
#
# I2C init
#
sda = board.GP0
scl = board.GP1
i2c = busio.I2C(scl, sda)
#
# Trellis
#
bt = BeatTrellis(midi, i2c)
#
# ControlPanel
#
play_btn_pin = board.GP2
mode_btn_pin = board.GP3
sel_btn_pin = board.GP4
ctl = ControlPanel(play_btn_pin, mode_btn_pin, sel_btn_pin, i2c)
# display init
tune_no = 0
ctl.display_bpm()
ctl.display_tune(tune_no)
interval = ctl.get_bpm_interval()
now = time.monotonic_ns() + interval
next_time = now + interval
while True:
is_change = False
now = time.monotonic_ns()
event = ctl.get_event()
if event == "sel":
ctl.bpm_change()
ctl.display_bpm()
interval = ctl.get_bpm_interval()
elif event == "play":
next_time = now
elif event == "stop":
next_time = -1
is_change = bt.get_presses()
if is_change:
bt.show()
if next_time == -1 or next_time > now:
# skip to next tune time
continue
next_time += interval
# stroke midi note
bt.stroke(tune_no)
tune_no += 1
if tune_no >= bt.ncol:
tune_no = 0
ctl.display_tune(tune_no)demo
GarageBandのPianoRoll もこの通り。そこそこ正確なリズムになってる。

エンクロージャー
とりあえず、つくったけど、ControlPanel を収容するところまでそのうちに。

