はじめに
こんにちは。
先日、電子キーボードの鍵盤部分を流用して、MIDIキーボードを自作しました。
今回は、作ろうと思ったきっかけから、使用した機材、実装した機能、開発中に苦労した点、実際に作ってみた感想までをメモがてら記事に残しておこうと思います。
なお、今回は無料版のCopilotやGeminiに相談しながら、実現方法の検討や機材の選定、ソースコードの調整などを進めました。
1. 自作しようとした動機
私はDTMのために32鍵のMIDIキーボードを購入して使っていました。やや鍵盤数が少ないため、音の確認をするときなどに少し不便さを感じていました。
高校時代の友人と飲んでいた際、近況報告の流れでDTMをしていること、MIDIキーボードの鍵盤数に不満があることを話したところ、友人から「家に54鍵の電子キーボードが余っている」という話が。
ありがたく譲ってもらうことになったのですが、譲ってもらう際にMIDI対応かどうかを確認しておらず、見事にMIDI非対応のものでした。
正直頭を抱えましたが、せっかく54鍵のキーボードを手に入れたので、鍵盤部分を流用してMIDIキーボードを自作できないかと考えました。
2. 実現したい機能
私のMIDIキーボードの使い方として、基本的には音高や音色、メロディの簡単な確認がメインだったので、ベロシティなどは実装を見送り、シンプルな構成としました。また、使用するのは鍵盤の基盤のみのため、鍵盤操作だけで完結することが必須でした。
具体的には、以下の2つの機能を実現することにしました。
鍵盤に対応したMIDI信号の出力
言わずもがな、鍵盤を押すと、その鍵盤に対応した音程のMIDI信号を出力する機能です。DAW上で通常のMIDIキーボードと同じように演奏できることがゴールです。
鍵盤によるオクターブシフト
左右の端にある鍵盤を操作することで、オクターブを切り替えられる機能です。
これにより、鍵盤数が限られていても、演奏する音域を変更できるようにしました。
3. 開発方法
今回は、無料版のCopilotと相談しながら開発を進めました。
具体的には、以下のような作業でAIを活用しました。
- 鍵盤部分を流用してMIDIキーボードを実現できるかの検討
- 必要なマイコンや周辺部品の選定
- 鍵盤の入力を検知する方法の検討
- MIDI信号を出力するためのソースコードの作成・調整
- 動作確認で問題が発生した際の原因の切り分け
また、GoogleのAIにも情報を提示してもらい、実現方法を検討しました。
組込み開発では、ソフトウェアだけでなく、実際の回路や部品の構成も考える必要があります。そのため、AIとのやり取りを通して、実現可能性を確認しながら少しずつ形にしていきました。
4. 使用した機材・ソフトウェア
今回使用したものは以下のとおりです。
| 機材・ソフトウェア | 用途 |
|---|---|
| Melody 54(電子キーボード) | 鍵盤部分の流用 |
| RP2040搭載基板(はんだ付け済み) | 鍵盤入力の処理、MIDI信号の出力 |
| ハーネス(フレキケーブル用、7pin,8pin) | 鍵盤側の配線への接続 |
| ジャンパワイヤ(メス-メス) | 基板と配線の接続 |
| CircuitPython | マイコン上で動作するプログラムの開発 |
| Tera Term | デバッグ時の情報確認 |
マイコンにはRP2040を搭載した基板を使用し、プログラムはCircuitPythonで実装しました。
また、配線や動作の確認にはTera Termを使用しました。
5. 実装
import time
import board
import digitalio
import usb_midi
from adafruit_midi import MIDI
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
# ====== 行列ピン設定 ======
ROW_PINS = [board.GP0, board.GP1, board.GP2, board.GP3, board.GP4, board.GP5, board.GP6] # 7本
COL_PINS = [board.GP7, board.GP8, board.GP9, board.GP10, board.GP11, board.GP12, board.GP13, board.GP14] # 8本
# ====== MIDIノート番号テーブル ======
note_table = [
[43, 42, 41, 40, 39, 38, 37, 36], # ROW0
[51, 50, 49, 48, 47, 46, 45, 44], # ROW1
[59, 58, 57, 56, 55, 54, 53, 52], # ROW2
[67, 66, 65, 64, 63, 62, 61, 60], # ROW3
[75, 74, 73, 72, 71, 70, 69, 68], # ROW4
[83, 82, 81, 80, 79, 78, 77, 76], # ROW5
[91, 90, 89, 88, 87, 86, 85, 84], # ROW6
]
rows = []
cols = []
# ROW は HIGH→LOW に切り替えてスキャン
for p in ROW_PINS:
io = digitalio.DigitalInOut(p)
io.direction = digitalio.Direction.OUTPUT
io.value = True
rows.append(io)
# COL は入力+プルアップ
for p in COL_PINS:
io = digitalio.DigitalInOut(p)
io.direction = digitalio.Direction.INPUT
io.pull = digitalio.Pull.UP
cols.append(io)
# MIDI 出力
midi = MIDI(midi_out=usb_midi.ports[1], out_channel=0)
pressed = [[False]*len(note_table[0]) for _ in range(len(note_table))]
octave=0
while True:
for r_idx, r in enumerate(rows):
r.value = False
time.sleep(0.001)
for c_idx, c in enumerate(cols):
is_pressed = not c.value
if is_pressed and not pressed[r_idx][c_idx]:
if note_table[r_idx][c_idx] == 89 and octave < 3:
octave += 1
if note_table[r_idx][c_idx] == 36 and octave >= -1:
octave -= 1
midi.send(NoteOn(note_table[r_idx][c_idx]+octave*12, 100))
pressed[r_idx][c_idx] = True
elif (not is_pressed) and pressed[r_idx][c_idx]:
midi.send(NoteOff(note_table[r_idx][c_idx]+octave*12, 0))
pressed[r_idx][c_idx] = False
r.value = True
time.sleep(0.001)
実装を進めるにあたっては、AIに相談しながらソースコードを調整し、実機で動作を確認するという流れを繰り返しました。
今回の実装では、大きく分けて「鍵盤の入力を検知する処理」と「入力に応じたMIDI信号を出力する処理」が必要になります。
鍵盤が押されたことを検知し、その鍵盤に対応する音程を判定したうえで、MIDI信号として出力する、という流れです。
鍵盤の基板は、入力側8本、出力側7本の信号線を組み合わせた、最大56通りの行列として構成されていました。
この行列の対応関係を特定するため、Tera Termを使用して入力側と出力側の信号を1つずつ確認し、どの組み合わせが各鍵盤に対応するのかを調べました。こうして得られた結果をもとに、鍵盤と行列の対応関係を定義しました。
左右端の鍵盤については、押下を検知するとoctaveの値を増減させることで、オクターブシフトを実現しました。
一方、この実装には課題も残っています。他の鍵盤を押した状態で端の鍵盤を押すと、オクターブの切り替えによって鍵盤の判定に使用する値が変化します。その結果、先に押していた鍵盤を離した際に、正しくOFF判定できず、音が鳴り続けてしまうという問題が発生します。
本来であれば、鍵盤を押した時点の音程や状態を保持するなどの対策が必要ですが、今回は自分で使用することを前提とし、端の鍵盤と他の鍵盤を同時に操作しないことで回避することにしました。
完成
6. 開発で苦労した点
期待どおりに動作しなかったときの原因の切り分けに苦労しました。
例えば、鍵盤を押しても正しく信号が出力されない場合、原因としてさまざまな可能性が考えられます。
- 配線の接触不良
- 鍵盤からの入力を正しく検知できていない
- 入力と音程の対応付けが間違っている
- ソースコードに問題がある
- MIDI信号の出力処理に問題がある
特に、物理的な配線とソフトウェアの両方が関係するため、どこに問題があるのかを判断するのが難しかったです。
実際の入出力や配線の状態も確認しながら、少しずつ問題を切り分けていく必要があると改めて感じました。
7. 作ってみた感想
実際に自作してみて、組込み開発ならではの面白さを改めて感じました。
組込みソフトウェアは、物理的な入力を受け取り、その結果を実際の機器や外部のシステムに出力します。そのため、プログラムを書いて終わりではなく、実機で動作を確認しながら開発を進めていきます。
今回の開発でも、鍵盤の入力を検知できたとき、入力に応じた音程を設定できたとき、そして実際に音が鳴ったときなど、段階ごとに感動がありました。
最初は思いどおりに動かなくても、原因を調べて修正し、少しずつ機能が実現していく。その過程がとても楽しかったです。
特に、自分で組み込んだプログラムによって、目の前の鍵盤から実際に音が出るという体験は、普段のソフトウェア開発とはまた違った面白さがありました。
また、今回はAIに相談しながら開発を進めましたが、単にコードを生成してもらうだけではなく、実現可能性を検討したり、実機で発生した問題を切り分けたりする必要がありました。AIを活用しつつ、実際の動作を確認しながら完成に近づけていく経験も得られました。
8. おわりに
マイコンを使って自分で機能を実装することで、DTMに使えるキーボードを作るという目標に挑戦できました。
今回の開発を通して、組込み開発の面白さを改めて実感できたのが一番の収穫です。
最後まで読んでいただき、ありがとうございました。



