M5StickC で、BLE-MIDI ドラム

目次
以前、RPi Pico でUSB MIDIを使ったMIDI ドラムを作ってみた。
今回は、M5StickC を利用して、BLE-MIDI でドラムを作ってみる。
BLE MIDI とは?
BLEを利用したMIDI I/F。対応している機器は少ないけど、アダプタを使えばMIDI機器ならだいたい対応できる。 無線なので、レイテンシーが気になるところ。
こんな記事を見つけた。
記事内では、USB接続で17msec。BLE接続だとBlueTooth自体のレーテンシーが 0から15msec なので、20msecから40msecのばらつきがでるらしい。 まあ、自分としてはそんな演奏レベルになりので、無問題。
UI仕様

BtnAをpressしている間は、音が鳴り、LEDが点灯する。
無線で使いたいので、バッテリーの残量も気になるところ。 初期画面ではバッテリーの電圧を表示する。
BLE-MIDI の接続方法
結論から。
KORGがリリースしている、 Bluetooth MIDI Connect というアプリを使えば、Mac、iOS、iPadOS の全てで完動。ありがとうKORG。
ドキュメントの Bluetooth MIDI に対応していないアプリケーションの場合 に関する部分を読んで、接続すればOK。
Mac の場合は、メニューの Bluetooth MIDI Connect から Connect Bluetooth MIDI Device をクリックすると、以下のように検出したデバイス一覧が表示される。
Connect ボタンをクリックして、GarageBand のTrackを表示すれば、音がでる。

プログラム
platformio.ini
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:m5stack-core-esp32]
platform = espressif32
lib_deps =
lathoub/USB-MIDI @ ^1.1.3
m5stack/M5StickC
h2zero/NimBLE-Arduino @ ^1.2.0
lathoub/BLE-MIDI@^2.1.0
board = m5stick-c
framework = arduino
upload_port = /dev/cu.usbserial-XXXXXXsrc/main.cpp
#include <BLEMIDI_Transport.h>
#include <M5StickC.h>
#include <hardware/BLEMIDI_ESP32_NimBLE.h>
//#include <hardware/BLEMIDI_ESP32.h>
//#include <hardware/BLEMIDI_nRF52.h>
//#include <hardware/BLEMIDI_ArduinoBLE.h>
#define MIDI_CHANNEL_NO 1
#define MIDI_DEVICE_NAME "StickDrum02"
// BLEMIDI_CREATE_DEFAULT_INSTANCE()
BLEMIDI_CREATE_INSTANCE(MIDI_DEVICE_NAME, MIDI);
// BLE connect
bool IsConnected = false;
// pitch of note
#define INIT_NOTE_PITCH 30
int CurrentNotePitch = INIT_NOTE_PITCH;
#define PIN_LED 10
// DISPLAY
void changeDisplay()
{
if (IsConnected)
{
M5.Lcd.setTextColor(DARKGREEN);
}
else
{
M5.Lcd.setTextColor(DARKGREY);
}
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(2);
M5.Lcd.setCursor(4, 8);
M5.Lcd.println(MIDI_DEVICE_NAME);
if (IsConnected)
{
M5.Lcd.setTextColor(DARKGREEN);
M5.Lcd.fillRect(0, 32, 256, 16, BLACK);
M5.Lcd.setCursor(4, 32);
M5.Lcd.printf("Pitch: %d", CurrentNotePitch);
}
}
// ====================================================================================
// Event handlers for incoming MIDI messages
// ====================================================================================
// -----------------------------------------------------------------------------
// Device connected
// -----------------------------------------------------------------------------
void OnConnected()
{
IsConnected = true;
changeDisplay();
}
// -----------------------------------------------------------------------------
// Device disconnected
// -----------------------------------------------------------------------------
void OnDisconnected()
{
IsConnected = false;
changeDisplay();
}
// -----------------------------------------------------------------------------
// Received note on
// -----------------------------------------------------------------------------
void OnNoteOn(byte channel, byte note, byte velocity)
{
}
// -----------------------------------------------------------------------------
// Received note off
// -----------------------------------------------------------------------------
void OnNoteOff(byte channel, byte note, byte velocity)
{
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void setup()
{
uint16_t vbat;
Serial.begin(9600);
// M5StickC init
M5.begin();
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
M5.Lcd.setRotation(3);
changeDisplay();
// check vat voltage
vbat = (uint16_t)(M5.Axp.GetVbatData() * 1.1 / 1000 * 100);
M5.Lcd.setCursor(4, 32);
M5.Lcd.printf("vbat: %4.2f V\r\n", (float)vbat / 100);
// MIDI init
MIDI.begin();
// BLE connect成功時のCallback
BLEMIDI.setHandleConnected(OnConnected);
// BLE disconnect時のCallback
BLEMIDI.setHandleDisconnected(OnDisconnected);
// 相手からのNoteOn Callbak
MIDI.setHandleNoteOn(OnNoteOn);
// 相手からのNoteOff CallBack
MIDI.setHandleNoteOff(OnNoteOff);
}
// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
void loop()
{
static bool prev_pressedA = 0;
MIDI.read();
if (!IsConnected)
{
delay(500);
return;
}
// pinの状態など更新
M5.update();
int pressedA = M5.BtnA.isPressed();
if (pressedA)
{
digitalWrite(PIN_LED, LOW);
}
else
{
digitalWrite(PIN_LED, HIGH);
}
if (M5.BtnB.wasPressed())
{
// change pitch of note 48 - 60
CurrentNotePitch += 1;
if (CurrentNotePitch > 60)
{
CurrentNotePitch = INIT_NOTE_PITCH;
}
changeDisplay();
}
if (M5.Axp.GetBtnPress())
{
// reset CurrentNotePitch
CurrentNotePitch = INIT_NOTE_PITCH;
changeDisplay();
}
if (prev_pressedA && !pressedA)
{
// NOTE OFF
MIDI.sendNoteOff(CurrentNotePitch + MIDI_CHANNEL_NO, 0, MIDI_CHANNEL_NO); // note 60, velocity 127 on channel 1
}
if (pressedA & !prev_pressedA)
{
// NOTE ON
MIDI.sendNoteOn(CurrentNotePitch + MIDI_CHANNEL_NO, 100, MIDI_CHANNEL_NO); // note 60, velocity 127 on channel 1
}
prev_pressedA = pressedA;
delay(2);
}演奏
やっぱり、ジョブスは偉い。無線がいい。
2個あれば、もっと楽しい。
iPhone のGarageBandで打ち込み
BLE-MIDIドラムを使って、iPhone のGarageBandで、打ち込みをしてみた。 3trackは、重ねて3回打ち込んだ。 スマートドラムより、打ち込みのほうが味があっていい。


