Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bakueikozo/aa1ced6d9d837bf969fe775d89a614a6 to your computer and use it in GitHub Desktop.
Save bakueikozo/aa1ced6d9d837bf969fe775d89a614a6 to your computer and use it in GitHub Desktop.
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); // MIDI IN/OUT on Serial1
void setup() {
Serial.begin(115200);
MIDI.begin(MIDI_CHANNEL_OMNI);
MIDI.setHandleSystemExclusive(onSysEx);
sendGSReset();
delay(1000); // 安定待ち
sendIdentityRequest(); // 送信
//delay(3000); // 安定待ち
// MIDI.sendProgramChange(81, 1);
delay(5000); // 安定待ち
sendBulkDumpRequest(0x10);
}
unsigned long last = 0;
void loop() {
MIDI.read(); // 常にチェック
}
// Rolandチェックサム計算
byte calcRolandChecksum(byte* data, size_t len) {
byte sum = 0;
for (size_t i = 0; i < len; i++) sum += data[i];
return (128 - (sum % 128)) & 0x7F;
}
// Roland向けバルクリクエスト送信(例: アドレス 0x010000, サイズ 0x000010)
void sendBulkDumpRequest(byte deviceId) {
byte address[3] = {0x0c, 0x00, 0x02}; // 開始アドレス
byte size[3] = {0x00, 0x00, 0x00}; // データ長(16バイトなど)
byte checksumData[6];
for (int i = 0; i < 3; i++) {
checksumData[i] = address[i];
checksumData[i + 3] = size[i];
}
byte checksum = calcRolandChecksum(checksumData, 6);
byte sysex[] = {
0xF0, 0x41, deviceId, 0x42, 0x11,
address[0], address[1], address[2],
size[0], size[1], size[2],
checksum, 0xF7
};
MIDI.sendSysEx(sizeof(sysex), sysex, true);
Serial.println("SysEx Request Sent.");
}
void sendGSReset() {
byte gsReset[] = {
0xF0, 0x41, 0x10, 0x42, 0x12,
0x40, 0x00, 0x7F, 0x00,
0x41, // チェックサム
0xF7
};
MIDI.sendSysEx(sizeof(gsReset), gsReset, true);
Serial.println("GS Reset sent.");
}
// Identity Request を送る(broadcast)
void sendIdentityRequest() {
byte identityRequest[] = {
0xF0, 0x7E, 0x10, 0x06, 0x01, 0xF7
};
MIDI.sendSysEx(sizeof(identityRequest), identityRequest, true);
Serial.println("Sent Identity Request.");
}
struct PatchNameData {
uint8_t variationNumber;
uint8_t mapNumber;
uint8_t programNumber;
uint8_t zero;
char name[13]; // 12バイト+null終端用
};
void parsePatchNameBlock(byte* data, int length) {
const int recordSize = 16;
int count = length / recordSize;
Serial.print("Number of patch records: ");
Serial.println(count);
for (int i = 0; i < count; i++) {
PatchNameData patch;
int offset = i * recordSize;
patch.variationNumber = data[offset + 0];
patch.mapNumber = data[offset + 1];
patch.programNumber = data[offset + 2];
patch.zero = data[offset + 3];
memcpy(patch.name, data + offset + 4, 12);
patch.name[12] = '\0'; // 明示的にnull終端
// 表示(非ASCII → ピリオドなどに置き換え可能)
Serial.print("Patch ");
Serial.print(i);
Serial.print(": Var# ");
Serial.print(patch.variationNumber);
Serial.print(", Map# ");
Serial.print(patch.mapNumber);
Serial.print(", Prog# ");
Serial.print(patch.programNumber);
Serial.print(", Name: ");
for (int j = 0; j < 12; j++) {
char c = patch.name[j];
if (c >= 32 && c <= 126) {
Serial.print(c);
} else {
Serial.print('.'); // 非表示文字対策
}
}
Serial.println();
}
}
const int SYSEX_BUFFER_SIZE = 1024;
byte sysexBuffer[SYSEX_BUFFER_SIZE];
int sysexIndex = 0;
bool receivingSysEx = false;
void handleSysExChunk(byte* data, unsigned len) {
/*
Serial.print("Received SysExChunk: ");
for (int i = 0; i < len; i++) {
Serial.print(data[i], HEX);
Serial.print(" ");
}
Serial.println();*/
for (unsigned i = 0; i < len; i++) {
byte b = data[i];
if (b == 0xF0 && i == 0) {
// 開始:バッファ初期化
sysexIndex = 0;
receivingSysEx = true;
sysexBuffer[sysexIndex++] = b;
}
else if (receivingSysEx) {
// 中間 or 終端
if (sysexIndex < SYSEX_BUFFER_SIZE) {
sysexBuffer[sysexIndex++] = b;
}
if (b == 0xF7) {
// 終了 → 完成したSysExメッセージを処理
receivingSysEx = false;
processFullSysEx(sysexBuffer, sysexIndex);
sysexIndex = 0;
}
} else {
// それ以外のときは無視(またはログに出す)
}
}
}
void processFullSysEx(byte* data, int length) {
// デバッグ表示
Serial.print("Received SysEx: ");
for (int i = 0; i < length; i++) {
Serial.print(data[i], HEX);
Serial.print(" ");
}
Serial.println();
// Roland ID チェック
if (length > 10 && data[1] == 0x41) {
// Roland Device
uint32_t address = (data[5] << 16) | (data[6] << 8) | data[7];
if (address == 0x0002) {
int payloadLength = length - 9; // [F0 + Header(5)] + Addr(3) + Checksum + F7
byte* payload = &data[8];
parsePatchNameBlock(payload, payloadLength);
}
}
}
// Identity Reply を受信した時の処理
void onSysEx(byte* data, unsigned length) {
handleSysExChunk(data, length);
}
void test(byte* data,unsigned length){
/*
Serial.println("Received SysEx:");
for (unsigned i = 0; i < length; i++) {
if (data[i] < 0x10) Serial.print("0");
Serial.print(data[i], HEX);
Serial.print(" ");
}
Serial.println();*/
// Roland バルクダンプ形式かチェック
if (length >= 12 &&
data[0] == 0xF0 &&
data[1] == 0x41 &&
data[3] == 0x42 &&
data[4] == 0x12) {
// アドレス抽出
byte addr[3] = { data[5], data[6], data[7] };
unsigned long address = (addr[0] << 16) | (addr[1] << 8) | addr[2];
Serial.print("Roland Bulk Dump at address: ");
Serial.println(address, HEX);
if (address == 0x00003) {
int dataStart = 8;
int dataEnd = length - 2; // チェックサムとF7除外
int dataLen = dataEnd - dataStart;
if (dataLen >= 16) {
byte patchData[dataLen];
memcpy(patchData, data + dataStart, dataLen);
parsePatchNameBlock(patchData, dataLen);
} else {
Serial.println("Too short for patch name block.");
}
}
}
// Identity Reply のパース(簡略版)
if (length >= 15 && data[1] == 0x7E && data[3] == 0x06 && data[4] == 0x02) {
Serial.println("Identity Reply:");
Serial.print("Manufacturer ID: ");
Serial.print(data[5], HEX); Serial.print(" ");
if (data[5] == 0x00 && data[6] == 0x41) Serial.println("(Roland)");
else Serial.println();
Serial.print("Family Code: ");
Serial.print(data[7], HEX); Serial.print(" ");
Serial.println(data[8], HEX);
Serial.print("Model Number: ");
Serial.print(data[9], HEX); Serial.print(" ");
Serial.println(data[10], HEX);
Serial.print("Version: ");
Serial.print(data[11], DEC); Serial.print(".");
Serial.print(data[12], DEC); Serial.print(".");
Serial.print(data[13], DEC); Serial.print(".");
Serial.println(data[14], DEC);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment