OSC to MIDI

Discuss using the OSC interface to control SL

Moderator: jesse

Post Reply
Apmusic
Posts: 2
Joined: Thu Jan 10, 2019 4:47 pm

OSC to MIDI

Post by Apmusic »

Hi guys!
First post here! I am working on a headless rpi system that will include an Arduino (actually a teensy 3.5) to send midi control to SL and I’d like to be able to incorporate some Adafruit neopixel rings to show loop status, but it appears the only way to read the status of loopers is via OSC. I have no idea how to use OSC, and specifically don’t know how to read OSC messages on my Arduino in order to control the NeoPixel rings.

Is there a program I can run on my rpi that will convert OSC messages to midi cc? Or is there a better way to do this?

Thanks guys!

Freasy
Posts: 60
Joined: Fri Aug 29, 2014 5:37 pm

Re: OSC to MIDI

Post by Freasy »

Hello,
yes indeed, OSC seems to be the way to go if you want to have access to full controllabilty.
I'm pretty sure there are "OSC-to-MIDI-Converters" but I'm afraid they all cost money (at least they did when I searched one some years ago). So I programmed my own. So if you can do a little bit C++, you would need a library that can send and receive MIDI and a library that can send and receive OSC and just need some code to "transfer" it. I would send you my program, but since everything is more or less hardcoded it wouldn't really work for you...
So a "converter" would look something like this:

Code: Select all

void midiReceive(status, int; number, int; value, int) {
switch(status) {
176: {sendOSC("/0/record_"+number+"/"); return;} //this would be a command change that triggers a record of loop "number", if every command change was for recording loops only
}
And vice-versa. Of course this code wouldn't really work probably, that's just a little bit from the top of my head (if it's even correct) to show you, what I mean. You can basically do what you want with the messages you get, you just have to tell your rpi or arduino.
I hope I could help a little bit.

Apmusic
Posts: 2
Joined: Thu Jan 10, 2019 4:47 pm

Re: OSC to MIDI

Post by Apmusic »

Thanks for your answer Freasy!

I guess I know less about this than I thought I knew. Is there any documentation I can find to follow. Here's what I'm trying to accomplish. I've got 4 of the NeoPixel rings, and I'm trying to use 4 loopers and have the rings display the status of each looper. For example, I want to read the status of Looper 1. If the status is "Recording" then I want to turn the neopixel Red. Then when that loop is "playing" I want to have the neopixel circle around the ring to show where in the loop I am (like a small clock in a way)

I've learned all my code from the perspective of getting button presses to send midi messages and don't really know anything about OSC or how it works.

Any help to get me up to speed on how to code this thing (For Dummies) would be amazing! :P

Thanks!

Freasy
Posts: 60
Joined: Fri Aug 29, 2014 5:37 pm

Re: OSC to MIDI

Post by Freasy »

I read your first post again and if you don't need MIDI for your NeoPixels, you won't need MIDI at all! You can do everything with OSC, you would have to set up a network connection though. OSC works via network with IPs and stuff. So I don't know exactly how OSC would work with an USB-plugged-in Ardiuno or so. Maybe there is a way to establish a network via USB?
OSC for itself is a very simple thing. You have a server and one or multiple clients. Basically a client tells the server "oi, send me all your data!" and the server will do so.
So an OSC-message will look something like this: "/sl/1/hit record" which would hit the record-button of sl. When record was started, SL will send "/sl/1/state 2" (I believe) and you could determine your actions by knowing, that state 2 means "recording".
For your better understanding here is a little bit of my code which receives the OSC message, deconstructs it and sends it to everything I want to:

Code: Select all

int oscEingang(const char *path, const char *types, lo_arg ** argv, int argc, void *data, void *user_data)
{
    std::string pfad = path;
    if (pfad.find("/sl/") == 0) {
    	std::string typetag;
    	for (int i = 0; i < argc; i++) {
    		typetag += types [i];
    	}
    	if (typetag == "isf") {
    		int loopID = argv[0]->i;
    		std::string befehl(&argv[1]->s);
    		float parameter = argv[2]->f;
    		behringer.BefehlOSC(loopID, befehl, parameter);
    		iPad.BefehlOSC(loopID, befehl, parameter);
    		launchPad.BefehlOSC(loopID, befehl, parameter);
    		if (logOSC) if (befehl != "in_peak_meter") std::cout << "OSC-Nachricht von SL: " << loopID << ", " << befehl << ", " << parameter << std::endl;
    	} else {
    		if (logEvents) std::cout << "FEHLER: unbekannter Typetag: " << typetag << std::endl;
    	}
    } else {
    	if (logEvents) std::cout << "FEHLER: unbekannter OSC-Pfad: " << pfad << std::endl;
    }
    fflush(stdout);
    return 1;
}
So it receives a message like "/sl/1/state 2" and deconstructs it this way:
"/sl/" isn't needed since there is only instance of sooperlooper, so it's only used to determine whether it is a message from sooperlooper or not.
The typetag means there is a message with an integer datatype, a string datatype and a float datatype. The integer would be the loopnumber (/sl/1/state 2), the string would be the "command" (/sl/1/state 2) and the float the value of that (/sl/1/state 2). It then takes those variables and stuffs them into "BefehlOSC" so it can be used by a function which will do whatever you want with this information. Note that nearly all messages will be in this format! So if you would want to use the value (which is called "parameter" in the next code-snippet) as an integer, you would have to convert it first into integer. Same thing for sending OSC, if you have an integer value to send, you would have to convert it to float first.

And here this received message tells a Novation LaunchPad (which does only accept MIDI, you could do anything you want with the data though) what to do when receiving the state of a looper:

Code: Select all

void LaunchPadSteuerung::BefehlOSC(int loopID, std::string befehl, float parameter) {
	if (befehl == "state") {
		switch ((int)parameter) {
			case 0: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpStumm + loopID, rot1);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpSolo + loopID, gruen1);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpAufnahme + loopID, gelb1);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpOverdub + loopID, aus);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpReplace + loopID, aus);
					aufnahmeStatus[loopID] = 0; break;
			case 1: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpAufnahme + loopID, blinkrot2);
					aufnahmeStatus[loopID] = 1; break;
			case 2: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpAufnahme + loopID, rot3);
					aufnahmeStatus[loopID] = 2; break;
			case 3: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpAufnahme + loopID, blinkrot2);
					aufnahmeStatus[loopID] = 3; break;
			case 4: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpAufnahme + loopID, gruen2);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpSolo + loopID, gruen1);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpStumm + loopID, rot1);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpOverdub + loopID, gelb1);
					midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpReplace + loopID, rot1);
					aufnahmeStatus[loopID] = 4; break;
			case 5: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpOverdub + loopID, gelb3);
					aufnahmeStatus[loopID] = 5; break;
			case 10: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpStumm + loopID, rot3); break;
			case 20: midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpStumm + loopID, rot3); break;
		}
	} else if (befehl == "is_soloed") {
		if ((int)parameter == 1) midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpSolo + loopID, gruen3);
		else midiBefehl(LaunchPadAusgang, portAusIDLP, noteOn, lpSolo + loopID, gruen1);
	}
}
So here I transfer all variables from the OSC message into a MIDI command to be understood by the LaunchPad. So "/sl/1/state 2" will make the button which correlates to the looper id (called loopID here + a constant which will "push" the desired command into the right place) shine in bright red.

Since I'm german I coded everything possible in german, so I hope you can understand what I did there.

There are a couple of OSC tutorials in the web and there is an OSC-documentation of sooperlooper as well which can be found here. A library for OSC on Arduino would be this (I used another one apparently - I coded this quite a while ago).

Good luck and a lot of patience!

Post Reply