Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"terminal.integrated.env.linux": {
"PATH": "/home/tadas/.platformio/penv/bin:/home/tadas/.platformio/penv:/home/tadas/bin:/home/tadas/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",
"PLATFORMIO_CALLER": "vscode"
}
},
"files.trimTrailingWhitespace": true
}
Binary file modified example.touchosc
Binary file not shown.
112 changes: 107 additions & 5 deletions src/OSC2Midi.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,90 @@
#include <OSCData.h>
#include <SoftwareSerial.h>
#include <MIDI.h>
#include <Ticker.h>

WiFiUDP udp;

/**
source address of last OSC message for midi2osc messages.
source address of last OSC message for midi2osc messages.
*/
IPAddress clientIP;

SoftwareSerial midiSerialPort(4, 5); // RX, TX pins to be used for ss port

MIDI_CREATE_INSTANCE(SoftwareSerial, midiSerialPort, MIDI);

/**
* Glide setting for smooth transition from one control value to another.
*/
float glide = 1.0;

/**
* Scheduler for running the glide task.
*/
void glideTask();
Ticker glideScheduler(glideTask, 50, 0, MILLIS);

class ControlChangeState {
public:
uint8_t targetValue;
float currentValue;
bool complete = true;
bool initialized = false;

void setTarget(uint8_t newTargetValue) {
if (initialized) {
targetValue = newTargetValue;
complete = false;
} else {
initialized = true;
complete = true;
targetValue = newTargetValue;
currentValue = (float)newTargetValue;
}
}

uint8_t getCurrentValue() {
return round(currentValue);
}

/**
* Update the control value state, i.e. move towards the desired target.
*
* @return bool true if state has changed and coresponding midi CC value should be sent
*/
bool update(float glideSetting) {
uint8_t previousValue = getCurrentValue();

// No need to send midi cc if state is complete
if(complete) {
return false;
}

if(round((float)targetValue - currentValue) != 0) {
if((float)targetValue > currentValue) {
currentValue += glideSetting;
} else {
currentValue -= glideSetting;
}
} else {
// We've reached zero difference between current and target
complete = true;
}

if(previousValue != getCurrentValue()) {
return true;
} else {
return false;
}
}
};

ControlChangeState ccStates[128];

void setup() {
delay(1000); // very important bit for Access Point to work properly... ¯\_(ツ)_/¯

Serial.begin(115200);
Serial.println();
Serial.println("Hello OSC2Midi!");
Expand All @@ -39,6 +108,8 @@ void setup() {
pinMode(4, INPUT_PULLUP);
pinMode(5, OUTPUT);
midiSerialPort.begin(31250);

glideScheduler.start();
}

void OSCToMidiCC(OSCMessage &msg, int offset) {
Expand All @@ -53,26 +124,30 @@ void OSCToMidiCC(OSCMessage &msg, int offset) {
value = round(msg.getFloat(0));
value = value > 127 ? 127 : value;
Serial.printf("MSG: %s\tCC: %u\tValue: %u\n", address, cc, value);
MIDI.sendControlChange(cc, value, 1);
//MIDI.sendControlChange(cc, value, 1);
ccStates[cc].setTarget(value);
} else if (msg.size() == 2 && msg.isFloat(0) && msg.isFloat(1)) {
// XY pad, two values
cc = getCC(address, 0);
value = round(msg.getFloat(0));
value = value > 127 ? 127 : value;
Serial.printf("MSG: %s\tCC: %u\tValue: %u\n", address, cc, value);
MIDI.sendControlChange(cc, value, 1);
// MIDI.sendControlChange(cc, value, 1);
ccStates[cc].setTarget(value);

cc = getCC(address, 1);
value = round(msg.getFloat(1));
value = value > 127 ? 127 : value;
Serial.printf("MSG: %s\tCC: %u\tValue: %u\n", address, cc, value);
MIDI.sendControlChange(cc, value, 1);
// MIDI.sendControlChange(cc, value, 1);
ccStates[cc].setTarget(value);
} else {
Serial.printf("Cannot handle: %s\n", address);
}
}

uint8_t getCC(char *str, int index) {
// TODO: no need to use String (which uses heap allocations)
String s = String(str);

// drop the first /
Expand Down Expand Up @@ -114,6 +189,30 @@ void MidiCCToOSC(uint8_t channel, uint8_t number, uint8_t value) {
udp.endPacket();
}

void OSCSetSettings(OSCMessage &msg, int offset) {
char address[100] = { 0 };

msg.getAddress(address, offset, sizeof(address));

if (msg.size() == 1 && msg.isFloat(0)) {
if (strcmp(address, "/glide") == 0) {
glide = msg.getFloat(0);
Serial.printf("Setting glide to: %f\n", glide);
}
} else {
Serial.printf("Cannot handle: %s\n", address);
}
}

void glideTask() {
for(uint8_t cc = 0; cc < 128; cc++) {
if(ccStates[cc].update(glide)) {
Serial.printf("Glide CC: %u\tValue: %u\n", cc, ccStates[cc].getCurrentValue());
MIDI.sendControlChange(cc, ccStates[cc].getCurrentValue(), 1);
}
}
}

void loop() {
OSCMessage msg;
uint8_t buffer[1024];
Expand All @@ -126,6 +225,7 @@ void loop() {

if (!msg.hasError()) {
msg.route("/midi/cc", OSCToMidiCC);
msg.route("/midi/settings", OSCSetSettings);
} else {
int error = msg.getError();
Serial.print("error: ");
Expand All @@ -138,5 +238,7 @@ void loop() {

// Check if there are any CC messages from synth itself
MIDI.read();

glideScheduler.update();
}