Created
June 19, 2025 01:09
-
-
Save bakueikozo/aa1ced6d9d837bf969fe775d89a614a6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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