ESP32 WROOM 32E / M5Stamp Pico でA2DPとBLE-MIDIとHigh Speed Serial MIDI

## こんなことやらんでしょ… ##
ほんとこんなことやる人他に居ないと思うけど、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を使う。

```cpp
#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());
    }
*/
}
```