THERE IT IS: the ultimate custom module for cubase users (at least for me)

To get the full name in MCU mode, i use this code in a script widget (code found somewhere here):

locals.text = locals.text || Array(112).fill(" ") // persistent variable to store full text

if (value.includes("f0 00 00 66 14 12")) { // sysex header for mackie lcd text

var d = value.split(" ").slice(6).map(x=>parseInt(x, 16)), // hex to int
    pos = d[0], // first byte -> position
    text = d.slice(1).map(x=>String.fromCharCode(x)) // rest -> updated characters

text.pop() // drop sysex closing byte

// update characters
for (var i = 0; i < text.length; i++) {
  locals.text[i + pos] = text[i] // update 
}


// update text widget
set("lcd", locals.text.slice(72,82).join(""))}

I modified the last line to focus on track name.
The original last line is :


// update text widget
set("lcd", locals.text.slice(0,56).join("") + "\n" + locals.text.slice(56).join(""))

Then, i just have a button to send note 45 (strip mode).

Just to be clear - does this work as follows -

  • you select a track in cubase
  • cubase fires out sysex on an MCU remote channel
  • OSC custom module reads the full track name

??

How many characters from the track name can you get into OSC?

this is what we get. 29 chars:

if (host === 'midi' && port === 'MCUtoOSC' && address === '/sysex') {

			
			

			if (args[0].value.includes('f0 00 00 66 14 12')) {
			
			
			
			// CUBASE TRACKS HEX TO ASCII STRING
            // send('midi', 'OSCtoMCU', '/note', 1,44,1);   EDIT: is not needed
			var input = args[0].value.split(' ').splice(7 ,35);
			input.pop();			
			input = input.join('');
			var trackname = '';
			for (var n = 0; n < input.length; n += 2) {
			trackname += String.fromCharCode(parseInt(input.substr(n, 2), 16));
			}
        }
            console.log(trackname)

:slight_smile: was about to reply
Using a mackie port in cubase, we just isolate a part of the lcd screen.


mackie2

No custom module, just a script widget with this, and a text wigdet to get the text:

locals.text = locals.text || Array(112).fill(" ") // persistent variable to store full text

if (value.includes("f0 00 00 66 14 12")) { // sysex header for mackie lcd text

var d = value.split(" ").slice(6).map(x=>parseInt(x, 16)), // hex to int
    pos = d[0], // first byte -> position
    text = d.slice(1).map(x=>String.fromCharCode(x)) // rest -> updated characters

text.pop() // drop sysex closing byte

// update characters
for (var i = 0; i < text.length; i++) {
  locals.text[i + pos] = text[i] // update 
}


// update text widget
set("lcd", locals.text.slice(72,102).join(""))}

This is awesome - any chance you can post your session file?

Don't know if your asking me or gcat, so...

Here's my lcd :
Mackie lcd.json (14.4 KB)

Just have sysex "enabled" here :
sysex

1 Like

Sorry you're goiing to have to talk me through this one as I'm getting strange behaviour...

This is the code in my cm

oscInFilter:function(data){


        var { address, args, host, port } = data


        // MCU TEST AREA HERE

        if (host === 'midi' && port === 'MCU' && address === '/sysex') {

            if (args[0].value.includes('f0 00 00 66 14 12')) {



                // CUBASE TRACKS HEX TO ASCII STRING
                // send('midi', 'OSCtoMCU', '/note', 1,44,1);   EDIT: is not needed
                var input = args[0].value.split(' ').splice(7, 35);
                input.pop();
                input = input.join('');
                var trackname = '';
                for (var n = 0; n < input.length; n += 2) {
                    trackname += String.fromCharCode(parseInt(input.substr(n, 2), 16));
                }
            }
            console.log('MCU Track name ' + trackname)

        }
//END MCU TEST AREA
       
}

This is my set up in cubase:

This is the tracks selected:

image

I get a lot of sysex when I enable or apply the MIDI port in the studio step up but I don't get anything when changing tracks.

Have I missed a step?

Sorry @Sylvain I was going to try to implement in custom module but I guess I have some more fundamental issues...

The code looks good. i'm not sure try to comment out

and add:

return {address, args, host, port}

and what i saw your MCU input is disabled in cubase. just connect it to OSCtoMCU. and one last thing, be sure OSC runs before cubase. shut down cubase and try again.

and now i'm out. sorry

dealing with covid-19. i guess i'm off for a few days.

C U

Really sorry to hear that, hope you and your close ones are ok

it just hit me this afternoon…my kids are both having it… so i guess they brought it home.
but all good. just massiv headacke and a bit of fever.

does your cm work now?

It’s been through our house too but I’ve managed to avoid so far (touch wood)

Xml rip does and have just put something on the other thread about a slight change of direction to limit system resource.

Can’t get the sysex thing to work at all currently but I’ve got so much going on with midi I/o on my system that I’ve got no idea on that right now!

Still trying to resolve this problem. I thought that I wasn't getting any sysex from cubase into OSC so closed Cubase to restart.

I then ended up with the following which suggests I am but it's not sending the track names...

FINALLY!
this aaaawwwsome. it now works all as it supposed to. i'm not sure 'bout the trackID yet. i guess putting the identifier at the end of the cubase track name wont work because it can get truncated or if it's shorter than 29 chars it also wont work because the rest of the track name will be filled up with spaces and therefor custom module wont find any digits at the end.

the only really annoying thing is this. look at the tracknames. this happens when i switch between these two. it happens elsewhere also:


this is the custom module so far:

var buttonGrid, labelTexts;

var buttons = {
		'b1': '/b1/show','b2': '/b2/show','b3': '/b3/show','b4': '/b4/show','b5': '/b5/show','b6': '/b6/show','b7': '/b7/show','b8': '/b8/show','b9': '/b9/show','b10': '/b10/show',
		'b11': '/b11/show','b12': '/b12/show','b13': '/b13/show','b14': '/b14/show','b15': '/b15/show','b16': '/b16/show','b17': '/b17/show','b18': '/b18/show','b19': '/b19/show','b20': '/b20/show',
		'b21': '/b21/show','b22': '/b22/show','b23': '/b23/show','b24': '/b24/show','b25': '/b25/show','b26': '/b26/show','b27': '/b27/show','b28': '/b28/show','b29': '/b29/show','b30': '/b30/show',
		'b31': '/b31/show','b32': '/b32/show','b33': '/b33/show','b34': '/b34/show','b35': '/b35/show','b36': '/b36/show','b37': '/b37/show','b38': '/b38/show','b39': '/b39/show','b40': '/b40/show',
		'b41': '/b41/show','b42': '/b42/show','b43': '/b43/show','b44': '/b44/show','b45': '/b45/show','b46': '/b46/show','b47': '/b47/show','b48': '/b48/show','b49': '/b49/show','b50': '/b50/show',
		'b51': '/b51/show','b52': '/b52/show','b53': '/b53/show','b54': '/b54/show','b55': '/b55/show'
	}
	
var labels = {
		'b1': '/b1/label','b2': '/b2/label','b3': '/b3/label','b4': '/b4/label','b5': '/b5/label','b6': '/b6/label','b7': '/b7/label','b8': '/b8/label','b9': '/b9/label','b10': '/b10/label',
		'b11': '/b11/label','b12': '/b12/label','b13': '/b13/label','b14': '/b14/label','b15': '/b15/label','b16': '/b16/label','b17': '/b17/label','b18': '/b18/label','b19': '/b19/label','b20': '/b20/label',
		'b21': '/b21/label','b22': '/b22/label','b23': '/b23/label','b24': '/b24/label','b25': '/b25/label','b26': '/b26/label','b27': '/b27/label','b28': '/b28/label','b29': '/b29/label','b30': '/b30/label',
		'b31': '/b31/label','b32': '/b32/label','b33': '/b33/label','b34': '/b34/label','b35': '/b35/label','b36': '/b36/label','b37': '/b37/label','b38': '/b38/label','b39': '/b39/label','b40': '/b40/label',
		'b41': '/b41/label','b42': '/b42/label','b43': '/b43/label','b44': '/b44/label','b45': '/b45/label','b46': '/b46/label','b47': '/b47/label','b48': '/b48/label','b49': '/b49/label','b50': '/b50/label',
		'b51': '/b51/label','b52': '/b52/label','b53': '/b53/label','b54': '/b54/label','b55': '/b55/label'
	}
	
module.exports = {

    oscInFilter:function(data)
	{
        var {address, args, host, port} = data         

		if (host === 'midi' && port === 'MCUtoOSC' && address === '/sysex') {

			if (args[0].value.includes('f0 00 00 66 14 12')) {
			send('midi', 'OSCtoMCU', '/note', 1 ,44 ,127);
			
			
            // CUBASE TRACKS HEX TO ASCII STRING
			var input = args[0].value.split(' ').splice(7 ,35);
			input.pop();			
			input = input.join('');
			var trackname = '';
			for (var n = 0; n < input.length; n += 2) {
			trackname += String.fromCharCode(parseInt(input.substr(n, 2), 16));
			} 
        
            
        console.log(trackname)
       


		// RECEIVE TRACKNAMES IN OSC DYNAMICALLY

        if (trackname) {
            receive('/EDIT', 'trackname', {value: trackname})

        }


		
        /*********** GENERATING EXPRESSION MAPS DYNAMCALLY - YESSSSS!!!!!! ************/

		const path = nativeRequire('path');
		const fs = nativeRequire('fs');
		const xml2js = nativeRequire('xml2js');
		const glob = nativeRequire('glob');
		const mapFiles = glob.sync(path.join(__dirname, '../../Steinberg/Expression Maps/*.expressionmap'));
		const trackID = (trackname.match(/[0-9]{4}$/) || [])[0] //
		

		async function buildMap() {

		    artArr = [];
		    artColor = [];
		    for (const mapFile of mapFiles) {
		        if (mapFile.includes(trackID)) {
		            const parser = new xml2js.Parser({
		                explicitArray: false,
		                mergeAttrs: true
		            }); // 'mergeAttrs: true' was essential

		            console.log('Selected Map:  ' + mapFile);

		            const data = await fs.promises.readFile(mapFile);
		            const result = await parser.parseStringPromise(data);
		            const art = result.InstrumentMap.member[1].list.obj;
		            for (let i = 0, len = art.length; i < len; i++) {
		                
		                artArr[i] = art[i].member[1].string.value
		                artColor[i] = art[i].int.value
		            }
		            // once we've run this inner for loop once, we can't
		            // run it again because it will overwrite values in 
		            // artArr and artColor, so we break the outer for loop
		            break;
		        }
		    }
		    return { artArr, artColor };
		}

		buildMap().then(result => {
		    // use your results here
console.log('TRACKID: ' + trackID);
		    

		    /************* BUTTONS GENERATING DYNAMICALLY - YESSSSSS!!! AGAIN ****************/

		    buttonGrid = artArr.length
		            labelTexts = Object.values(artArr);
		            for (i = 0; i < buttonGrid; i++)
		                {
		                var label = labelTexts[i]
		                receiveOsc({address: Object.values(buttons)[i], args: [{type: 'i', value: 1}]})
		                receiveOsc({address: Object.values(labels)[i], args: [{type: 's', value: label}]})
		                }
		                                
		            for (i = buttonGrid; i < 55; i++)
		                {
		                receiveOsc({address: Object.values(buttons)[i], args: [{type: 'i', value: 0}]})                 
		                }
		            return
		            
		         
		    
		}).catch(err => {
		    console.log(err);
		});
		
		}

    }    
            

            

			// CUBASE BEATS & BARS

			if (host === 'midi' && port === 'MCUtoOSC') {
            
            var [channel, control, value] = data.args.map(x=>x.value)
            
            if (control > 63 && control < 74) {
                
                var digit = 73 - control,
                    msb = value >> 4,
                    val = value & 0xF
                
                if (msb >> 2) val += '.'
                if (!(msb & (1 << 1))) val = ''

                // digit -> digit
                // val -> digit's value 
                
                // assuming 10 text widgets with incrementing preArgs 
                receive('/timecode', digit, val)
				return
                }
        }
	return data
	},
}
if (args[0].value.includes('f0 00 00 66 14 12')) {
			send('midi', 'OSCtoMCU', '/note', 1 ,44 ,127);

i'm not sure if this is needed. but this makes sure we are in the EQ Mackie mode. is your MCU input connected in cubase? try to reset the mcu in cubase.this sometimes helps. also try switch this back an forth:

mine is set to cubase. sometimes i have to change the custom module with another one, start and stop the server and load the original one again. that's what i would try. hope something helps.

Got it.

But now have an issue with truncated track names...

I think you noted this above where the characters get dropped if they were in the previous name.

sometimes it happens with the same amount of characters. i have no idea from where this is coming. and every time this happens (if trackID gets truncated) OSC throws an error and we have to start the server again.
didn't find a work around yet.

by the way, thank you very much so far for putting so much effort into this. i really appreciate that!

What's the error exactly ?

i use const trackID = trackname.match(/\d{4}/)[0]; to get my identifier and if the track gets truncated but within these 29 chars i get:

Bildschirmfoto 2022-01-24 um 23.23.36

these are the related cubase tracks in this example:

it should be 'WWEnsemble [NISS] 0017' but get's truncated. i guess it's because the two tracks have the same amount of chars before the space. there are several places in the cubase template this happens.