#概要
これまでは、M5StackでBLE MIDIコントローラを作ってきましたが、今回は、M5StickCでBLE MIDIコントローラを作ってみます。
#デモ
M5StickC x KORG Gadget Phoenix
(YouTube)
これまでの集大成のようなデモです。
- ボタンを押すとNote On、離すとNote Off
- 縦に傾けると音程変更(Pitch Bend)
- 横に動かすと音量変更(CC: 7)
#開発環境構築
「M5StickCを動かしてみる(itiB_S144様)」を参考にしてみてください。
BLEライブラリの追加は、前回の記事を参考にしてみてください。
#構成
- M5StickC
- KORG Gadget2 for iPad(iPhoneでも可)
#ソース
note_cc_pitch_imu_ble_midi_stickC_sample.zip
note_cc_pitch_imu_ble_midi_stickC_sample.ino
#include <M5StickC.h>
#include <BLEDevice.h>
#include <BLEServer.h>
//Note
const int NOTE_NUM = 60;
//BLE関連
//デバイス名
const char *DEVICE_NAME = "m5sC";
//BLE MIDIのサービスとキャラクタラスティック
const char *SERVICE_UUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
const char *CHAR_UUID = "7772E5DB-3868-4112-A1A9-F2669D106BF3";
//BLEデータ
BLEServer *pServer;
BLECharacteristic *pCharacteristic;
bool isConnected = false;
//MIDIデータ関連
//ccデータバッファ
int cc[128];
int pitch;
//IMUバッファ
float x, y, z;
int intX, intY;
//Buttonバッファ
bool pressed[2];
//Display関連
int cnt;
int circleSize;
int dspX, dspY;
//BLE MIDIデータ
unsigned char buff[] = {0x80, 0x80, 0xB0, 0x01, 0x64};
//BLE関連関数
// サーバーのコールバック関数
class cbServer: public BLEServerCallbacks {
void onConnect(BLEServer *pServer) {
isConnected = true;
M5.Lcd.fillScreen(BLACK);
M5.Lcd.drawCentreString("Connected", 40, 0, 1);
};
void onDisconnect(BLEServer *pServer) {
isConnected = false;
M5.Lcd.fillScreen(BLACK);
M5.Lcd.drawCentreString(DEVICE_NAME, 40, 80, 1);
}
};
//マッピングと制限を行う関数
int mapAndLimit(int value, int fromLow, int fromHigh, int toLow, int toHigh) {
int tmp, maxValue, minValue;
if(toLow < toHigh)
{
minValue = toLow;
maxValue = toHigh;
}
else
{
minValue = toHigh;
maxValue = toLow;
}
tmp = map(value, fromLow , fromHigh, toLow, toHigh);
tmp = tmp > maxValue ? maxValue : tmp;
tmp = tmp < minValue ? minValue : tmp;
return tmp;
}
//MIDI関連関数
//BLE Noteを送信
void notifyNote(int note, int velocity)
{
//noteをvelcityの強さで送信
buff[2] = 0x90;
buff[3] = note;
buff[4] = velocity;
//MIDIデータをNotify
pCharacteristic->setValue(buff, 5);
pCharacteristic->notify();
}
//BLE MIDIのCCデータを送信
void notifyCC(int ccNum, int value, int sensitivity)
{
if(abs(cc[ccNum] - value) > sensitivity)
{
//元情報にデータを入れておく
cc[ccNum] = value;
//valueをCC(ccNum)のデータにする
buff[2] = 0xb0;
buff[3] = ccNum;
buff[4] = value;
//MIDIデータをNotify
pCharacteristic->setValue(buff, 5);
pCharacteristic->notify();
}
}
//BLE MIDIのピッチベンドを送信
void notifyPitch(int value, int sensitivity)
{
if(abs(pitch - value) > sensitivity)
{
//元情報にデータを入れておく
pitch = value;
//valueをCC(ccNum)のデータにする
buff[2] = 0xe0;
buff[3] = value & 127;
buff[4] = value >> 7;
//MIDIデータをNotify
pCharacteristic->setValue(buff, 5);
pCharacteristic->notify();
}
}
void setup(){
int i;
// M5Stackの初期化
M5.begin();
// MPU6886の初期化
M5.MPU6886.Init();
//CCバッファの初期化
for(i = 0; i < 128; i++)
{
cc[i] = 64;
}
//Pitchバッファの初期化
pitch = 8192;
//Display関連の初期化
cnt = 0;
circleSize = 2;
dspX = 40;
dspY = 80;
//Btnの初期化
for(i = 0; i < 2; i++)
{
pressed[i] = false;
}
// BLE初期化
BLEDevice::init(DEVICE_NAME);
// サーバーの作成
BLEServer *pServer = BLEDevice::createServer();
// コールバック関数の設定
pServer->setCallbacks(new cbServer());
// サービスの作成
BLEService *pService = pServer->createService(SERVICE_UUID);
// キャラクタリスティックの作成
pCharacteristic = pService->createCharacteristic(
CHAR_UUID,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
// サービスの開始
pService->start();
//アドバタイジングの開始
pServer->getAdvertising()->addServiceUUID(SERVICE_UUID);
pServer->getAdvertising()->start();
//ディスプレイにデバイスネームを表示
M5.Lcd.fillScreen(BLACK);
M5.Lcd.drawCentreString(DEVICE_NAME, 40, 80, 1);
}
void loop() {
if (isConnected)
{
//BtnAでNote On
if(M5.BtnA.read() && !pressed[0])
{
pressed[0] = true;
notifyNote(NOTE_NUM, 100);
}
//BtnAを離したらNote Off
else if(!M5.BtnA.read() && pressed[0])
{
pressed[0] = false;
notifyNote(NOTE_NUM, 0);
}
M5.update();
// 加速度を取得する
// X、Y、Zの値を得る
M5.MPU6886.getAccelData(&x, &y, &z);
x = x * 128.0;
y = y * 16383.0;
intX = mapAndLimit((int)x, 64, -64, 0, 127);
intY = mapAndLimit((int)y, 8192, -8192, 0, 16383);
//NoteOnの状態のときのみMIDI情報を送る
if(pressed[0])
{
//IMUのデータをCC1, CC71に送信
notifyCC(7, intX, 1);
notifyPitch(intY, 1);
}
//100msごとDisplay更新
if(cnt == 0)
{
M5.Lcd.fillScreen(BLACK);
M5.Lcd.drawCentreString("Connected", 40, 0, 1);
if(pressed[0])
{
circleSize = 7;
}
else
{
circleSize = 2;
}
dspX = mapAndLimit((int)x, 64, -64, 0, 80);
dspY = mapAndLimit((int)y, -8192, 8192, 0, 160);
M5.Lcd.drawCircle(dspX, dspY, circleSize, RED);
}
cnt = (cnt+1) % 10;
// 少し休ませる
delay(10);
}
}
#解説
今後、行いたいと思います。
#課題
どことなく音の変化がデジタル臭いんですよね。delayを5msにすると、音の変化がリアルタイムで動かなくなってしまってダメでした。
解消には、もう少しMIDIの規格、BLE MIDIの規格を読み込んで、MIDIのランニングステータスの利用、BLE MIDIのタイムスタンプの利用、一度のBLEパケットに複数のMIDI信号を乗せる、などの工夫で必要ですね。
#M5Stackとの差分
プログラムを組む上では、大きな差分は無かったですね。
M5StickCの良いところ
- 安い(WLAN/BT, IMU付いてArduino UNOより安いのは異常)
- IMU標準搭載 & M5Stack Grayのものよりライブラリが使いやすい
- 電源ボタンが素直
- USBケーブルを抜いてもリセットされない
- 電池の持ちも良さそう
M5Stackの良いところ
- ディスプレイが大きい
- IOが多い
- リセットボタン搭載