信頼できる発行元

百均とジャンクコーナーと時々中華通販

ESP-32でエレフエのBLE MIDIをハードシンセにつなぐ 結局改造した

ソプラノリコーダーのようなサイズ感と誰でも扱える簡単操作が売りの電子楽器「エレフエ」を購入した。全10種の音色と内蔵のスピーカーはとてもバランス良くできていて、単体で演奏しても十分に楽しめる。

今回はエレフエをハードシンセに繋ぐためのBLE MIDI Centralとなる変換器をESP-32のマイコンボードで制作した。でもって、本体に手を加えて低遅延なMIDI出力を得る方法も見つけたので、順番に紹介しよう。

エレフエの紹介

この記事はあくまでBLE MIDIのコントローラーをハードシンセに繋ぐ変換器の制作がメインだが、せっかくなのでエレフエについても紹介しよう。

エレフエ(Elefue)は台湾タホーン社の電子リコーダーで、いわゆるウインドシンセをより手軽にした電子楽器である。値段も14000円くらいで、電子楽器としてはかなり安い部類。運指はソプラノリコーダーと同じものと、独自のものを選ぶことができる。

ブレスセンサーにより息を吹く強さを検知しているので、音の強弱を表現したり、タンギングもできる。

演奏音は本体スピーカーか、ヘッドホンから出力されるので、周りに気を遣って静かに演奏することもできる。

基本はリコーダー準拠なものの、10種の音色が選べたり、3オクターブの音域を持っていたり、トランスポーズができるなど細かく見ていくと地味に便利に出来ている。流石に5万円ほどのウインドシンセには表現力や質で敵わないが、それでも独自の魅力が感じられる良い楽器だと思う。

おまけにBLE MIDIにも対応しているから、iPhoneでなんらかのシンセアプリを立ち上げて接続すれば多種多様で本格的な音も楽しめる。

ちなみに、BLE MIDIを5ピンDINの従来の機器と接続するハードウェアとしてCME WIDI Masterというものがある。7千円ほどと決して安くはないが、評判は良い模様。

BLE MIDIを変換する機器はほかにもあるようだが、ざっと調べた限りCentralとPeripheral両方になれるという記述があるのはWIDI Masterだけだった。BLE MIDIコントローラーをハードシンセに接続するにはCentralとして振舞う必要がある。

やっていく

必要なもの
  • ESP-32マイコンボード
  • 3.5mm TRS or 5ピンDIN
  • 抵抗器33Ω2本

よくあるDOIT ESP32 DEVKIT V1相当のボードでOK。MIDI OUTには5ピンDINではなく、ガジェット系シンセとの相性が良い3.5mmのステレオプラグを使用した。

結線

DINコネクタまたは3.5mmTRSコネクタを次の通り結線。

  • TX2 -[33Ω]- Sink(5またはTip)
  • GND - Shield(2またはSleeve)
  • Vcc(3.3V) -[33Ω]- Source(4またはRing)
スケッチをアップロード

Arduino IDEでプログラムを書き込む。大改修で近代化が図られた最新バージョンはとても快適にコードが書けるようになったので、久しぶりの方は要チェック。

BLEMIDIライブラリのサンプルコードを適当にいじってそれっぽくしたのがこれ。ボードマネージャーからESP-32のインストールとライブラリArduino-BLE-MIDIのインストールが必要。

#include <Arduino.h>
#include <BLEMIDI_Transport.h>
#include <hardware/BLEMIDI_Client_ESP32.h>

// トランスポートのインスタンス名は「BLE + BLEMIDIのインスタンス名」になる
// デバイス名を空にすると最初に見つけたBLE MIDI Peripheralに接続する
BLEMIDI_CREATE_INSTANCE("", midiBle)

MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, midiUart);

void ReadCB(void *parameter);

bool isConnected = false;

void setup()
{
  Serial.begin(115200);
  Serial.println("MIDI Central");
  
  midiUart.begin();
  midiBle.begin();
  
  // (同じインターフェースのIN-OUTの)スルーを無効化
  midiBle.turnThruOff();
  midiUart.turnThruOff();

  // 全チャンネルのメッセージを受信
  midiBle.setInputChannel(MIDI_CHANNEL_OMNI);
  midiUart.setInputChannel(MIDI_CHANNEL_OMNI);

  xTaskCreatePinnedToCore(ReadCB,           //See FreeRTOS for more multitask info  
                          "MIDI-READ",
                          3000,
                          NULL,
                          1,
                          NULL,
                          1);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  BLEmidiBle.setHandleConnected(OnConnected);
  BLEmidiBle.setHandleDisconnected(OnDisconnected);
  Serial.println("ready");
}

void loop()
{

}

void ReadCB(void *parameter)
{
  for (;;)
  {
    if(midiUart.read()&&isConnected){
      midiBle.send(midiUart.getType(), midiUart.getData1(), midiUart.getData2(), midiUart.getChannel());
      //Serial.println("message from keyboard.");
    }

    if(midiBle.read()&&isConnected){
      midiUart.send(midiBle.getType(), midiBle.getData1(), midiBle.getData2(), midiBle.getChannel());
      //Serial.println("message from host.");
    }
    // vTaskDelay(1 / portTICK_PERIOD_MS); //Feed the watchdog of FreeRTOS.
  }
}

void OnConnected() {
  isConnected = true;
  digitalWrite(LED_BUILTIN, HIGH);
  Serial.println("ble connected.");
}

void OnDisconnected() {
  isConnected = false;
  digitalWrite(LED_BUILTIN, LOW);
  Serial.println("ble disconnected.");
}
動作チェック

マイコンボードをモバイルバッテリーやPC等のUSB電源に接続して、エレフエのBluetoothをオンにして少し待つと自動的に接続される。接続中はマイコンボードのLEDが青く光る模様。

MIDI OUTをシンセに接続して吹いてみるとしっかりと音が鳴る。

結局改造する

BLE MIDIではやはり遅延が気になる。分解して片っ端からオシロのプローブをあてて信号を探しだした。もちろん、分解や改造は保証の対象外となるだけでなく、故障や事故のリスクもあるので自己責任の上。

BLE MIDI接続時にBLEモジュールの一番端のピンに115200bpsのUARTでMIDIが出ていることがわかったので、こちらを横取りできるように改造を施す。microUSBのD+D-が未結線なのでこちらにUARTを結線し、microUSBコネクタと適当な線材でマイコンボードのRXに接続した。なお、スケッチアップロード時は外す必要がある。

先ほどのコードをちょっといじる。信号はBLE接続時にのみ出ることがわかってるので、BLE MIDIのセントラルとなる機能は残してある(接続だけでデータは見てない)。

#include <Arduino.h>
#include <BLEMIDI_Transport.h>
#include <hardware/BLEMIDI_Client_ESP32.h>

// トランスポートのインスタンス名は「BLE + BLEMIDIのインスタンス名」になる
// デバイス名を空にすると最初に見つけたBLE MIDI Peripheralに接続する
BLEMIDI_CREATE_INSTANCE("", midiBle)

struct midi115200 : public midi::DefaultSettings
{
  static const long BaudRate = 115200;
};
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial, midiUartRx, midi115200);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, midiUart);

void ReadCB(void *parameter);

bool isConnected = false;

void setup()
{
  
  midiUart.begin();
  midiBle.begin();
  midiUartRx.begin();
  
  // (同じインターフェースのIN-OUTの)スルーを無効化
  midiBle.turnThruOff();
  midiUart.turnThruOff();
  midiUartRx.turnThruOff();

  // 全チャンネルのメッセージを受信
  midiBle.setInputChannel(MIDI_CHANNEL_OMNI);
  midiUart.setInputChannel(MIDI_CHANNEL_OMNI);
  midiUartRx.setInputChannel(MIDI_CHANNEL_OMNI);

   xTaskCreatePinnedToCore(ReadCB,           //See FreeRTOS for more multitask info  
                          "MIDI-READ",
                          3000,
                          NULL,
                          1,
                          NULL,
                          1);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);

  BLEmidiBle.setHandleConnected(OnConnected);
  BLEmidiBle.setHandleDisconnected(OnDisconnected);
}

void loop()
{

}

void ReadCB(void *parameter)
{
  for (;;)
  {
    if(midiUartRx.read()&&isConnected){
      midiUart.send(midiUartRx.getType(), midiUartRx.getData1(), midiUartRx.getData2(), midiUartRx.getChannel());
    }
    midiUart.read();
    midiBle.read();
  }
}

void OnConnected() {
  isConnected = true;
  digitalWrite(LED_BUILTIN, HIGH);
}

void OnDisconnected() {
  isConnected = false;
  digitalWrite(LED_BUILTIN, LOW);
}

さいごに

軽い気持ちで始めたエレフエハックだが、つい本体改造にまで手が出てしまった。もちろん、BLE MIDIでも表現の幅は狭まるものの十分楽しめる*1ので、軽率な改造はおすすめしない。もっとも、エレフエ内蔵の音でも十分気持ちがい。

*1:BLE MIDI自体の限界ではなく、ESP32のBLEスタックかライブラリが遅い説があり、ちゃんとした機材ではもっといい感じかも