MIDI Filter based on dynamic list of MIDI CC# values?
Hi,
I have a patch that needs to filter MIDI CC based on a specific MID CC# value, rather than the CC# itself.
The patch is working well, but with multiple instances the way I'm filtering seems to spike CPU use in Ableton a bit more than I would like.
What is the best way to filter MIDI CC# values? Right now I'm using zl.filter as below.
Thanks,
Mike

that looks pretty benign to me. Is the stuff from the receives changing often? Is there anything else that might be causing the cpu spikes?
zl.rev -> route
Roman:
I original used zl.rev--> routepass (and also route) but had issues.
Routepass had an issue with an incoming MIDI CC message of "0 0". In testing, after receiving that "0 0" input, routepass would freeze for 4-5 seconds on the next input and then work properly. More significantly, route/routepass will route multiple places at the same time potentially yielding double MIDI outputs. For example, "match value 1" is 22 and "match value 2" is also 22. In such an instance routepass would pass through the value of 22 to midiout twice—once for "match value 1" and again for "match value 2".
I could not figure out how to thin a dynamic input list for route or routepass.
Is that possible?
Thanks,
Mike
Tyler,
I tested the patch independently from the larger device and by itself works fine. The patch increases CPU utilization by roughly 2%.
The issue is using multiple instances on a track. I typically use 13 instances on a track and CPU use is up around 30%. With multiple instances, device 1 extracts the MIDI CC#, matches it against the filer list, then outputs the filtered list to device #2....which extracts the MIDI CC#, matches it against a different filter list, then outputs to devices 3, etc. With 13 devices it does this 13 times for each MIDI CC received.
I thought my approach would be light enough to handle multiple instances, but from what I’m seeing perhaps not. I’m not very familiar with Max’s ability to generate efficient low-level code (in terms of CPU utilization), as I am a first time Max developer.
The receive objects don't change often at all, but the MIDI input the device is filtering does change quote often—especially when the MIDI CC is manipulated by a slow running LFO (at 1hz).
If I can’t figure out how to make this algorithm lighter on the CPU, my only other choice is to put the filtering feature in a different device—which would ensure the filtering is only done once per MIDI CC input (instead of 13 times with the multiple devices).
As I write this I’m thinking it was a poor design decision to put the filtering in each device. Maybe I should just put it in a dedicated device….
Any thoughts?
Thanks,
Mike
how do you know that it is the routepass object that freezes when it receives controller #0?
there are a few other suspects involved, from the host to the receiving device, and i would bet the max object is innocent.
I too would suspect that such a simple Max object that has been around for a while would be innocent. Certainly could have been the multiple devices interacting in some unknown way in some other part of the patch. I suspect additional testing would have yielded a different culprit.
I don't think any object is light enough to handle what I'm asking it to do. When using an LFO at 1 hz , 1000 MIDI CC values per second hit the filtering algorithm in each device. With 13 devices on the track, the filtering algorithm is being trigger 13,000 times per second. At that rate, CPU usage could be in play regardless of what object I use for the filtering.
To that end I feel it best to put the algorithm in a separate device. By doing so I can constrain the filtering boundaries such that [split] can do the filter (which I suspect would be even faster than routepass) and limit the algorithm triggering to 1,000 timer per second.
Would you agree with the approach?
I would avoid cascading midi 13 times in first place.
you say 1 hz lfo produces 1000 non repeating midi values per second
what waveform does so with 0 - 127 midi range ?
Many thanks SourceAudio.
You're right my math was off.
A sine wave whose depth is set at 100% goes from 0 to 127 to 0 every second which is 128*2 (256) non repeating values every second. So this is even stranger to me than I originally thought. 30% CPU use (per ableton) filtering 256 values 13 times on a track seems very high (based on my coding experience in other languages like C and VB). But then again this is my first Max patch and maybe this is just how it is.
I think this issue is not my device. In testing I hooked up a simple preexisting 8 encoder device (CC8) to an LFO running at 1hz. Running alone on the track, the CC8 causes Ableton to display 5% cpu utilization. Adding 13 instances of the default Ableton max MIDI effect, which just has a [midiin]-->[midiout] in it, causes Ableton displays CPU utilization as 10-15%. It seems that just the act of passing MIDI through a device takes a certain amount of processing power.
I agree 13 devices cascaded devices is probably not ideal to say the least. This is a potential use case because the device is designed (primarily) for use with the Push which has a 8 encoder display (without the use of buttons that can page through the display). I'm controlling roughly 100 parameters on an external device through MIDI CC which mandates roughly 13 devices on a track.Each device can be controller with MIDI CC# which mandates some type of MIDI filtering so the correct MIDI CC control values can goto the place in thedevice.
I wound up creating dedicated device to do the filtering which dropped CPU utilization from 30% to 20% (in the LFO use case I described).
That last 20% seems to the result of using 13 devices on the track which pass through MIDI (see my above post). I plan on bypassing those 13 devices by sending MIDI CC to a device at the end of the device chain using send->receive objects. That last device will dump the received MIDI CC to the track. I hope then is "problem solved".
We'll see....
Bypassing the [midiin]-->[midiout] of the 13 downstream devices by routing it to the last device in the chain using [send]-->[receive] reduced displayed CPU utilization in Ableton by more than 50%: down to 7-10% utilization from 20%-25%.
Looks like problem solved. Many thanks to all who assisted with this. Very much appreciated.
"Would you agree with the approach?"
no. while it is theoretically possible, i would never send data with 1ms framerate at high priority.
given how low the value resolution of midi is - and taking into acount that most VST plug-ins will interpolate between the values when transforming it into a signal - that little bit of extra noise which occurs when you use e.g. a [qmetro 20] instead of a [metro 1,] can be ignored.
i would even go so far and trigger a MIDI LFO only every 64th note.
the third option would be to match the clock speed of the LFO to the 7 bit resolution of MIDI, to make sure that you have control over the min and max values so that they are always contained in the stream.
3.)

you can drive that further and set a limit for the clock speed.
for example if clock speed would go below 20ms, you could fold it back to 40ms and leave out every second midi value (counter range, offset, *2)
Thanks Roman. All great ideas and good points.
The problem is coming from an upstream device with encoders that are MIDI mapped to an LFO: the automation update limit of the encoders are set to 1ms. My understanding is the transmit speed of MIDI is roughly 1,000 values per second, yielding a maximum time resolution of 1ms. I figured why not max out the resolution of the encoders—thinking any modern-era PC could handle a 1,000 values per second easily.
Obviously I was wrong.
I'll experiment with increasing the automation update limit on those encoders. My next project is to sample an LFO signal and output MIDI CC values. I planned on sampling the LFO signal at 1ms, but after your advice will experiment with a slower sample time.
Can you suggest a good starting point here? 20ms sample time as in your reply?
Maybe an adjustable sample time would be called for here.
sure, if an USB hardware is involved, it runs at up to 1ms because that is the clock speed of USB 2.x (and most modern encoder knobs are probably also fire out data at such a rate)
it is just that MIDI has such a low resolution anway, and that mainly depends on the target how fast you you want to send the data out again. leaving it to the user to adjust the rate seems appropiate.
(are you using a signal LFO, like most people do? i woud not do that. :) the counter object in my picture should represent the LFO(!)...)
you wrote:
"A sine wave whose depth is set at 100% goes from 0 to 127 to 0 every second which is 128*2 (256) non repeating values every second"
midi can not send minus range so you are left with 128 values to describe a waveform, not 256.
I don't understand why you need to update speed of LFO's that fast
if midi waveform needs much slower rate.
And again, as Roman says, a counter reading from whatever list
of values would be much more efficient.
But I guess it is much easier to use premade and animating
devices, like that overcomplicatd LFO, including fancy display of jumping curves etc, then to calculate 128 values for given waveform.
What are you controlling with that midi values at the end ?
I hope not signals again.
Roman, fascinating. My priority is efficiency and don't care much about displaying the signal so seems like the counter object would be the way to go, rather than sampling a LFO signal.
One attractive feature of the LFO object is the ability to generate different waveforms (sine, triangle, etc.). Getting different waveforms using this counter approach seems difficult; looks this approach would only output oscillating values between a minimum and maximum at a rate specified by metro.
SourceAudio, the destination of the MIDI CC values generated by the LFO device is an external synth—specifically in this case the Access Virus TI2 connected via a serial cable (not USB).
I should have been more accurate as follows:
"A LFO sine wave that is outputting MIDI CC to that eternal synth goes from 0 to 127 to 0 every second which is 128*2 (256) non repeating values every second".
I like the counter object approach rather than sampling a LFO due to effenency, but it seems much harder to generate different waveforms using that approach. Though I have a development background in C and VB, as a first time Max developer I doubt my ability to code different waveforms using the counter approach.
counter -> /127. -> /2pi -> cos -> /2 -> +0.5 = cosinus
counter -> split 64 128 -> [!- 128] -> *2 -> triangle
...
in case of serial midi, you need to take more care about speed, (31250 bits/s)
each byte needs 10 bits, one CC# message needs 3 bytes,
means 1041 CC# or any other 3 byte messages per second maximum.
But you need to send notes and other midi messages as well,
maybe even sync Virus using midi clock ....
and you can prefill coll with all waveforms that you need,
decide if your middle is 63 or 64 ....
[counter] to [function] works well for Midi LFO and you can manipulate the curve either manually or by knobs/sliders. With a combination of calculated, [coll] and [function] curves surrounded by a couple of simple math objects you get a switchable and pretty versatile LFO and/or Envelope.