以前、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。

KORGのBLE_MIDI接続マニュアル

ドキュメントの Bluetooth MIDI に対応していないアプリケーションの場合 に関する部分を読んで、接続すればOK。

Mac の場合は、メニューの Bluetooth MIDI Connect から Connect Bluetooth MIDI Device をクリックすると、以下のように検出したデバイス一覧が表示される。

Connect ボタンをクリックして、GarageBand のTrackを表示すれば、音がでる。

プログラム

platformio.ini

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-XXXXXX

src/main.cpp

main.cpp · Gistcpp
#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回打ち込んだ。 スマートドラムより、打ち込みのほうが味があっていい。