Initial version from https://github.com/tadas-s/OSC2Midi
https://github.com/tadas-s/OSC2Midi
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1161 lines
28 KiB
1161 lines
28 KiB
/*
|
|
OSC2MIDI
|
|
|
|
OSC2MIDI is a heavily extended port of https://github.com/tadas-s/OSC2Midi
|
|
|
|
(c)2020 H. Wirtz <wirtz@parasitstudio.de>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
// Use from 0 to 4. Higher number, more debugging messages and memory usage.
|
|
#define _WIFIMGR_LOGLEVEL_ 1
|
|
#define DEBUG 1
|
|
|
|
#include <WiFi.h>
|
|
#include <WiFiUdp.h>
|
|
#include <WiFiManager.h>
|
|
#include "debug.h"
|
|
#include <Arduino.h>
|
|
#include <ESPmDNS.h>
|
|
#include <OSCMessage.h>
|
|
#include <OSCBundle.h>
|
|
#include <OSCData.h>
|
|
#include <MIDI.h>
|
|
#include "OSC2Midi.h"
|
|
#include <HardwareSerial.h>
|
|
#include <SoftwareSerial.h>
|
|
#include <LiquidCrystal_I2C.h>
|
|
#include <looper.h>
|
|
#include <SPIFFS.h>
|
|
#include <ESPFlash.h>
|
|
#include <ESP32Encoder.h>
|
|
|
|
#define MDNS_NAME "osc2midi"
|
|
#define AP_SSID_NAME "OSC2MIDI"
|
|
#define AP_PASSWORD "osc2midi"
|
|
#define AP_SSID_CONFIG_NAME "OSC2MIDI-Config"
|
|
#define AP_CONFIG_PASSWORD "osc2midi"
|
|
#define WIFI_CONNECT_TIMEOUT 30
|
|
#define CFG_PORTAL_TIMEOUT 90
|
|
#define SOFT_SERIAL_RX 18
|
|
#define SOFT_SERIAL_TX 19
|
|
#define AP_MODE_PIN 26
|
|
#define LCD_I2C_ADDR 0x27
|
|
#define LCD_COL 16
|
|
#define LCD_ROW 2
|
|
#define UDP_RECV_PORT 8000
|
|
#define UDP_SEND_PORT 9000
|
|
#define KRATE_MODE 200
|
|
#define KRATE_STATE 2000
|
|
#define KRATE_RESET_AP_DATA 5000
|
|
#define KRATE_CHECK_WRITE_STATE 10000
|
|
#define LAST_USAGE_TIMER 5000
|
|
#define FORMAT_SPIFFS_IF_FAILED true
|
|
#define MIDI_SOLO_VOLUME 100
|
|
#define MIDI_IGNORE_VOLUME_CC 150
|
|
#define ENC_BUTTON_PIN 32
|
|
#define ENC_A_PIN 34
|
|
#define ENC_B_PIN 35
|
|
|
|
void OSCToMidiCC(OSCMessage &msg, int offset);
|
|
void OSCMixerMuteToMidiCC(OSCMessage &msg, int offset);
|
|
void OSCMixerSoloToMidiCC(OSCMessage &msg, int offset);
|
|
void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t value);
|
|
void ConfigAPWeb(void);
|
|
void ConfigAPStarted(WiFiManager* wm);
|
|
|
|
/*void change_midi_state(uint8_t midichannel, uint8_t cc, uint8_t value);
|
|
void show_midi_state(void);
|
|
void set_midi_state(void);
|
|
void check_mode(void);*/
|
|
|
|
WiFiUDP udp;
|
|
IPAddress clientIP;
|
|
LiquidCrystal_I2C lcd(LCD_I2C_ADDR, LCD_COL, LCD_ROW);
|
|
HardwareSerial midi1(2); // RX: 16, TX: 17
|
|
#ifndef D5
|
|
#define D5 (SOFT_SERIAL_RX)
|
|
#define D6 (SOFT_SERIAL_TX)
|
|
//#define D7 (23)
|
|
//#define D8 (5)
|
|
//#define TX (1)
|
|
#endif
|
|
SoftwareSerial midi2;
|
|
bool ap_mode_state;
|
|
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;
|
|
uint32_t last_usage = millis();
|
|
bool broadcast_send = false;
|
|
uint32_t midi_ignore_volume_cc = 0;
|
|
ESP32Encoder enc;
|
|
|
|
MIDI_CREATE_INSTANCE(HardwareSerial, midi1, MIDI1);
|
|
|
|
void setup()
|
|
{
|
|
bool enc_but_pin_reset;
|
|
|
|
pinMode(AP_MODE_PIN, INPUT_PULLDOWN);
|
|
ap_mode_state = digitalRead(AP_MODE_PIN);
|
|
pinMode(ENC_BUTTON_PIN, INPUT);
|
|
//ESP32Encoder::useInternalWeakPullResistors = UP;
|
|
enc.attachSingleEdge(ENC_A_PIN, ENC_B_PIN);
|
|
enc.setCount(0);
|
|
enc_but_pin_reset = digitalRead(ENC_BUTTON_PIN);
|
|
|
|
Serial.begin(115200);
|
|
|
|
DEBUG_MSG("<SETUP>\n");
|
|
|
|
memset(midistate_cc, -1, 16 * 128);
|
|
memset(midistate_mute, -1, 16);
|
|
memset(midistate_solo, -1, 16);
|
|
|
|
Serial.setDebugOutput(true);
|
|
Serial.println(F("OSC2MIDI (c)2020 H. Wirtz <wirtz@parasitstudio.de>"));
|
|
|
|
lcd.init();
|
|
lcd.backlight();
|
|
lcd.clear();
|
|
lcd.noCursor();
|
|
lcd.setCursor(2, 0);
|
|
lcd.print(F("* OSC2MIDI *"));
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(F("(c)parasiTstudio"));
|
|
delay(1000);
|
|
|
|
if (enc_but_pin_reset == LOW && digitalRead(ENC_BUTTON_PIN) == LOW)
|
|
{
|
|
reset_ap_data();
|
|
}
|
|
|
|
WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
|
|
|
|
if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED))
|
|
{
|
|
DEBUG_MSG("An Error has occurred while mounting SPIFFS!");
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(F("SPIFFS ERROR"));
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(F("Restarting..."));
|
|
delay(1000);
|
|
ESP.restart();
|
|
}
|
|
|
|
if (ap_mode_state == LOW)
|
|
{
|
|
DEBUG_MSG("Mode Access-Point\n");
|
|
|
|
if (!WiFi.softAPConfig(IPAddress(192, 168, 4, 1), IPAddress(192, 168, 4, 1), IPAddress(255, 255, 255, 0))) {
|
|
DEBUG_MSG("AP Config Failed\n");
|
|
}
|
|
|
|
if (!WiFi.softAP(AP_SSID_NAME, AP_PASSWORD))
|
|
{
|
|
DEBUG_MSG("Failed to start AP\n");
|
|
lcd.print(F("Failed "));
|
|
delay(1000);
|
|
lcd.print(F("- restart"));
|
|
delay(1000);
|
|
ESP.restart();
|
|
}
|
|
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(F("Mode AP"));
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(WiFi.softAPIP());
|
|
}
|
|
else
|
|
{
|
|
DEBUG_MSG("Mode Client\n");
|
|
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(F("Connecting... "));
|
|
|
|
WiFiManager wm;
|
|
|
|
wm.setWebServerCallback(ConfigAPWeb);
|
|
wm.setAPCallback(ConfigAPStarted);
|
|
wm.setConnectTimeout(WIFI_CONNECT_TIMEOUT);
|
|
wm.setConfigPortalTimeout(CFG_PORTAL_TIMEOUT);
|
|
wm.setBreakAfterConfig(true);
|
|
wm.setRemoveDuplicateAPs(true);
|
|
wm.setWiFiAutoReconnect(false);
|
|
wm.setEnableConfigPortal(false);
|
|
|
|
if (!wm.autoConnect(AP_SSID_CONFIG_NAME, AP_CONFIG_PASSWORD))
|
|
{
|
|
DEBUG_MSG("Failed to connect\n");
|
|
lcd.print(F("Failed"));
|
|
delay(1000);
|
|
lcd.print(F("- restart"));
|
|
delay(1000);
|
|
ESP.restart();
|
|
}
|
|
else
|
|
{
|
|
DEBUG_MSG("Connected\n");
|
|
|
|
if (!MDNS.begin(MDNS_NAME))
|
|
{
|
|
DEBUG_MSG("Error setting up MDNS responder!\n");
|
|
}
|
|
else
|
|
{
|
|
DEBUG_MSG("mDNS started.\n");
|
|
}
|
|
}
|
|
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(F("Mode WiFi client"));
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(WiFi.localIP());
|
|
}
|
|
|
|
udp.begin(UDP_RECV_PORT);
|
|
DEBUG_MSG("Listening for UDP packets on port %d\n", UDP_RECV_PORT);
|
|
|
|
midi1.begin(31250); // 16,17
|
|
midi2.begin(31250, SWSERIAL_8N1, D5, D6, false, 95, 11);
|
|
midi2.enableIntTx(false);
|
|
|
|
MIDI1.begin(MIDI_CHANNEL_OMNI);
|
|
MIDI1.setHandleControlChange(MidiCCToOSC);
|
|
MIDI1.turnThruOff();
|
|
|
|
sched.addJob(check_mode, KRATE_MODE);
|
|
sched.addJob(check_reset_ap_data, KRATE_RESET_AP_DATA);
|
|
sched.addJob(check_write_state, KRATE_CHECK_WRITE_STATE);
|
|
sched.addJob(enc_show, 1000);
|
|
//sched.addJob(show_midi_state, KRATE_STATE);
|
|
|
|
listDir(SPIFFS, "/", 1);
|
|
read_midistate(1);
|
|
show_midistate();
|
|
|
|
DEBUG_MSG("<LOOP>\n");
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
OSCMessage msg;
|
|
uint8_t buffer[1024];
|
|
uint16_t outPort;
|
|
|
|
size_t size = udp.parsePacket();
|
|
|
|
if (udp.available())
|
|
{
|
|
IPAddress tmpIP = udp.remoteIP();
|
|
// Keep track of the client IP address for "talking back"
|
|
if (clientIP != tmpIP)
|
|
{
|
|
clientIP = tmpIP;
|
|
DEBUG_MSG("New connection from: %d.%d.%d.%d\n", clientIP[0], clientIP[1], clientIP[2], clientIP[3]);
|
|
}
|
|
// Check if there are any OSC packets to handle
|
|
udp.read(buffer, size);
|
|
msg.fill(buffer, size);
|
|
|
|
if (!msg.hasError())
|
|
{
|
|
DEBUG_OSC_MESSAGE(msg);
|
|
msg.route("/midi/cc", OSCToMidiCC);
|
|
msg.route("/midi/mixer/mute", OSCMixerMuteToMidiCC);
|
|
msg.route("/midi/mixer/solo", OSCMixerSoloToMidiCC);
|
|
msg.route("/ping", ping);
|
|
//msg.route("/midi/sysex", OSCToMidiSYSEX);
|
|
//msg.route("/midi/note", OSCToMidiNote);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_MSG("Error parsing OSC message: %d\n", msg.getError());
|
|
}
|
|
}
|
|
|
|
// Check if there are any CC messages from synth itself
|
|
if (MIDI1.read())
|
|
{
|
|
DEBUG_MSG("MIDI-IN[1] Type: ");
|
|
DEBUG_MSG("%3d", MIDI1.getType());
|
|
DEBUG_MSG(" Data1: ");
|
|
DEBUG_MSG("%3d", MIDI1.getData1());
|
|
DEBUG_MSG(" Data2: ");
|
|
DEBUG_MSG("%3d", MIDI1.getData2());
|
|
DEBUG_MSG(" Channel: ");
|
|
DEBUG_MSG("%0d", MIDI1.getChannel());
|
|
DEBUG_MSG("\n");
|
|
}
|
|
|
|
// MIDI-Merger from (Soft-)MIDI2 to MIDI1
|
|
if (midi2.available() > 0)
|
|
{
|
|
while (midi2.available() > 0)
|
|
{
|
|
DEBUG_MSG("MIDI-IN[2]: %03d\n", midi2.peek());
|
|
midi1.write(midi2.read());
|
|
}
|
|
}
|
|
|
|
sched.scheduler();
|
|
}
|
|
|
|
void OSCToMidiCC(OSCMessage & msg, int offset)
|
|
{
|
|
char address[100] = { 0 };
|
|
uint8_t cc, value;
|
|
uint8_t midichannel;
|
|
|
|
msg.getAddress(address, offset, sizeof(address));
|
|
midichannel = getMIDIChannel(address);
|
|
|
|
if (msg.size() == 1 && msg.isFloat(0))
|
|
{
|
|
// Single or multi control with sending one value
|
|
cc = getCC(address);
|
|
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);
|
|
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))
|
|
{
|
|
// XY pad, two values
|
|
cc = getVar(address, 1);
|
|
value = round(msg.getFloat(0));
|
|
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_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_midistate_cc(midichannel, cc, value);
|
|
}
|
|
else
|
|
{
|
|
DEBUG_MSG("Cannot handle: %s\n", address);
|
|
}
|
|
}
|
|
|
|
void OSCMixerMuteToMidiCC(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("MixerMute 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);
|
|
|
|
midi_ignore_volume_cc = millis();
|
|
change_midistate_mute(midichannel, value);
|
|
|
|
set_midi_solo_mode();
|
|
}
|
|
}
|
|
|
|
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);
|
|
*/
|
|
|
|
midi_ignore_volume_cc = millis();
|
|
change_midistate_solo(midichannel, value);
|
|
set_midi_solo_mode();
|
|
}
|
|
}
|
|
|
|
void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val)
|
|
{
|
|
char buffer[1024];
|
|
|
|
if (channel < 1 && channel > 16)
|
|
return;
|
|
if (number > 127)
|
|
return;
|
|
val = constrain(val, 0, 127);
|
|
|
|
if (number == 7)
|
|
if (millis() - MIDI_IGNORE_VOLUME_CC < midi_ignore_volume_cc)
|
|
return;
|
|
else
|
|
DEBUG_MSG("\n!!!%d - %d!!!\n\n", millis() - MIDI_IGNORE_VOLUME_CC, midi_ignore_volume_cc);
|
|
|
|
snprintf(buffer, sizeof(buffer), "/midi/cc/%d/%d", channel, number);
|
|
|
|
DEBUG_MSG("MidiCCToOsc: %s %f\n", buffer, 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)
|
|
{
|
|
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 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_cc[(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 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");
|
|
|
|
listDir(SPIFFS, "/", 1);
|
|
|
|
for (m = 0; m < 16; m++)
|
|
{
|
|
DEBUG_MSG("MIDI - Channel %2d\n", m + 1);
|
|
for (c = 0; c < 128; c++)
|
|
{
|
|
if (midistate_cc[m * 128 + c] >= 0)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
void check_mode(void)
|
|
{
|
|
if (ap_mode_state != digitalRead(AP_MODE_PIN))
|
|
{
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Changing mode...");
|
|
delay(500);
|
|
ESP.restart();
|
|
}
|
|
}
|
|
|
|
void check_write_state(void)
|
|
{
|
|
DEBUG_MSG("Checking midi state.\n");
|
|
|
|
if (write_state == true && millis() - last_usage > LAST_USAGE_TIMER)
|
|
{
|
|
write_midistate(1);
|
|
write_state = false;
|
|
show_midistate();
|
|
}
|
|
}
|
|
|
|
void write_midistate_cc(uint8_t setup_number)
|
|
{
|
|
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_cc[m * 128 + c] >= 0)
|
|
{
|
|
DEBUG_MSG("Found MIDI Channel %2d, CC#%2d = %3d\n", m, c, midistate_cc[m * 128 + c]);
|
|
|
|
char midi_cc_name[24];
|
|
int16_t tmp_val;
|
|
|
|
sprintf(midi_cc_name, "/%d/midistate_cc/%d/%d", setup_number, m, c);
|
|
|
|
if (SPIFFS.exists(midi_cc_name))
|
|
{
|
|
File midi_cc = SPIFFS.open(midi_cc_name, "r");
|
|
if (midi_cc)
|
|
{
|
|
tmp_val = midi_cc.read();
|
|
DEBUG_MSG("Data for MIDI Channel %d, CC %d exists : %d\n", m, c, tmp_val);
|
|
}
|
|
close(midi_cc);
|
|
}
|
|
else
|
|
{
|
|
tmp_val = -1;
|
|
}
|
|
|
|
if (midistate_cc[m * 128 + c] != tmp_val)
|
|
{
|
|
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_cc[m * 128 + c]);
|
|
midi_cc.flush();
|
|
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);
|
|
close(midi_cc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
lcd.setCursor(LCD_COL - 1, LCD_ROW - 1);
|
|
lcd.print(" ");
|
|
}
|
|
|
|
void read_midistate_cc(uint8_t setup_number)
|
|
{
|
|
char setup_dir_name[24];
|
|
|
|
DEBUG_MSG("Read MIDI CC state\n");
|
|
|
|
sprintf(setup_dir_name, "/%d/midistate_cc", setup_number);
|
|
|
|
File midistate_cc_dir = SPIFFS.open(setup_dir_name);
|
|
|
|
if (!midistate_cc_dir)
|
|
{
|
|
DEBUG_MSG("Failed to open directory %s.\n", setup_dir_name);
|
|
return;
|
|
}
|
|
|
|
if (!midistate_cc_dir.isDirectory())
|
|
{
|
|
DEBUG_MSG("%s is not a directory.\n", setup_dir_name);
|
|
return;
|
|
}
|
|
|
|
File channel_cc = midistate_cc_dir.openNextFile();
|
|
|
|
while (channel_cc)
|
|
{
|
|
DEBUG_MSG("Trying %s (Size : %d)\n", channel_cc.name(), channel_cc.size());
|
|
|
|
if (uint8_t(channel_cc.size()) == 1)
|
|
{
|
|
if (!channel_cc.isDirectory())
|
|
{
|
|
char tmp_name[33];
|
|
uint8_t midi_channel;
|
|
uint8_t midi_cc;
|
|
uint8_t count = 0;
|
|
|
|
DEBUG_MSG("Using %s\n", channel_cc.name());
|
|
|
|
strcpy(tmp_name, channel_cc.name());
|
|
for (String part = strtok(tmp_name, "/"); part; part = strtok(NULL, "/"))
|
|
{
|
|
count++;
|
|
if (count == 3)
|
|
midi_channel = atoi(part.c_str());
|
|
else if (count == 4)
|
|
midi_cc = atoi(part.c_str());
|
|
}
|
|
|
|
File cc = SPIFFS.open(channel_cc.name(), "r");
|
|
if (cc)
|
|
{
|
|
int8_t val = cc.read();
|
|
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());
|
|
SPIFFS.remove(channel_cc.name());
|
|
}
|
|
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 CC state : \n");
|
|
|
|
for (m = 0; m < 16; m++)
|
|
{
|
|
DEBUG_MSG("MIDI - Channel %2d\n", m + 1);
|
|
for (c = 0; c < 128; c++)
|
|
{
|
|
if (midistate_cc[m * 128 + c] >= 0)
|
|
{
|
|
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)
|
|
{
|
|
if (digitalRead(ENC_BUTTON_PIN) == LOW && last_reset_ap_check == true)
|
|
{
|
|
reset_ap_data();
|
|
}
|
|
else if (digitalRead(ENC_BUTTON_PIN) == LOW)
|
|
{
|
|
if (digitalRead(ENC_BUTTON_PIN) == LOW)
|
|
DEBUG_MSG("Reset AP data pressed\n");
|
|
last_reset_ap_check = true;
|
|
}
|
|
else
|
|
last_reset_ap_check = false;
|
|
}
|
|
|
|
void ping(OSCMessage & msg, int offset)
|
|
{
|
|
if (clientIP && broadcast_send == false)
|
|
{
|
|
broadcast_send = true;
|
|
broadcast_midistate();
|
|
}
|
|
}
|
|
|
|
void ConfigAPWeb(void)
|
|
{
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print(F("Mode Config-AP"));
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(F("192.168.4.1"));
|
|
}
|
|
|
|
void ConfigAPStarted(WiFiManager * wm)
|
|
{
|
|
lcd.clear();
|
|
DEBUG_MSG("Config-AP started\n");
|
|
}
|
|
|
|
void reset_ap_data(void)
|
|
{
|
|
WiFiManager wm;
|
|
|
|
DEBUG_MSG("Reset AP data\n");
|
|
lcd.clear();
|
|
lcd.setCursor(0, 0);
|
|
lcd.print("Reset AP data...");
|
|
wm.resetSettings();
|
|
lcd.setCursor(0, 1);
|
|
lcd.print("done.");
|
|
delay(500);
|
|
lcd.clear();
|
|
ESP.restart();
|
|
}
|
|
|
|
void firmware_reset(void)
|
|
{
|
|
WiFiManager wm;
|
|
|
|
lcd.setCursor(0, 3);
|
|
lcd.print(F("Firmware reset..."));
|
|
DEBUG_MSG("Reset AP data\n");
|
|
wm.resetSettings();
|
|
DEBUG_MSG("FORMAT SPIFFS...");
|
|
SPIFFS.format();
|
|
DEBUG_MSG("done.\n");
|
|
lcd.setCursor(0, 1);
|
|
lcd.print(F("done."));
|
|
delay(500);
|
|
lcd.clear();
|
|
ESP.restart();
|
|
}
|
|
|
|
void enc_show(void)
|
|
{
|
|
DEBUG_MSG("Encoder count = %d", enc.getCount());
|
|
DEBUG_MSG(" button[%d] = %d\n", ENC_BUTTON_PIN, (uint8_t)digitalRead(ENC_BUTTON_PIN));
|
|
}
|
|
|
|
void listDir(fs::FS & fs, const char * dirname, uint8_t levels)
|
|
{
|
|
DEBUG_MSG("Listing directory : %s\n", dirname);
|
|
|
|
File root = fs.open(dirname);
|
|
if (!root)
|
|
{
|
|
DEBUG_MSG("* Failed to open directory\n");
|
|
return;
|
|
}
|
|
if (!root.isDirectory())
|
|
{
|
|
DEBUG_MSG("* Not a directory\n");
|
|
return;
|
|
}
|
|
|
|
File file = root.openNextFile();
|
|
while (file)
|
|
{
|
|
if (file.isDirectory())
|
|
{
|
|
DEBUG_MSG(" DIR: ");
|
|
DEBUG_MSG("%s\n", file.name());
|
|
if (levels)
|
|
{
|
|
listDir(fs, file.name(), levels - 1);
|
|
}
|
|
}
|
|
else
|
|
DEBUG_MSG(" FILE: %s\t\tSIZE: %d\n", file.name(), file.size());
|
|
|
|
file = root.openNextFile();
|
|
}
|
|
}
|
|
|