What's new

Open Stage Control (Tutorial) - An alternative to Lemur and TouchOSC

I've started on a little utility that parses Cubase expression maps to extract information about the articulation, keyswitch, and UACC info. It's in python but you might find it helpful:


easiest way to run it is to just call it from the command line like:
>python expression-map-parser.py {path-to-directory-containing-expression-maps} --csv

(note the "--csv" flag at the end). This will parse every expression map contained in the directory and dump a CSV with all the articulation, keyswitch, and UACC details for each expression map. I've been using it to design the UI for my OSC articulation controller, like you I have found it very useful to get a list of all the articulations possible in a given library without having to manually type them all out.

The script does other stuff too if you don't add the "--csv" flag but that is fairly specific to my OSC configuration and requires a few more variables to be set up. I posted a quick and dirty blurb about how that works here https://vi-control.net/community/th...nd-cubase-expression-maps.114281/post-4912751 and I may eventually get around to cleaning the code up a bit and properly "releasing" the OSC controller plus associated utility scripts at some point.
 
I just started to look into making an OSC controller as well. A generic articulation switcher would be my main goal. Did any of you find a generic way of identifying which track is selected in Cubase, without having to manually put some MIDI transformer or plugin sending specific identifier values on each and every track?
 
I just started to look into making an OSC controller as well. A generic articulation switcher would be my main goal. Did any of you find a generic way of identifying which track is selected in Cubase, without having to manually put some MIDI transformer or plugin sending specific identifier values on each and every track?
I have not found a way to do this. I have a poly pressure input transformer on each track. OSC is configured to send a PP 0 message whenever *any* track is selected (which is routed to the selected track automatically of course) and each track transforms PP0 into a different channel/value combo and sends it back to OSC. It's not as painful as it sounds if you make your OSC controller configuration driven. For example in mine, the channel identifies the "library" and the PP value identifies which instrument number within that library, so the incoming PP message is decoded and a single JS script is called that sets up the UI appropriately.
 
I have not found a way to do this. I have a poly pressure input transformer on each track. OSC is configured to send a PP 0 message whenever *any* track is selected (which is routed to the selected track automatically of course) and each track transforms PP0 into a different channel/value combo and sends it back to OSC. It's not as painful as it sounds if you make your OSC controller configuration driven. For example in mine, the channel identifies the "library" and the PP value identifies which instrument number within that library, so the incoming PP message is decoded and a single JS script is called that sets up the UI appropriately.
Ok, I will probably have to consider such a solution. Thanks for your answer!
 
If you are looking for a quick and stable solution, I suggest to use the one mentioned above with the transformer, this way it only works with midi tracks though. In order to use it with instrument tracks, you‘ll have to use a plugin in the FX chain and get the values via the generic remote.
But there are other solutions as well, using e.g. the MCU protocol, or EUCON. Eucon is not feasible imo, since the protocol is not published. I am currently using MCU, but the solution it is a little bit fiddly; it takes some effort to make it run robust.
 
If you are looking for a quick and stable solution, I suggest to use the one mentioned above with the transformer, this way it only works with midi tracks though. In order to use it with instrument tracks, you‘ll have to use a plugin in the FX chain and get the values via the generic remote.
But there are other solutions as well, using e.g. the MCU protocol, or EUCON. Eucon is not feasible imo, since the protocol is not published. I am currently using MCU, but the solution it is a little bit fiddly; it takes some effort to make it run robust.
Aha, I'll look into MCU and see how that could be utilized also then. Thanks!
 
I've started on a little utility that parses Cubase expression maps to extract information about the articulation, keyswitch, and UACC info. It's in python but you might find it helpful:


easiest way to run it is to just call it from the command line like:
>python expression-map-parser.py {path-to-directory-containing-expression-maps} --csv

(note the "--csv" flag at the end). This will parse every expression map contained in the directory and dump a CSV with all the articulation, keyswitch, and UACC details for each expression map. I've been using it to design the UI for my OSC articulation controller, like you I have found it very useful to get a list of all the articulations possible in a given library without having to manually type them all out.

The script does other stuff too if you don't add the "--csv" flag but that is fairly specific to my OSC configuration and requires a few more variables to be set up. I posted a quick and dirty blurb about how that works here https://vi-control.net/community/th...nd-cubase-expression-maps.114281/post-4912751 and I may eventually get around to cleaning the code up a bit and properly "releasing" the OSC controller plus associated utility scripts at some point.
That's a very convenient tool, thank you for sharing. Although for some reason it does not actually work as intended when executed. No CSV file is created and the command line simply prints: ExpressionMapName, Articulation, Keyswitch, UACC

I had re-installed python from the Windows Store just before trying it out, could critical dependencies be missing? Although no errors were thrown mentioning any missing dependencies.
The commands I tried in sequence, using the command line tool in Administrator mode:
1. D: 2. cd <locationContainingThePythonScript> 3. python expression-map-parser.py {D:\Example\Directory\For\ExpressionMaps} --csv
Removing the brackets for the directory causes the script to throw an AttributeError at line 376, so I assume they're meant to be there. I'll dive into python some more later if need be to better understand the language's semantics.
 
3. python expression-map-parser.py {D:\Example\Directory\For\ExpressionMaps} --csv[/ICODE]
Removing the brackets for the directory causes the script to throw an AttributeError at line 376, so I assume they're meant to be there. I'll dive into python some more later if need be to better understand the language's semantics.
I should clarify, the brackets aren't needed (they are just there to indicate that you have to fill in the argument yourself). You do have to put the path in quotes though. For example this is the exact invocation I used on Windows:

python expression-map-parser.py "E:\Audio\Cubase Projects\Expression Maps\Spitfire Audio\Olafur Arnalds Chamber Evolutions"
 
I should clarify, the brackets aren't needed (they are just there to indicate that you have to fill in the argument yourself). You do have to put the path in quotes though. For example this is the exact invocation I used on Windows:

python expression-map-parser.py "E:\Audio\Cubase Projects\Expression Maps\Spitfire Audio\Olafur Arnalds Chamber Evolutions"
That's what I assumed to be the case at first. But when I tried that it also resulted in the following error:
File "D:\locationContainingThePythonScript\expression-map-parser.py", line 376, in <module> articulation = articulationElement.find(".//*[@name='description']").get('value').strip() AttributeError: 'NoneType' object has no attribute 'get'

Seems to be related to a function call/assignment error. Any ideas? Faulty expression map description formatting on my end?
 
If you are looking for a quick and stable solution, I suggest to use the one mentioned above with the transformer, this way it only works with midi tracks though. In order to use it with instrument tracks, you‘ll have to use a plugin in the FX chain and get the values via the generic remote.
But there are other solutions as well, using e.g. the MCU protocol, or EUCON. Eucon is not feasible imo, since the protocol is not published. I am currently using MCU, but the solution it is a little bit fiddly; it takes some effort to make it run robust.

Yes agre e - very stable but as you say massive limitation is that it only works for midi tracks - simply because instrument temracks don’t have a midi send, only insert. It’s my number one on the list of things I wish cubase had!

You mention about a plug-in on the insert but I’m not aware of one? Are you?!
 
Seems to be related to a function call/assignment error. Any ideas? Faulty expression map description formatting on my end?
Your expression map is probably ok, it's likely my script doesn't account for all the possibilities of the way expression maps can be formatted since I've only tested it with the ones I use myself.

Do you want to send me an example of an expression map that isn't working? I can try it locally and see if I can figure out what's going on....
 
Your expression map is probably ok, it's likely my script doesn't account for all the possibilities of the way expression maps can be formatted since I've only tested it with the ones I use myself.

Do you want to send me an example of an expression map that isn't working? I can try it locally and see if I can figure out what's going on....
That's nice of you to do, thank you. I'll send it over PM in that case.
 
Yes agre e - very stable but as you say massive limitation is that it only works for midi tracks - simply because instrument temracks don’t have a midi send, only insert. It’s my number one on the list of things I wish cubase had!

You mention about a plug-in on the insert but I’m not aware of one? Are you?!
The idea is to insert any plugin (does not matter which one) in the insert FX chain of the instrument, must be in the same slot for every instrument you want to control. Pick any number of vst Parameters (e.g. compressor treshhold, attack time). These n parameters and their values serve the same purpose as the callback values in the midi track transformer, so set them a value according to your scheme (e.g. attack 123 = VSL Oboe)
Assign them in a generic remote. Pseudocode for generic remote: selectedTrack->Insert1->ParameterN.
Mute the Plugin (can't remember, whether the generic remote still gets updated if you disable the plugin, just try it). Every time you click on a new instrument track, or change the parameter in the plugin the values are send out via the generic remote.
I created a small vst plugin for this purpose, which provides me with other track parameters, which is great, but only works with instrument, audio, fx etc. tracks. So this another dead end, since in Cubase cannot use custom midi plugins :sad:
 
Last edited:
The idea is to insert any plugin (does not matter which one) in the insert FX chain of the instrument, must be in the same slot for every instrument you want to control. Pick any number of vst Parameters (e.g. compressor treshhold, attack time). These n parameters and their values serve the same purpose as the callback values in the midi track transformer, so set them a value according to your scheme (e.g. attack 123 = VSL Oboe)
Assign them in a generic remote. Pseudocode for generic remote: selectedTrack->Insert1->ParameterN.
Mute the Plugin (can't remember, whether the generic reote still gets updated if you disable the plugin, just try it). Every time you click on a new instrument track, or change the parameter in the plugin the values are send out via the generic remote.
I created a small vst plugin for this purpose, which provides me with other track parameters, which is great, but only works with instrument, audio, fx etc. tracks. So this another dead end, since in Cubase cannot use custom midi plugins :sad:
This sounds really promising - are you saying that if you assign the plug-in parameter on each instrument track to say 116, and then for tracks 1-50(say) you put 116-1, 116-2 etc, when selected the track, the generic remktw will fire out 116-1, or 116-2 etc.

I suppose one way that cc value hits OSC it doesn’t need to distinguish between instrument and midis track?…
 
This sounds really promising - are you saying that if you assign the plug-in parameter on each instrument track to say 116, and then for tracks 1-50(say) you put 116-1, 116-2 etc, when selected the track, the generic remktw will fire out 116-1, or 116-2 etc.

I suppose one way that cc value hits OSC it doesn’t need to distinguish between instrument and midis track?…
Yes, it doesn‘t need to distinguish (if you don‘t want to). But since you can use as many virtual midi ports as you like, you could do all kinds of routings.
 
Yes, it doesn‘t need to distinguish (if you don‘t want to). But since you can use as many virtual midi ports as you like, you could do all kinds of routings.
So I've tested this - with limited success.

It works as described above with some modification of my custom module, but the difficulty that I have is that I have already implemented over 1000 midi tracks that use the challenge and response version that works as follows:

Select Track > Cubase Sends cc127 - OSC reads 127 and sends back cc126 - Cubase Transformer on track midi send coverts the cc126 to a specific CCxxx Val xx to define identify track and OSC variable in the custom module.

If I implement the plugin version on instrument tracks as suggested by @Drjay above I get two triggered cc values into OSC every track selection which can't be distinguished between an instrument track, or a midi track.

It's seems it's an "either or" on these solutions and since I have spent hours building the template on the challenge response version I'm reluctant to move away from it.

What I really need is a plug-in that can sit in the insert of the instrument track and work like the transformer on a midi-send, passing through the midi data that is used to play the instrument track and just filtering out the midi message that triggers it to send the identifying cc value.
 
Please have a look at my other post.
As always there are many solutions. Not sure, I fully understood the mechanism you are using, but I would not use the insert fx to get the track selected message. I would use the fx to get the instrument of the track. If you put your code to update the OSC gui in a subroutine, you can implement a different logic to evaluate the feedback from instrument and midi tracks and then call the subroutine. Additionally try a different ‚selected track‘ command in the generic remote. There must be three, midi mixer-> selected, VST Mixer -> selected and Mixer -> selected. 1st one only reacts on midi tracks, 2nd to all, 3rd to audio and midi, as far as I remember.
 
So I was/am using the Mihkel Zilmer method on my midi tracks.
And I understand the method you have outlined in your other post - I have set that up to work on my instrument tracks - using almost the same settings as you have to ensure I get it exactly right.

The difficulty I have having is I can't get the last bit as you describe to work - that being:
Please have a look at my other post.
Additionally try a different ‚selected track‘ command in the generic remote. There must be three, midi mixer-> selected, VST Mixer -> selected and Mixer -> selected. 1st one only reacts on midi tracks, 2nd to all, 3rd to audio and midi, as far as I remember.
No matter what I set each mixer to in each generic remote, they always both fire so I end up with 2 midi messages appearing into OSC and because they both arrive each time with various data, it is difficult, if not impossible to use any logic code to chose which routine to run.
 
Hard to tell :thumbsdown: Stupid question: Did you check the value? If you change from a midi track to an instrument track, the instrument cmd sends value 127 and the midi cmd value zero.
 
Last edited:
Top Bottom