Script: Automatically delete all tracks that show meter activity?

Hi everyone,

I was wondering if it would be possible to create a script (or a tool for Cubase) that automatically deletes every track whose level meter has shown any activity during playback, leaving only the tracks that are completely unused.

And if it isn’t possible at the moment, I think it would be an amazing feature to add!

I prepared a script you may want to check. While playing it checks for channels with meter change, and when the playback is stopped it allows us to select these particular tracks, so we can then obviously delete it.
I have two midi CC messages, one is for toggling whether the script should do these checks while play is active, and the other one is for executing the final actions after we have hit stop and checking is active. You can go further and instead of using this second cc you can immediately execute the action. You’re going to need a PLE, not just the script.

Script:

// @ts-nocheck
var midiremote_api = require('midiremote_api_v1')
var deviceDriver = midiremote_api.makeDeviceDriver("Test", "Find Tracks With Meter Activity", "mc")

var midiInput = deviceDriver.mPorts.makeMidiInput("anInput")


var detectionUnit=deviceDriver.makeDetectionUnit()

detectionUnit.detectSingleInput(midiInput)
    .expectInputNameEquals("Bome MIDI Translator 1")
    
var surface = deviceDriver.mSurface

var buttonFindOnOff=surface.makeCustomValueVariable("buttonFindOn")
buttonFindOnOff.mMidiBinding
	.setInputPort(midiInput)
	.bindToControlChange(0,0)

var lampFindOnOff=surface.makeLamp(0,0,1,1)
var labelFindOnOff=surface.makeLabelField(1,0,4,1)

var customLogTracks=surface.makeCustomValueVariable("customLogTracks")
customLogTracks.mMidiBinding
	.setInputPort(midiInput)
	.bindToControlChange(0,1)

var lampLogTracks=surface.makeLamp(0,1,1,1)
var labelLogTracks=surface.makeLabelField(1,1,4,1)

var customPlay=surface.makeCustomValueVariable("customPlay")

var customExecutePLE=surface.makeCustomValueVariable("customExecutePLE")

var mapping = deviceDriver.mMapping

var page=mapping.makePage("Default")
page.setLabelFieldText(labelFindOnOff,"Scanning")
page.setLabelFieldText(labelLogTracks,"Log Tracks")

var daMixer=page.mHostAccess.makeDirectAccess(page.mHostAccess.mMixConsole)
var tagMeterAll=4009
var tagChannelName=1024

var changedTracksSuffix="[altered]"
var pleCommandName="Select Tracks with [altered] suffix"

var mapChannels={}
var lstChannelsChanged=[]

var playing=0
var finding=0

buttonFindOnOff.mOnProcessValueChange=function(activeDevice,value,diff){
	
	if(value==1){
	
		finding=1-finding
		lampFindOnOff.mSurfaceValue.setProcessValue(activeDevice,finding)
	
	}
	
}

var customLogTracksHostValue=page.mCustom.makeHostValueVariable("customLogTracksHostValue")

page.makeValueBinding(customLogTracks,customLogTracksHostValue).mOnValueChange=function(activeDevice,activeMapping,value,diff){
	
	if(value==1){

		customLogTracks.setProcessValue(activeDevice,0)
		logAllChangedTracks(activeDevice,activeMapping)
	
	}

}

page.makeCommandBinding(customExecutePLE,"Process Project Logical Editor",pleCommandName)

page.makeValueBinding(customPlay,page.mHostAccess.mTransport.mValue.mStart)

page.mHostAccess.mTransport.mValue.mStart.mOnProcessValueChange=function(activeDevice,activeMapping,value){

	playing=value
	console.log("playing="+playing)

	if(finding==1 && playing==1){

		fetchAllTracks(activeMapping,true)

	}

}

function fetchAllTracks(activeMapping,initializing){
	
	if(initializing){

		mapChannels={}
		lstChannelsChanged=[]

	}
	
	var mixerID=daMixer.getBaseObjectID(activeMapping)
	var channelsCount=daMixer.getNumberOfChildObjects(activeMapping,mixerID)

	if(channelsCount>0){

		for(var i=0;i<channelsCount;i++){
			
			var channelID=daMixer.getChildObjectID(activeMapping,mixerID,i)
			
			if(lstChannelsChanged.indexOf(channelID)!=-1) continue
			
			var channelType=daMixer.getObjectTypeName(activeMapping,channelID)
			
			if(channelType=="OutputChannel") continue

			var meterValue=daMixer.getParameterProcessValue(activeMapping,channelID,tagMeterAll)

			if(initializing==false){

				var previousValue=mapChannels[channelID]

				if(previousValue!=meterValue){

					lstChannelsChanged.push(channelID)

				}

			}

			mapChannels[channelID]=meterValue

		}

	}

}

var pollCountMax=5
var pollCount=0

page.mOnIdle=function(activeDevice,activeMapping){

	
	if(finding==1 && playing==1){
	
		pollCount++
	
		if(pollCount==pollCountMax){
	
			pollCount=0
			fetchAllTracks(activeMapping,false)
	
		}
	
	}

}

function logAllChangedTracks(activeDevice,activeMapping){

	if(lstChannelsChanged.length>0){

		lstChannelsChanged.forEach(function(channelID){

			var currentName=daMixer.getParameterDisplayValue(activeMapping,channelID,tagChannelName)
			currentName=currentName+changedTracksSuffix
			daMixer.setParameterDisplayValue(activeMapping,channelID,tagChannelName,currentName)

		})

		customExecutePLE.setProcessValue(activeDevice,1)
	
	}
	
}

The necessary PLE named “Select Tracks with [altered] suffix”:

I have set the action to “Select” you can obviously change it to “Delete” after some testing.

Here’s a small video demonstrating the script:

Could you please share the use case you’re thinking of? I always like to learn new workflows.

Haha, awesome! I’m impressed — I’ll definitely test that.

Just to explain the idea: when you start from a template, you often begin with a huge number of tracks, but many of them end up never being used. I already use macros like “Delete Unused Tracks”, but they don’t work with auxiliary or group tracks because they don’t contain any MIDI or audio events.

The idea would be to detect which tracks have had any activity on their VU meters, and then automatically delete all the tracks that have never had any activity.

By the way, how do I install this script?

This explains the reason I asked you for your workflow. In the original post, you actually requested for the exact opposite, i.e. delete the tracks that show activity :smiley: So my script is actually wrong. Not a big deal, I can recreate it and upload an installer.

How do you plan to send the MIDI Messages asked from the MIDI Remote?

Yes, that’s exactly what I was trying to achieve: the goal is to delete the tracks that have never shown any activity on their VU meters, not the ones that have activity. Your clarification makes perfect sense, and that’s exactly what I need. Thank you for offering to recreate the script and provide an installer!

For the MIDI communication, I can use “loopMIDI Port” and “loopMIDI Port 1” as the MIDI ports for communication between the MIDI Remote and the script.

If that works with your script, that would be perfect for me.

Here’s the installation file (Go to Studio→MIDI Remote Manager, click on “Import Script”):
Test_Find Tracks With Meter Activity.midiremote (2.1 KB)

We use “loopMIDI Port” for input, output is not needed.

With CC 0 (at channel 0) we toggle the scanning process. When on and we hit play, channels will be scanned for activity. When off, no scanning happens.

With CC 1 (channel 0 again) we execute the process of renaming the channels with no activity, and we execute the PLE “Select Tracks with [no activity] suffix”.

Here’s the PLE:


If the PLE works as expected in your tests, you can then alter the action from “Select” to “Delete”.

Please make sure you test it thoroughly — I wouldn’t want you cursing me afterwards! :grinning_face_with_smiling_eyes:

It doesn’t work, and I think the problem might be the MIDI port name.

The port name needs to be exactly “loopMIDI Port”, not “loopMIDI”.

Does the script load? Do you see its UI in the lower zone of Cubase in the MIDI Remote Tab? If not, then surely it can be a port name thing. In my script I ask for “loopMIDI Port”. If you have “loopMIDI” you can load the script and set the port manually.

Sorry, I have my limitations when it comes to all this scripting stuff :wink:

I can see the script in the lower section of the Cubase window, so it seems to be loaded correctly.

For the MIDI commands, I am sending the CC messages from a Stream Deck.

One thing I noticed is that I cannot select MIDI Channel 0 on the Stream Deck. It only allows me to select channels 1 through 16.

Could this be the problem? If your script expects Channel 0, perhaps Channel 1 from the Stream Deck needs to be interpreted as Channel 0 internally.

The script is not really loaded, it’s disconnected. This means that most probably your other remotes already consume the loopMIDI port. So we have to alter the script to use another port, you can name one, no problem. You can add another loopMIDI Port, just give me the new name.

In scripting we have channels counting from 0, but since Stream Deck uses the 1-16 approach, you can simply choose 1. This will be “0” for the script, no problem.

“mc port in”

Cool. Just to avoid further installations, try this:

  • Open Studio→MIDI Remote Manager
  • Click on the tab Scripts
  • In the tableView that appears, navigate to Find Tracks With Meter Activity
  • In the lower section of this window, you will notice a Path section. Click on the button at the right end of it (Open Script Folder)
  • You will see in the file explorer that opens, a file named Test_findTracksWithActivity.js
  • Right click it, and open with Notepad
  • You will see a line reading .expectInputNameEquals(“loopMIDI Port”)
  • Change loopMIDI Port to mc port in , save the file and close this notepad window
  • In the MIDI Remote Manager, click the Reload Scripts button

If everything went OK, the script should now be active so you can test it out. Not at home to provide an updated versions, the above should work.

Thank you very much for your work and for this new version of the script. I ran a few tests, and here is what I found:

So, the script works!

I tested it on a project with many different track types. Overall, it worked quite well. However, it seems to cause Cubase to crash, even though I can continue using the program after the error window appears.

As you can see in the screenshots, it generally worked well, displaying [no activity] for tracks that had no activity.

There is one exception, however: MIDI 01 is marked as [no activity], even though it actually contains MIDI events and there was clearly activity on that track, as shown in the screenshot.

So, overall it works well, but there seems to be a minor issue with activity detection on that specific MIDI track.

It might be because this track is associated with the “ARP Quadra Mod wheel to Lead filter” instrument. Perhaps this type of MIDI/instrument track is handled differently by Cubase?

French

Merci beaucoup pour votre travail et pour cette nouvelle version du script. J’ai fait quelques tests et voici ce que j’ai constaté :

Alors, le script fonctionne !

Je l’ai testé sur un projet avec beaucoup de types de pistes différents. Globalement, il a plutôt bien fonctionné. Cependant, il semble faire planter Cubase, même si je peux continuer à utiliser Cubase après l’apparition de la fenêtre d’erreur.

Comme vous pouvez le voir sur les captures d’écran, il a globalement bien fonctionné en indiquant [no activity] sur les pistes qui n’avaient aucune activité.

Il y a cependant une exception : MIDI 01 est indiquée comme [no activity], alors qu’elle contient bien des événements MIDI et qu’il y a clairement eu de l’activité sur cette piste, comme vous pouvez le voir sur la capture d’écran.

Donc, globalement, ça fonctionne bien, mais il semble y avoir un petit problème dans la détection de l’activité sur cette piste MIDI en particulier.

C’est peut-être parce que cette piste est associée à l’instrument « ARP Quadra Mod wheel to Lead filter ». Peut-être que ce type de piste MIDI/instrument est géré différemment par Cubase ?

MIDI tracks do not have audio activity. Consequently, they are inevitably included among the tracks considered inactive and, therefore, among those suggested for deletion.

We can simply disable this check for MIDI tracks so that they are not factored into the detection process.

Regarding the Cubase exception error, it is difficult for me to draw a conclusion without a test project that reproduces the issue. It could potentially be a conflict with Cubase’s MIDI Remote engine, but the cause could just as easily be something completely different.

With a reproducible test project, I would be able to investigate this much more precisely.

Note that I am not Jean-Michel Jarre and my French is terrible :smiley: Let’s stick to English, since it is also the official language of this forum

French

Les pistes MIDI n’ont pas d’activité audio. Elles sont donc inévitablement incluses parmi les pistes considérées comme inactives et, par conséquent, parmi celles proposées pour la suppression.

Nous pouvons simplement désactiver ce contrôle pour les pistes MIDI, afin qu’elles ne soient pas prises en compte dans cette détection.

Concernant l’erreur d’exception de Cubase, il m’est difficile de tirer une conclusion sans disposer d’un projet de test permettant de reproduire le problème. Il peut éventuellement s’agir d’un conflit avec le moteur MIDI Remote de Cubase, mais la cause peut tout aussi bien être complètement différente.

Avec un projet de test reproductible, je pourrai examiner cela beaucoup plus précisément.

Notez que je ne suis pas Jean-Michel Jarre et que mon français est épouvantable :smiley: Restons en anglais, puisque c’est aussi la langue officielle de ce forum.

Oh, sorry! As I’m very bad at English, I accidentally sent you the translated version without realizing it. :sweat_smile:

I’ve just done some more testing.

It doesn’t crash anymore because I disable Scanning before pressing Log Tracks.

This is fantastic—it genuinely saves time. It’s actually a feature I’ve been wishing for for a long time, and you’ve finally provided a solution. I should have asked about it much earlier! :slightly_smiling_face:

To take it a little further, here are a couple of ideas—but of course, do whatever you think is best. :wink:

  • When Scan is activated, it would be really useful if an on-screen notification appeared, similar to the messages Cubase displays when a preference is enabled or disabled. That way, there would be no need to open the lower panel just to check whether Scanning is active.

  • For MIDI tracks, perhaps there could be a filter that marks them as [no activity] only if they contain no MIDI events. That would prevent MIDI tracks with actual notes from being considered inactive.

I’m off to bed now. Thank you so, so much for your help, and we’ll keep in touch. Good night!

I only skimmed through the thread, but…

Couldn’t you just use the PLE to filter so that it selects all audio/midi tracks that have the property set to “empty”, and then select them and then delete selected?

Or am I missing something?