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

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...

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 = {

        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);
			var input = args[0].value.split(' ').splice(7 ,35);
			input = input.join('');
			var trackname = '';
			for (var n = 0; n < input.length; n += 2) {
			trackname += String.fromCharCode(parseInt(input.substr(n, 2), 16));


        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
		    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}]})                 
		}).catch(err => {




			if (host === 'midi' && port === 'MCUtoOSC') {
            var [channel, control, value] =>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 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.

ok the error can be avoided by providing a fallback value in case match() returns null (which can't be accessed like an array with [0]):

const trackId = (trackname.match(/\d{4}/) || [])[0]

This won't prevent the id from being truncated though as it seems to happen somewhere upstream.

I think the error that you’re getting is because the characters that the custom module extracts using your code const trackID = trackname.match(/\d{4}/)[0]; are being used to search for the file but they may not be in the file name.

Have you looked at the sysex hex coming in - is it cubase truncating before it arrives at OSC or something at the OSC end truncating?

nono it all works. it's the same as you got earlier. the track name that arrives in custom module should be
'WWEnsemble [NISS] 0017' but it only arrives 'WWEnsemble'. i guess this is a cubase bug or a mackie thing. it's happening before the custom module.

thanks this stops the error

Bed time reading for me tonight then - mackie protocol

1 Like

All those problems with missing characters are most likely related to the way MCU protocol works. In order to save bandwidth, only altered parts of the LCD are send. E.g. see the post above with the bassoon. Since the string ‚bass‘ is unaltered it does not get resend. You need to buffer the display string and change its contents according to the incoming sysex message and evaluate the buffer afterwards. Which parts of the display string gets resend might vary from DAW to DAW.
Check the MCU doc regarding the display position byte(s).

1 Like

thanks for this @drjay

makes totally sence

thanks @Sylvain for this:

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_MODIFIED", locals.text.slice(72,101).join(""))}

with this script the tracknames work as expected.
how would i write this script widget in a custom module?

i get the track name displayed correctly but the value is still 'wrong'. and the value is what i need, so that's not the solution i guess:



      //  console.log('*******************Filter has fired*************')

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


        if (host === 'midi' && port === 'MCU_To_OSC' && address === '/sysex') {
            if (args[0].value.includes("f0 00 00 66 14 12")) { // sysex header for mackie lcd text

                var sysExVal = args[0].value
                var d = sysExVal.split(" ").slice(6).map(x => parseInt(x, 16))
                    pos = d[0], // first byte -> position // hex to int
                   text = d.slice(1).map(x => String.fromCharCode(x)) // rest -> updated characters
                text.pop() // drop sysex closing byte                    
               var trackName = text.join('')
                console.log('TrackName Joined = ' + trackName);



        return {address, args, host, port}
1 Like