こんなことやらんでしょ…
ほんとこんなことやる人他に居ないと思うけど、Teensy4.1でシンセサイザーを作る際のサブCPUとして最適だと思うESP32。さらにM5Stamp Picoだと小さくてとてもいい。Teensy4.1にはBluetoothもWiFiもないので、それを補ってもらうためのもの。
Raspberry Pi Zero 2の方が安いし一発で済むかもだけど、ベアメタルで使うの結構ハードル高いから。てかZero 2 + ESP32もいいと思う。
ざっくりとした接続方法
- ESP32はI2Sのマスターにする(本当はスレーブで動かしたかったがどうやらサポートされてない様子)
- A2DPで受けたオーディオをI2SでTeensy4.1に送り、Teensy4.1の中で他の音とミックスして出力
- BLE-MIDIで受けたものをTeensy4.1に送る
- Teensy4.1から送られたMIDIデータをBT-MIDIに流す
- ESP32側はSerial2、Teensy4.1側はSerial4を使ってる。
これでBLE-MIDI対応のシンセサイザーが作れるし、iPhoneから送ったオーディオも扱える。ESP32の使い方としては非常に勿体無い。
ESP32 <-> Teensy4.1間のMIDI送受信はMIDIライブラリを使いつつ速度を1.5Mbpsに上げている。BLE-MIDIがどんだけガシガシ送ってくるのか来ないのかわかんないけど。実際使ってみると特に問題なく動く。
ソースコード
Arduino IDEを使う。
#define USE_M5ATOM 1
#if USE_M5ATOM
#include <M5Atom.h>
#endif
#include <AudioTools.h>
#include <BluetoothA2DPSink.h>
#include <BLEMIDI_Transport.h>
#include <hardware/BLEMIDI_ESP32.h>
// For ESP32 onboard LED
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif
// A2DP
AudioInfo info(44100, 2, 32);
I2SStream out;
NumberFormatConverterStream convert(out);
BluetoothA2DPSink a2dp_sink(convert);
bool isA2dpConnected = false;
// MIDI
#define TWVMIDI_RXD 36
#define TWVMIDI_TXD 18
BLEMIDI_CREATE_INSTANCE("TWV2000M", btMidi);
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, twvMidi);
bool isBleMidiConnected = false;
void updateLed()
{
#if USE_M5ATOM
M5.dis.drawpix(0, (isBleMidiConnected? 0x0000ff: 0x000000) | (isA2dpConnected? 0xff0000: 0x000000));
#else
digitalWrite(LED_BUILTIN, isBleMidiConnected? HIGH: LOW);
#endif
}
void setup() {
#if USE_M5ATOM
M5.begin(true, false, true);
M5.dis.drawpix(0, 0x000000);
#endif
Serial.begin(115200);
//delay(500);
//AudioToolsLogger.begin(Serial, AudioLogger::Info);
Serial.println("Setup start");
// Configure i2s to use 32 bits
auto cfg = out.defaultConfig();
cfg.copyFrom(info);
cfg.pin_bck = 26;
cfg.pin_ws = 25;
cfg.pin_data = 22;
out.begin(cfg);
convert.begin(16, 32); // Convert from 16 to 32 bits
a2dp_sink.set_auto_reconnect(true, 1000);
a2dp_sink.start("TWV2000M");
a2dp_sink.set_on_connection_state_changed([](esp_a2d_connection_state_t state, void* ctx) {
Serial.printf("A2DP connection state changed: %d\n", state);
if (state == ESP_A2D_CONNECTION_STATE_CONNECTED) isA2dpConnected = true;
else if (state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) isA2dpConnected = false;
updateLed();
});
a2dp_sink.set_on_audio_state_changed([](esp_a2d_audio_state_t state, void* ctx) {
Serial.printf("A2DP audio state changed: %d\n", state);
});
// Sub MIDI
Serial2.begin(1500000, SERIAL_8N1, TWVMIDI_RXD, TWVMIDI_TXD); // Specify pin number
twvMidi.begin(); // I think this makes serial speed to 31250bps
twvMidi.turnThruOff(); // Important!!! Disable MIDI THRU
Serial2.updateBaudRate(1500000);
twvMidi.setHandleSystemExclusive([](byte* data, unsigned len) {
Serial.printf("ESP32 Serial-MIDI len=:%d ", len);
for (int i = 0; i < len; i++)
{
Serial.printf("%02x ", data[i]);
}
Serial.println();
btMidi.sendSysEx(len, data, true);
});
BLEbtMidi.setHandleConnected([]() {
isBleMidiConnected = true;
updateLed();
});
BLEbtMidi.setHandleDisconnected([]() {
isBleMidiConnected = false;
updateLed();
});
btMidi.begin();
btMidi.setHandleNoteOn([](byte channel, byte note, byte velocity) {
//Serial.printf ("Note on: ch=%d, note=%d, vel=%d\n", channel, note, velocity);
twvMidi.sendNoteOn(note, velocity, channel);
});
btMidi.setHandleNoteOff([](byte channel, byte note, byte velocity) {
//Serial.printf ("Note off: ch=%d, note=%d, vel=%d\n", channel, note, velocity);
twvMidi.sendNoteOff(note, velocity, channel);
});
btMidi.setHandleControlChange ([](byte channel, byte number, byte value) {
//Serial.printf ("CC: ch=%d, number=%d, vzlue=%d\n", channel, number, value);
twvMidi.sendControlChange(number, value, channel);
});
btMidi.setHandleProgramChange ([](byte channel, byte number){
twvMidi.sendProgramChange(number, channel);
});
btMidi.setHandleAfterTouchChannel ([](byte value, byte channel){
twvMidi.sendAfterTouch(value, channel);
});
btMidi.setHandleSystemExclusive([](byte* data, unsigned len) {
Serial.printf("ESP32 BLE-MIDI len=%d: ", len);
for (int i = 0; i < len; i++)
{
Serial.printf("%02x ", data[i]);
}
Serial.println();
twvMidi.sendSysEx(len, data, true);
});
}
void loop() {
btMidi.read();
twvMidi.read();
/*
static uint32_t last = 0;
if (millis() - last > 1000) {
last = millis();
Serial.printf("Free Heap: %d\n", ESP.getFreeHeap());
}
*/
}