diff --git a/OSC2MIDI.ino b/OSC2MIDI.ino index 46fc262..99f88a7 100644 --- a/OSC2MIDI.ino +++ b/OSC2MIDI.ino @@ -42,6 +42,7 @@ #define KRATE_CHECK_WRITE_STATE 10000 #define LAST_USAGE_TIMER 5000 #define FORMAT_SPIFFS_IF_FAILED true +#define MIDI_SOLO_VOLUME 100 void OSCToMidiCC(OSCMessage &msg, int offset); void OSCMixerMuteToMidiCC(OSCMessage &msg, int offset); @@ -65,7 +66,9 @@ HardwareSerial midi1(2); // RX: 16, TX: 17 #endif SoftwareSerial midi2; bool ap_mode_state; -int8_t midistate[16 * 128]; +int8_t midistate_cc[16 * 128]; +int8_t midistate_mute[16]; +int8_t midistate_solo[16]; bool last_reset_ap_check = false; looper sched; bool write_state = false; @@ -82,7 +85,9 @@ void setup() //Serial.print("FORMAT SPIFFS..."); SPIFFS.format(); Serial.println("done."); while (1); - memset(midistate, -1, 16 * 128); + memset(midistate_cc, -1, 16 * 128); + memset(midistate_mute, -1, 16); + memset(midistate_solo, -1, 16); pinMode(AP_DATA_RESET_PIN, INPUT_PULLDOWN); pinMode(AP_MODE_PIN, INPUT_PULLDOWN); @@ -203,7 +208,7 @@ void setup() listDir(SPIFFS, "/", 1); read_midistate(1); - show_midi_state(); + show_midistate(); DEBUG_MSG("\n"); } @@ -288,8 +293,16 @@ void OSCToMidiCC(OSCMessage & msg, int offset) value = round(msg.getFloat(0)); value = value > 127 ? 127 : value; DEBUG_MSG("MSG: %s\tChannel: %u\t\tCC: %u\tValue: %u\n", address, midichannel, cc, value); - MIDI1.sendControlChange(cc, value, midichannel); - change_midi_state(midichannel, cc, value); + if (cc == 7) + { + if (midistate_mute[cc - 1] < 1 && midistate_solo[cc - 1] < 1) + MIDI1.sendControlChange(cc, value, midichannel); + else + DEBUG_MSG("No volume change - only state change to %d\n", value); + } + else + MIDI1.sendControlChange(cc, value, midichannel); + change_midistate_cc(midichannel, cc, value); } else if (msg.size() == 2 && msg.isFloat(0) && msg.isFloat(1)) { @@ -299,21 +312,21 @@ void OSCToMidiCC(OSCMessage & msg, int offset) value = constrain(value, 0, 127); DEBUG_MSG("MSG: %s\tChannel: %u\t\tCC: %u\tValue: %u\n", address, midichannel, cc, value); MIDI1.sendControlChange(cc, value, midichannel); - change_midi_state(midichannel, cc, value); + change_midistate_cc(midichannel, cc, value); cc = getVar(address, 2); value = round(msg.getFloat(1)); value = constrain(value, 0, 127); DEBUG_MSG("MSG: %s\tChannel: %u\t\tCC: %u\tValue: %u\n", address, midichannel, cc, value); MIDI1.sendControlChange(cc, value, midichannel); - change_midi_state(midichannel, cc, value); + change_midistate_cc(midichannel, cc, value); } else { - DEBUG_MSG("Cannot handle: % s\n", address); + DEBUG_MSG("Cannot handle: %s\n", address); } } -void OSCMixerMuteToMidiCC(OSCMessage &msg, int offset) +void OSCMixerMuteToMidiCC(OSCMessage & msg, int offset) { char address[100] = { 0 }; uint8_t value; @@ -330,14 +343,36 @@ void OSCMixerMuteToMidiCC(OSCMessage &msg, int offset) if (value == 1) MIDI1.sendControlChange(7, 0, midichannel); else - MIDI1.sendControlChange(7, midistate[(midichannel - 1) * 128 + 6], midichannel); - //change_midi_state(midichannel, cc, value); + MIDI1.sendControlChange(7, midistate_cc[(midichannel - 1) * 128 + 6], midichannel); + + change_midistate_mute(midichannel, value); } } -void OSCMixerSoloToMidiCC(OSCMessage &msg, int offset) +void OSCMixerSoloToMidiCC(OSCMessage & msg, int offset) { - ; + char address[100] = { 0 }; + uint8_t value; + uint8_t midichannel; + + msg.getAddress(address, offset, sizeof(address)); + + if (msg.size() == 1 && msg.isFloat(0)) + { + // Single or multi control with sending one value + midichannel = getVar(address, 0); + value = constrain(msg.getFloat(0), 0, 1); + DEBUG_MSG("MixerSolo MSG: %s\t Channel: %u\tMute \tValue: %u\n", address, midichannel, value); + /* + if (value == 1) + MIDI1.sendControlChange(7, 0, midichannel); + else + MIDI1.sendControlChange(7, midistate_cc[(midichannel - 1) * 128 + 6], midichannel); + */ + + change_midistate_solo(midichannel, value); + set_midi_solo_mode(); + } } void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val) @@ -354,7 +389,66 @@ void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val) DEBUG_MSG("MidiCCToOsc: %s %f\n", buffer, val); - change_midi_state(channel, number, val); + change_midistate_cc(channel, number, val); + + if (clientIP) + { + OSCMessage msg = OSCMessage(buffer); + msg.add(val); + + udp.beginPacket(clientIP, UDP_SEND_PORT); + msg.send(udp); + udp.endPacket(); + + msg.empty(); + } + else + { + DEBUG_MSG("No client IP.\n"); + } +} + +void MidiMuteToOSC(uint8_t channel, uint8_t val) +{ + char buffer[1024]; + + if (channel < 1 && channel > 16) + return; + val = constrain(val, 0, 1); + + snprintf(buffer, sizeof(buffer), "/midi/mixer/mute/%d", channel); + + DEBUG_MSG("MidiMuteToOsc: %s %f\n", buffer, val); + + if (clientIP) + { + OSCMessage msg = OSCMessage(buffer); + msg.add(val); + + udp.beginPacket(clientIP, UDP_SEND_PORT); + msg.send(udp); + udp.endPacket(); + + msg.empty(); + } + else + { + DEBUG_MSG("No client IP.\n"); + } +} + +void MidiSoloToOSC(uint8_t channel, uint8_t val) +{ + char buffer[1024]; + + if (channel < 1 && channel > 16) + return; + if (val > 1) + val = 1; + + snprintf(buffer, sizeof(buffer), "/midi/mixer/solo/%d", channel); + + DEBUG_MSG("MidiSoloToOsc: %s %f\n", buffer, float(val)); if (clientIP) { @@ -373,39 +467,119 @@ void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val) } } -void change_midi_state(uint8_t midichannel, uint8_t cc, uint8_t value) +void change_midistate_cc(uint8_t midichannel, uint8_t cc, uint8_t value) { last_usage = millis(); DEBUG_MSG("Setting internal state of MIDI Channel %2d CC#%02d to %d\n", midichannel, cc, int8_t(value)); - if (midistate[(midichannel - 1) * 128 + cc - 1] != int8_t(value)) + if (midistate_cc[(midichannel - 1) * 128 + cc - 1] != int8_t(value)) { - midistate[(midichannel - 1) * 128 + cc - 1] = int8_t(value); + midistate_cc[(midichannel - 1) * 128 + cc - 1] = int8_t(value); write_state = true; lcd.setCursor(LCD_COL - 1, LCD_ROW - 1); lcd.print("*"); } } -void show_midi_state(void) +void change_midistate_mute(uint8_t midichannel, bool value) +{ + last_usage = millis(); + + DEBUG_MSG("Setting internal mute state of MIDI Channel %2d to %d\n", midichannel, int8_t(value)); + + if (midistate_mute[(midichannel - 1)] != value) + { + midistate_mute[(midichannel - 1)] = value; + write_state = true; + lcd.setCursor(LCD_COL - 1, LCD_ROW - 1); + lcd.print("*"); + } +} + +void change_midistate_solo(uint8_t midichannel, bool value) +{ + last_usage = millis(); + + DEBUG_MSG("Setting internal solo state of MIDI Channel %2d to %d\n", midichannel, int8_t(value)); + + if (midistate_solo[(midichannel - 1)] != value) + { + midistate_solo[(midichannel - 1)] = value; + write_state = true; + lcd.setCursor(LCD_COL - 1, LCD_ROW - 1); + lcd.print("*"); + } +} + +void show_midistate(void) { uint8_t m, c; - DEBUG_MSG("Current MIDI state:\n"); + DEBUG_MSG("Current MIDI state: \n"); listDir(SPIFFS, "/", 1); for (m = 0; m < 16; m++) { - DEBUG_MSG("MIDI-Channel %2d\n", m + 1); + DEBUG_MSG("MIDI - Channel %2d\n", m + 1); for (c = 0; c < 128; c++) { - if (midistate[m * 128 + c] >= 0) + if (midistate_cc[m * 128 + c] >= 0) { - DEBUG_MSG("\tCC: %3d = %3d\n", c + 1, midistate[m * 128 + c]); + DEBUG_MSG("\tCC: %3d = %3d\n", c + 1, midistate_cc[m * 128 + c]); } } + if (midistate_mute[m] > 0) + { + DEBUG_MSG("\tMUTE\n"); + } + if (midistate_solo[m] > 0) + { + DEBUG_MSG("\tSOLO\n"); + } + } +} + +void set_midi_solo_mode(void) +{ + bool all_solo_off = true; + + DEBUG_MSG("Set MIDI solo mode\n"); + + for (uint8_t m = 0; m < 16; m++) + { + if (midistate_solo[m] == 1) + { + all_solo_off = false; + + MIDI1.sendControlChange(7, MIDI_SOLO_VOLUME, m + 1); + DEBUG_MSG("\tChannel: %d: SOLO Volume: %d\n", m + 1, MIDI_SOLO_VOLUME); + } + else if (midistate_solo[m] == 0) + { + MIDI1.sendControlChange(7, 0, m + 1); + DEBUG_MSG("\tChannel: %d: Volume: %d\n", m + 1, 0); + } + } + + if (all_solo_off == true) + { + set_midi_mute_mode(); + } +} + +void set_midi_mute_mode(void) +{ + for (uint8_t m = 0; m < 16; m++) + { + if (midistate_mute[m] == 1) + { + DEBUG_MSG("\t Channel: %d: MUTE\n", m); + MIDI1.sendControlChange(7, 0, m + 1); + } + else if (midistate_mute[m] == 0) + MIDI1.sendControlChange(7, constrain(midistate_cc[m * 128 + 6], 0, 127), m + 1); } } @@ -429,28 +603,26 @@ void check_write_state(void) { write_midistate(1); write_state = false; - show_midi_state(); + show_midistate(); } } -void write_midistate(uint8_t setup_number) +void write_midistate_cc(uint8_t setup_number) { - DEBUG_MSG("Write MIDI state\n"); - - uint32_t t0 = millis(); + DEBUG_MSG("Write MIDI CC state\n"); for (uint8_t m = 0; m < 16; m++) { for (uint8_t c = 0; c < 128; c++) { - if (midistate[m * 128 + c] >= 0) + if (midistate_cc[m * 128 + c] >= 0) { - DEBUG_MSG("Found MIDI Channel %2d, CC#%2d = %3d\n", m, c, midistate[m * 128 + c]); + DEBUG_MSG("Found MIDI Channel %2d, CC#%2d = %3d\n", m, c, midistate_cc[m * 128 + c]); - char midi_cc_name[33]; + char midi_cc_name[24]; int16_t tmp_val; - sprintf(midi_cc_name, "/%d/midistate/%d/%d", setup_number, m, c); + sprintf(midi_cc_name, "/%d/midistate_cc/%d/%d", setup_number, m, c); if (SPIFFS.exists(midi_cc_name)) { @@ -458,7 +630,7 @@ void write_midistate(uint8_t setup_number) if (midi_cc) { tmp_val = midi_cc.read(); - DEBUG_MSG("Data for MIDI Channel %d, CC %d exists: %d\n", m, c, tmp_val); + DEBUG_MSG("Data for MIDI Channel %d, CC %d exists : %d\n", m, c, tmp_val); } close(midi_cc); } @@ -467,16 +639,16 @@ void write_midistate(uint8_t setup_number) tmp_val = -1; } - if (midistate[m * 128 + c] != tmp_val) + if (midistate_cc[m * 128 + c] != tmp_val) { - DEBUG_MSG(" Change from %d to %d detected\n", tmp_val, midistate[m * 128 + c]); + DEBUG_MSG(" Change from %d to %d detected\n", tmp_val, midistate_cc[m * 128 + c]); File midi_cc = SPIFFS.open(midi_cc_name, "w"); if (midi_cc) { - midi_cc.write(midistate[m * 128 + c]); + midi_cc.write(midistate_cc[m * 128 + c]); midi_cc.flush(); - DEBUG_MSG("Wrote %d to %s.\n", midistate[m * 128 + c], midi_cc_name); + DEBUG_MSG("Wrote %d to %s.\n", midistate_cc[m * 128 + c], midi_cc_name); } else DEBUG_MSG("Cannot write to %s.\n", midi_cc_name); @@ -488,39 +660,35 @@ void write_midistate(uint8_t setup_number) lcd.setCursor(LCD_COL - 1, LCD_ROW - 1); lcd.print(" "); - - uint32_t t1 = millis(); - - DEBUG_MSG("T1-T0=%d\n", t1 - t0); } -void read_midistate(uint8_t setup_number) +void read_midistate_cc(uint8_t setup_number) { - char setup_dir_name[33]; + char setup_dir_name[24]; - DEBUG_MSG("Read MIDI state\n"); + DEBUG_MSG("Read MIDI CC state\n"); - sprintf(setup_dir_name, "/%d/midistate", setup_number); + sprintf(setup_dir_name, "/%d/midistate_cc", setup_number); - File midistate_dir = SPIFFS.open(setup_dir_name); + File midistate_cc_dir = SPIFFS.open(setup_dir_name); - if (!midistate_dir) + if (!midistate_cc_dir) { DEBUG_MSG("Failed to open directory %s.\n", setup_dir_name); return; } - if (!midistate_dir.isDirectory()) + if (!midistate_cc_dir.isDirectory()) { DEBUG_MSG("%s is not a directory.\n", setup_dir_name); return; } - File channel_cc = midistate_dir.openNextFile(); + File channel_cc = midistate_cc_dir.openNextFile(); while (channel_cc) { - DEBUG_MSG("Trying %s (Size: %d)\n", channel_cc.name(), channel_cc.size()); + DEBUG_MSG("Trying %s (Size : %d)\n", channel_cc.name(), channel_cc.size()); if (uint8_t(channel_cc.size()) == 1) { @@ -547,40 +715,281 @@ void read_midistate(uint8_t setup_number) if (cc) { int8_t val = cc.read(); - DEBUG_MSG(" MIDI-Channel %d CC#%d = %d\n", midi_channel + 1, midi_cc + 1, val); - midistate[midi_channel * 128 + midi_cc] = val; + DEBUG_MSG(" MIDI - Channel %d CC#%d = %d\n", midi_channel + 1, midi_cc + 1, val); + midistate_cc[midi_channel * 128 + midi_cc] = val; cc.close(); } } } else { - DEBUG_MSG("Removing %s: not the right size.\n", channel_cc.name()); + DEBUG_MSG("Removing %s : not the right size.\n", channel_cc.name()); SPIFFS.remove(channel_cc.name()); } - channel_cc = midistate_dir.openNextFile(); + channel_cc = midistate_cc_dir.openNextFile(); + } +} + +void write_midistate_mute(uint8_t setup_number) +{ + DEBUG_MSG("Write MIDI MUTE state\n"); + + for (uint8_t m = 0; m < 16; m++) + { + if (midistate_mute[m] >= 0) + { + DEBUG_MSG("Found MIDI Channel %2d = %3d\n", m, midistate_mute[m]); + + char midi_mute_name[21]; + int8_t tmp_val = -1; + + sprintf(midi_mute_name, "/%d/midistate_mute/%d", setup_number, m); + + if (SPIFFS.exists(midi_mute_name)) + { + File midi_mute = SPIFFS.open(midi_mute_name, "r"); + if (midi_mute) + { + tmp_val = midi_mute.read(); + DEBUG_MSG("Mute data for MIDI Channel %d exists : %d\n", m, tmp_val); + } + close(midi_mute); + } + + if (midistate_mute[m] != tmp_val) + { + DEBUG_MSG(" Change from %d to %d detected\n", tmp_val, midistate_mute[m]); + + File midi_mute = SPIFFS.open(midi_mute_name, "w"); + if (midi_mute) + { + midi_mute.write(midistate_mute[m]); + midi_mute.flush(); + DEBUG_MSG("Wrote %d to %s.\n", midistate_mute[m], midi_mute_name); + } + else + DEBUG_MSG("Cannot write to %s.\n", midi_mute_name); + close(midi_mute); + } + } + } +} + +void read_midistate_mute(uint8_t setup_number) +{ + char setup_dir_name[21]; + + DEBUG_MSG("Read MIDI MUTE state\n"); + + sprintf(setup_dir_name, "/%d/midistate_mute", setup_number); + + File midistate_mute_dir = SPIFFS.open(setup_dir_name); + + if (!midistate_mute_dir) + { + DEBUG_MSG("Failed to open directory %s.\n", setup_dir_name); + return; + } + + File channel_mute = midistate_mute_dir.openNextFile(); + + while (channel_mute) + { + DEBUG_MSG("Trying %s (Size : %d)\n", channel_mute.name(), channel_mute.size()); + + if (uint8_t(channel_mute.size()) == 1) + { + if (!channel_mute.isDirectory()) + { + char tmp_name[21]; + uint8_t midi_channel; + uint8_t count = 0; + + DEBUG_MSG("Using %s\n", channel_mute.name()); + + strcpy(tmp_name, channel_mute.name()); + for (String part = strtok(tmp_name, "/"); part; part = strtok(NULL, "/")) + { + count++; + if (count == 3) + midi_channel = atoi(part.c_str()); + } + + File mute = SPIFFS.open(channel_mute.name(), "r"); + if (mute) + { + int8_t val = mute.read(); + DEBUG_MSG(" MIDI - Channel MUTE %d = %d\n", midi_channel + 1, val); + midistate_mute[midi_channel] = val; + mute.close(); + } + } + } + else + { + DEBUG_MSG("Removing %s : not the right size.\n", channel_mute.name()); + SPIFFS.remove(channel_mute.name()); + } + channel_mute = midistate_mute_dir.openNextFile(); + } +} + +void write_midistate_solo(uint8_t setup_number) +{ + DEBUG_MSG("Write MIDI SOLO state\n"); + + for (uint8_t m = 0; m < 16; m++) + { + if (midistate_solo[m] >= 0) + { + DEBUG_MSG("Found MIDI Channel %2d = %3d\n", m, midistate_solo[m]); + + char midi_solo_name[21]; + int8_t tmp_val = -1; + + sprintf(midi_solo_name, "/%d/midistate_solo/%d", setup_number, m); + + if (SPIFFS.exists(midi_solo_name)) + { + File midi_solo = SPIFFS.open(midi_solo_name, "r"); + if (midi_solo) + { + tmp_val = midi_solo.read(); + DEBUG_MSG("Solo data for MIDI Channel %d exists : %d\n", m, tmp_val); + } + close(midi_solo); + } + + if (midistate_solo[m] != tmp_val) + { + DEBUG_MSG(" Change from %d to %d detected\n", tmp_val, midistate_solo[m]); + + File midi_solo = SPIFFS.open(midi_solo_name, "w"); + if (midi_solo) + { + midi_solo.write(midistate_solo[m]); + midi_solo.flush(); + DEBUG_MSG("Wrote %d to %s.\n", midistate_solo[m], midi_solo_name); + } + else + DEBUG_MSG("Cannot write to %s.\n", midi_solo_name); + close(midi_solo); + } + } + } +} + +void read_midistate_solo(uint8_t setup_number) +{ + char setup_dir_name[21]; + + DEBUG_MSG("Read MIDI SOLO state\n"); + + sprintf(setup_dir_name, "/%d/midistate_solo", setup_number); + + File midistate_solo_dir = SPIFFS.open(setup_dir_name); + + if (!midistate_solo_dir) + { + DEBUG_MSG("Failed to open directory %s.\n", setup_dir_name); + return; + } + + File channel_solo = midistate_solo_dir.openNextFile(); + + while (channel_solo) + { + DEBUG_MSG("Trying %s (Size : %d)\n", channel_solo.name(), channel_solo.size()); + + if (uint8_t(channel_solo.size()) == 1) + { + if (!channel_solo.isDirectory()) + { + char tmp_name[20]; + uint8_t midi_channel; + uint8_t count = 0; + + DEBUG_MSG("Using %s\n", channel_solo.name()); + + strcpy(tmp_name, channel_solo.name()); + for (String part = strtok(tmp_name, "/"); part; part = strtok(NULL, "/")) + { + count++; + if (count == 3) + midi_channel = atoi(part.c_str()); + } + + File solo = SPIFFS.open(channel_solo.name(), "r"); + if (solo) + { + int8_t val = solo.read(); + DEBUG_MSG(" MIDI - Channel SOLO %d = %d\n", midi_channel + 1, val); + midistate_solo[midi_channel] = val; + solo.close(); + } + } + } + else + { + DEBUG_MSG("Removing %s : not the right size.\n", channel_solo.name()); + SPIFFS.remove(channel_solo.name()); + } + channel_solo = midistate_solo_dir.openNextFile(); } + + set_midi_solo_mode(); +} + +void read_midistate(uint8_t setup_number) +{ + read_midistate_cc(setup_number); + read_midistate_mute(setup_number); + read_midistate_solo(setup_number); +} + +void write_midistate(uint8_t setup_number) +{ + write_midistate_cc(setup_number); + write_midistate_mute(setup_number); + write_midistate_solo(setup_number); + + lcd.setCursor(LCD_COL - 1, LCD_ROW - 1); + lcd.print(" "); } void broadcast_midistate(void) { uint8_t m, c; - DEBUG_MSG("Broadcast MIDI state:\n"); + DEBUG_MSG("Broadcast MIDI CC state : \n"); for (m = 0; m < 16; m++) { - DEBUG_MSG("MIDI-Channel %2d\n", m + 1); + DEBUG_MSG("MIDI - Channel %2d\n", m + 1); for (c = 0; c < 128; c++) { - if (midistate[m * 128 + c] >= 0) + if (midistate_cc[m * 128 + c] >= 0) { - DEBUG_MSG("\tCC: %3d = %3d\n", c + 1, midistate[m * 128 + c]); - MidiCCToOSC(m + 1, c + 1, midistate[m * 128 + c]); - MIDI1.sendControlChange(c + 1, midistate[m * 128 + c], m + 1); + DEBUG_MSG("\tCC : %3d = %3d\n", c + 1, midistate_cc[m * 128 + c]); + MidiCCToOSC(m + 1, c + 1, midistate_cc[m * 128 + c]); + MIDI1.sendControlChange(c + 1, midistate_cc[m * 128 + c], m + 1); } } + + if (midistate_mute[m] >= 0) + { + DEBUG_MSG("\tMUTE : %3d\n", midistate_solo[m]); + MidiMuteToOSC(m + 1, midistate_mute[m]); + } + + if (midistate_solo[m] >= 0) + { + DEBUG_MSG("\SOLO : %3d\n", midistate_solo[m]); + MidiSoloToOSC(m + 1, midistate_solo[m]); + } } + + set_midi_solo_mode(); } void check_reset_ap_data(void) @@ -620,12 +1029,13 @@ void ping(OSCMessage & msg, int offset) } } -void listDir(fs::FS & fs, const char * dirname, uint8_t levels) { - Serial.printf("Listing directory: %s\r\n", dirname); +void listDir(fs::FS & fs, const char * dirname, uint8_t levels) +{ + Serial.printf("Listing directory : %s\r\n", dirname); File root = fs.open(dirname); if (!root) { - Serial.println("- failed to open directory"); + Serial.println(" - failed to open directory"); return; } if (!root.isDirectory()) { @@ -642,9 +1052,9 @@ void listDir(fs::FS & fs, const char * dirname, uint8_t levels) { listDir(fs, file.name(), levels - 1); } } else { - Serial.print(" FILE: "); + Serial.print(" FILE : "); Serial.print(file.name()); - Serial.print("\tSIZE: "); + Serial.print("\tSIZE : "); Serial.println(file.size()); } file = root.openNextFile();