First running version!

master
Holger Wirtz 4 years ago
parent 90f8157b9d
commit 33cbeb50dc
  1. 6
      OSC2Midi.cpp
  2. 2
      OSC2Midi.h
  3. 93
      OSC2MidiGateway.ino
  4. 12
      debug.h
  5. 90
      doc/midi_message_types.txt

@ -6,7 +6,7 @@
TODO: this will return rather unpredictable numbers when
given strings with numbers > 255.
*/
uint8_t getCC(const char *str, int index)
uint8_t getVar(const char *str, int index)
{
const char *offset = str;
@ -38,10 +38,10 @@ uint8_t getCC(const char *str, int index)
uint8_t getCC(const char *str)
{
return getCC(str, 1);
return getVar(str, 1);
}
uint8_t getMIDIChannel(const char *str)
{
return getCC(str, -1);
return getVar(str, 0);
}

@ -3,7 +3,7 @@
#include <cstdint>
uint8_t getCC(const char *str, int index);
uint8_t getVar(const char *str, int index);
uint8_t getCC(const char *str);
uint8_t getMIDIChannel(const char *str);

@ -1,6 +1,9 @@
#define DEBUG 1
#include "debug.h"
#include <Arduino.h>
#include <ESP8266WiFi.h>
//#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <WiFiUdp.h>
#include <OSCMessage.h>
#include <OSCBundle.h>
@ -21,96 +24,124 @@ IPAddress clientIP;
IPAddress gateway(192,168,4,1);
IPAddress subnet(255,255,255,0);*/
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);
/*
UART RX IO TX IO CTS RTS
UART0 GPIO3 GPIO1 N/A N/A
UART1 GPIO9 GPIO10 GPIO6 GPIO11
UART2 GPIO16 GPIO17 GPIO8 GPIO7
*/
MIDI_CREATE_INSTANCE(HardwareSerial, Serial2, MIDI); // Pins on ESP32: RX: 6, TX: 7
//MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI2); // Pins on ESP32: RX: ?, TX: ?
void setup() {
// Midi via UART0
Serial.begin(31250);
Serial.swap();
void setup()
{
Serial2.begin(31250);
#ifdef DEBUG
// Debug via UART1 TX only
Serial1.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY, 2);
Serial1.setDebugOutput(true);
#endif
Serial.begin(115200);
Serial.setDebugOutput(true);
DEBUG_MSG("\nHello OSC2Midi!\n");
WiFi.softAP("OSC2Midi", "Midi2OSCGateway");
WiFi.softAP("OSC2Midi", "1357924680");
DEBUG_MSG("\nAP IP address: %s\n", WiFi.softAPIP().toString().c_str());
udp.begin(8000);
MIDI.begin(MIDI_CHANNEL_OMNI);
MIDI.setHandleControlChange(MidiCCToOSC);
MIDI.turnThruOff();
}
void OSCToMidiCC(OSCMessage &msg, int offset) {
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);
//midichannel--;
if (msg.size() == 1 && msg.isFloat(0)) {
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\tCC: %u\tValue: %u\n", address, cc, value);
DEBUG_MSG("MSG: %s\tChannel: %u\t\tCC: %u\tValue: %u\n", address, midichannel, cc, value);
MIDI.sendControlChange(cc, value, midichannel);
} else if (msg.size() == 2 && msg.isFloat(0) && msg.isFloat(1)) {
}
else if (msg.size() == 2 && msg.isFloat(0) && msg.isFloat(1))
{
// XY pad, two values
cc = getCC(address, 1);
cc = getVar(address, 1);
value = round(msg.getFloat(0));
value = value > 127 ? 127 : value;
DEBUG_MSG("MSG: %s\tCC: %u\tValue: %u\n", address, cc, value);
DEBUG_MSG("MSG: %s\tChannel: %u\t\tCC: %u\tValue: %u\n", address, midichannel, cc, value);
MIDI.sendControlChange(cc, value, midichannel);
cc = getCC(address, 2);
cc = getVar(address, 2);
value = round(msg.getFloat(1));
value = value > 127 ? 127 : value;
DEBUG_MSG("MSG: %s\tCC: %u\tValue: %u\n", address, cc, value);
DEBUG_MSG("MSG: %s\tChannel: %u\t\tCC: %u\tValue: %u\n", address, midichannel, cc, value);
MIDI.sendControlChange(cc, value, midichannel);
} else {
}
else
{
DEBUG_MSG("Cannot handle: %s\n", address);
}
}
void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t value) {
void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t val)
{
char buffer[1024];
snprintf(buffer, sizeof(buffer), "/midi/cc/%u/%u", channel, number);
OSCMessage msg = OSCMessage(buffer);
msg.add(value * 1.0);
DEBUG_MSG("MidiCCToOsc: %s %f\n", buffer, val * 1.0);
DEBUG_MSG("MidiCCToOsc: %s %f", buffer, value * 1.0);
if (clientIP)
{
OSCMessage msg = OSCMessage(buffer);
msg.add(val);
udp.beginPacket(clientIP, 8001);
udp.beginPacket(clientIP, 9000);
msg.send(udp);
udp.endPacket();
}
}
void loop() {
void loop()
{
OSCMessage msg;
uint8_t buffer[1024];
uint16_t outPort;
// Check if there are any OSC packets to handle
size_t size = udp.parsePacket();
if (size > 0 && size <= 1024) {
if (size > 0 && size <= 1024)
{
udp.read(buffer, size);
msg.fill(buffer, size);
if (!msg.hasError()) {
if (!msg.hasError())
{
DEBUG_OSC_MESSAGE(msg);
msg.route("/midi/cc", OSCToMidiCC);
} else {
//msg.route("/midi/sysex", OSCToMidiSYSEX);
//msg.route("/midi/note", OSCToMidiNote);
}
else
{
DEBUG_MSG("Error parsing OSC message: %d\n", msg.getError());
}
// Keep track of the client IP address for "talking back"
clientIP = udp.remoteIP();
udp.flush();
}
// Check if there are any CC messages from synth itself

@ -3,19 +3,19 @@
#ifdef DEBUG
#define DEBUG_ESP_PORT Serial1
#define DEBUG_MSG(...) Serial1.printf( __VA_ARGS__ )
#define DEBUG_ESP_PORT Serial
#define DEBUG_MSG(...) Serial.printf( __VA_ARGS__ )
#define DEBUG_OSC_MESSAGE(msg) \
do { \
char address[100]; \
msg.getAddress(address, 0, sizeof(address)); \
Serial1.printf("osc message: [%d] %s ", msg.size(), address); \
Serial.printf("osc message: [%d] %s ", msg.size(), address); \
for (int i = 0; i < msg.size(); i++) { \
if (msg.isFloat(i)) { Serial1.printf("f:%f\t", msg.getFloat(i)); } \
if (msg.isInt(i)) { Serial1.printf("i:%d\t", msg.getInt(i)); } \
if (msg.isFloat(i)) { Serial.printf("f:%f\t", msg.getFloat(i)); } \
if (msg.isInt(i)) { Serial.printf("i:%d\t", msg.getInt(i)); } \
} \
Serial1.printf("\n"); \
Serial.printf("\n"); \
} while(0);
#else

@ -0,0 +1,90 @@
MIDI message types
(hex)
8 = note_off
9 = note_on
A = aftertouch (ie, key pressure)
B = controller_change
C = program_change
D = channel_pressure
E = pitch_wheel
** additions **
sysex
start
stop
continue
mode
file
control_change messages
(dec)
0 bank_select (coarse)
1 modulation_wheel (coarse)
2 breath_controller (coarse)
4 foot_pedal (coarse)
5 portamento Time (coarse)
6 data_entry (coarse)
7 volume (coarse)
8 balance (coarse)
10 pan position (coarse)
11 expression (coarse)
12 effect_control_1 (coarse)
13 effect_control_2 (coarse)
16 general_purpose_slider_1
17 general_purpose_slider_2
18 general_purpose_slider_3
19 general_purpose_slider_4
32 bank_select (fine)
33 modulation_wheel (fine)
34 breath_controller (fine)
36 foot_pedal (fine)
37 Portamento Time (fine)
38 Data Entry (fine)
39 Volume (fine)
40 Balance (fine)
42 Pan position (fine)
43 Expression (fine)
44 Effect Control 1 (fine)
45 Effect Control 2 (fine)
64 Hold Pedal (on/off)
65 Portamento (on/off)
66 Sustenuto Pedal (on/off)
67 Soft Pedal (on/off)
68 Legato Pedal (on/off)
69 Hold 2 Pedal (on/off)
70 Sound Variation
71 Sound Timbre
72 Sound Release Time
73 Sound Attack Time
74 Sound Brightness
75 Sound Control 6
76 Sound Control 7
77 Sound Control 8
78 Sound Control 9
79 Sound Control 10
80 General Purpose Button 1 (on/off)
81 General Purpose Button 2 (on/off)
82 General Purpose Button 3 (on/off)
83 General Purpose Button 4 (on/off)
91 Effects Level
92 Tremulo Level
93 Chorus Level
94 Celeste Level
95 Phaser Level
96 Data Button increment
97 Data Button decrement
98 Non-registered Parameter (fine)
99 Non-registered Parameter (coarse)
100 Registered Parameter (fine)
101 Registered Parameter (coarse)
120 All Sound Off
121 All Controllers Off
122 Local Keyboard (on/off)
123 All Notes Off
124 Omni Mode Off
125 Omni Mode On
126 Mono Operation
127 Poly Operation
Loading…
Cancel
Save