MIDI signal active only after passing threshold

I’m looking for a way to have an incoming MIDI control signal take effect only after it’s passed a certain threshold. e.g. a physical fader on my MIDI controller is at 0.75, a slider in TD is at 0.5, I don’t want the slider to follow the fader until it’s passed 0.5.

I’ve done this in a few different ways, but none that are completely effective. If it helps to describe the problem more specifically, I’m using 8 faders on a MIDI controller to control 16 sliders in TD, by using a button to switch between banks. But if I leave bank 1 with fader 1 at 0.75, and fader 1 in bank 2 is at 0.5, as soon as I move the fader, the value jumps to 0.75, I want to avoid that.

I don’t think this is what you actually want…

base_update_contorl.tox (1.61 KB)

Here a given TD slider will only update if the incoming val is greater than or equal to the current Touch slider Position.

But, I don’t think that works around your mismatch between number of physical sliders, and number of midi sliders. You might think about how you can set a visual indicator for which slider bank is active / updating, and then use your switch button to help handle that routing:

base_update_contorl_v2.tox (2.65 KB)

That’s almost what I want. I’ve attached a .toe, this is the closest i’ve gotten. the buttons in ‘1X3_exclusive_router’ switch the banks (i have just one slider representing each bank for simplicity), every time you switch, the value of the slider you’re routing AWAY from is recorded in the table ‘last_vals’. the respective cell is referenced in the trigger chop’s threshold par. when triggered it increases the value of the count chop, and then there’s a simple if statement in chopexec3 that connects the select’s value to the slider IF count [0] > 0.

that’s great, except it only works in one direction, increasing (or decreasing, however you set the triggeron par). I’ve written probably 5 different variations of code to fix this, mainly by changing the triggeron par to 0 or 1, depending on if the incoming value is greater than or less than the last_val, but I’ve gotten nothing functional.
slider_updating.3.toe (6.54 KB)

Hey Corbin,

I’m not sure I totally follow the kind of behavior you’re trying to make happens - can you say a little more about what an ideal behavior looks like?

I think what I’m understanding is that you’d like the behavior in the example I posted, plus a non-change until a threshold is met… is that right? What I’m missing here is how you want to control for ascending or descending vals. Seems like you could easily end up in a situation where your midi control seems un-responsive, but that’s only due to the fact that you haven’t reached a threshold yet.

Another alternative would be to think in terms of relative changes - so inputs from a midi controller add to or subtract from an existing val depending on the slope of the change. Is that more what you’re after?

huh, so about 5 minutes after I posted that, I think I figured it out. I included an updated .toe file if anyone’s interested in how to do this. I still have to test it with an actual MIDI controller and multiple sources, but the trick seems to be to use the incoming signal BEFORE it is routed to control the triggeron par.

if anyone comes up with a different way to do this, i’d love to hear it!

whoops, here’s the file.
slider_updating.4.toe (6.73 KB)

So I’ve got this almost working, but I’m running into some quantum weirdness where the code behaves differently depending on what i’m looking at.

in the attached file, in (‘/project1/control’) ‘container1’ has all the source sliders, representing what would be physical sliders. the ‘1X4_exclusive_router’ routes this signal to one of its outputs, thereby changing the bank, of which I have just two. so if you change to bank2, move the sliders in container1 to different positions, then switch back to bank1 and start moving them again, you’ll see the sliders in the ‘faders1’ container jump, that is what i want to avoid.

so here’s the funny thing, split the view and in one panel go into (‘/project1/control/faders1/item1’) and repeat that process. you’ll notice that the target slider only starts moving again when slider1 in ‘container1’ hits the target slider’s previous value (the value before you switch banks), that’s exactly the behavior i want, but it only works if I’m looking at it.

i’ve narrowed this weirdness down to the count chop. this might be a bit tedious to follow, but every time i switch banks, the slider values are recorded in the table ‘last_vals’. a trigger chop references the appropriate cell for its threshold, when the threshold is passed, the trigger goes off, the count increases from zero, and a little python in a chopexecute DAT says “if count [0] == 0, do nothing, if count [0] > 0, the slider’s value equals the incoming value”. the button that i use to switch banks also sets that count chop value to 0… if i’m looking at. if i’m looking away, it does not reset the count chop to 0…

and, to make it even weirder. if i switch from bank1 to bank 2, print a count chop’s value in bank1 i get ‘None’. that’s normal. then i switch back and print that count chop’s value again, i get ‘0.0’. that’s normal. BUT if i look at the count chop BEFORE I print its value, it’s whatever value it was before i switched banks… whaaaaaa

is TD playing a joke on me?
quantum TD.8.toe (36.3 KB)

Hey Corbin,

I’m having a hard time replicating your issue - and I’m seeing entirely different issues. Namely that some sliders refuse to update after switching banks and the last vals table refuses to update.

Some things to keep in mind here that might help:

Looking through your python, you’d be well served to think about how to use some for loops to tackle some of your challenges here. It’ll save you a lot of time and helps catch errors.

Clones are tricky and you end up with strange behavior that can be difficult to predict sometimes, and if I had to guess I’d bet this was related to cloning rather than the count CHOP itself. You can use the clone immune flag to ensure that ops are unique between clones, and that might be something to chase here a little.

More generally - as a person who has done tackled my fair share of these kinds of challenges, I think you’ve got a solution that’s a little more complicated than it might need to be. I did another pass on a the update_control example I posted yesterday:

base_update_contorl_v3.tox (3.26 KB)

Here when we change banks we set some storage keys for the sliders - preventing them from being updated unless the incoming slider val is greater than or equal to the last known val for the slider.

Take a look at the behavior here and see if it’s close to what you’re after.

i think i forgot to add +0.01 to the end of the python reference in the trigger, for some reason that helps it catch on; although i’m not sure why the last_vals wouldn’t update.

i did end up getting it to work properly by changing the value of the resetpulse par in the count chop when i switch banks, albeit still a bit clunky at times. I’m new to Python, so yeah, there’s probably an easier way to do just about everything i’m doing. i haven’t looked at for loops in years, since a brush with arduino; would that allow me to do something like write one if/elif statement and have it repeat for each slider instead of writing 8 of them with the same end goal?

your example is closer to what i’m looking for, except that it doesn’t work in both directions.

i think i’m going to try to pull apart your code here and learn something from it, a quick question though: in the expression op(‘container_touch1’).findChildren(type=sliderComp, depth=1), what exactly does depth refer to?

I think some for loops would help in all of the places where you’re doing things that are in sequence.

For example, lets say you want to print the names and vals for all of the channels in null 1 in your network. You can do that in two lines with a for loop like this:

[code]# print the channel vals in null1

for item in range(op(‘null1’).numChans):
print( op(‘null1’)[item].name ,op(‘null1’)[item][0] )[/code]

Instead of:

print(op('null1')[0][0]) print(op('null1')[1][0]) print(op('null1')[2][0]) print(op('null1')[3][0]) print(op('null1')[4][0]) print(op('null1')[5][0]) print(op('null1')[6][0]) print(op('null1')[7][0])

We might also check the > < == status of comparing channels and sliders. We could do that like this:

[code]# check if vals are greater than or less than faders1

faderPath = ‘faders1/item{}/slider1’
nullCHOP = op(‘null1’)

for item in range( nullCHOP.numChans ):
msg = ‘slider {slider} is {logic} than channel {channel}’
targetSlider = faderPath.format(item+1)

if float( nullCHOP[item][0] ) > op(targetSlider).panel.u:
	print( msg.format(slider=op(targetSlider).parent(), channel=item, logic='>') )

elif float( nullCHOP[item][0] ) < op(targetSlider).panel.u:
	print( msg.format(slider=op(targetSlider).parent(), channel=item, logic='<') )

else:
	print( msg.format(slider=op(targetSlider).parent(), channel=item, logic='=') )[/code]

There’s some good bits here about Python in touch that might be useful:
matthewragan.com/teaching-resou … hdesigner/

findChildren() is a method that lets you find ops inside of a Component OP. In this case specifying a depth of 1 limits of the search distance of the call to only one network depth - otherwise it would recursively go through all layers of the children ops.

In your network try this:

[code]children = op( ‘faders1’ ).findChildren(depth=1)

for item in children:
print(item)[/code]

Try changing the depth parameter in call to see which ops show up in the textport.

When you say:

What do you mean exactly? I would interpret this to mean that you’re really after making relative changes to your sliders in Touch, but I don’t think I’m understanding exactly what you’re after.

i’ve looked at a number of those tutorials, looks like it’s about time to take a deep breath and dive back in.

when i say it only works in one direction, i mean that it only works when ascending, i.e. if the midi slider is below the saved value of the touch slider. but if the midi slider is above the saved value of the touch slider, and is descending, then the touch slider jumps up to meet it. does that make sense?

so right now there’s this piece of code:

if panelValue >= op(targetSlider).fetch('lockVal'):

but sometimes i need it to behave like this:

if panelValue <= op(targetSlider).fetch('lockVal'):

i’m trying to figure it out on my own, but the TD/Python rabbit hole goes wayyyy deep.

hip.

That’s super helpful.

One more time… check out this one:
base_update_contorl_v4.tox (3.41 KB)

Here there are few more features that we add. When changing decks we also store a value called direction. We can use this to do another logical operation to pick if we want >= or <= for the update behavior.

I fought with Python a lot when I started, and as frustrating as it was - I can also say it was totally worth it. Writing those tuts helped me solidify some of the most important things we do with Python in touch and if you feel like some thing are missing or could be explained better I’d love to know what I can do to improve them.

Hang in there with the Python - it gets easier the more you practice and use it, even though it feels like it takes forever.

i think i see what you’re doing here (and i just found a new DAT to play with), but the examine DAT only seems to be updating the direction when i move the slider directly, if i switch banks after playing with a midi slider, it just goes to descending. it also doesn’t seem to account for a situation where the slider would be descending, but when switching back it needs to be ascending. I took a shot at changing some of the code around focusing on the lockVal, to no avail.

BUT i think i have a lot here to pick apart and explore. i won’t complain if you come up with another version, but for now i’m going to try to just sink my teeth into what you’ve given me so far; no doubt it will lead somewhere.

thanks a lot! :smiley:

AHHHHHH!

Okay one more time around the sun here.
base_update_contorl_v5.tox (3.56 KB)

I realized after reading your comments that it’s really about setting the direction flag for the opposite deck - comparing the slider that was just changed to the slider that just did the changing is all wrong. Instead you have to compare the spoof midi val against the non-targeted sliders as that will tell you if you’re watching for ascending or descending vals. Woof.

This also doesn’t protect against some edge cases - but I think it gets closer.

Hope this helps you get to the ideal midi-updater solution.

This is spot on. definitely a lot more coding than i had anticipated, but much more sophisticated than my rube goldberg machine of CHOPs. Okay, i’m going to study this and try to recreate it. Thanks so much, again!

Glad we got there!

Logic flows are always a little more scripting than you expect they might be - I feel like I regularly discover the little things I didn’t think about until I’m in the thick of it.

Anyhoo - glad this gets you a little closer. :slight_smile: