Combining fluid.ampslice~ / fluid.ampgate~

So the original (profoundly confusing(!)) fluid.ampslice~ was broken up into two objects in what is now known as fluid.ampgate~-gate.

I had some initial confusion with how fluid.ampgate~ was supposed to work, and after some clarification from @weefuzzy, got something working for the context I needed it in.

Certain things about it are still confusing about it to me, and for the purposes of my patch I’m combining half of the original Max-version of fluid.ampslice~ with the tail end of fluid.ampgate~. Doing something like this:

A bit strange that I have to go “half and half” with it, but not the end of the world.

Today I started thinking about the cool lookahead and lookback features of the original fluid.ampslice~ had, and how those can be used with the separate objects now.

Now, it’s (very(!)) possible that I didn’t understand how that was meant to work in the original version, but my understanding is that, for a bit of extra latency, you could be more certain of where the transient/attack happened. Is that about right?

If so, how does one re-combine the two objets back together in order to do that?


I can picture doing it with the “half and half” version I posted above, where you feed the differential into fluid.ampgate~, but then it’s going through another envelope follower and thresholding stage, which has to have some kind of impact on the signal.


So in short, the two things I don’t understand how to do after the creation of fluid.ampgate~-gate are:

  • How to have a gate output from an incoming signal based on the differential algorithm from fluid.ampslice~ without having to “half and half” things.
  • How can you leverage the single-sample-spike output of fluid.ampslice~ but with the lookahead and lookback features of fluid.ampgate~?


I don’t understand what you try to do here, so I’ll explain from the simpler questions, and then you will help me understand what you’re missing. Same old: it works :wink:

This is hard to reply to because it depends on what you want to get out of this. If only gating, then use @floor in ampslice and you have it already.

One approach would be to use bufampgate triggered on the past X samples of your input when ampslice detects something. That way you can look in the past and pick the local minimum.

But I might just read you wrong.

No. For a bit of extra latency, you get that latencie’s amount more to find the local minimum of the envelop. In other words, with @lookback 500 you get to look, once the gate gets ON, to the 500 samples before it gets ON to see if there is a lower value in your envelop (there should be) and you change the ON status at that position. Does it make sense? In practice, it does help to find the lowest point, where one would start its edit point in a DAW.

That only gives a click. I would like a signal that goes high when the threshold is passed, and goes low, when the lower threshold is passed. So a gate vs a click.

I mean for real-time here. So I want to be able to use lookahead and lookback but in a real-time context.

At the moment, it seems like you have to choose between having a volume differential with thresholding and a click output OR a simple thresh with gate output, with the possibility for lookahead/lookback. I can’t see how to have the time stuff with differential thresholding, etc… if that makes sense.

it does and doesn’t… I’d have to draw it. But better is the idea I gave you: JIT do the bufampgate lookback and you’re in business. I’d be very surprised if it wasn’t at the beginning of that latency buffer, but again you have the advantage of being able to change the env parameters of that search…

If I’m aiming for an increase in accuracy of a small amount of samples (say 128samples), leaving signal-rate, banging some fluid.buf~ objects, and then back, would negate any benefits from the increased accuracy.

I guess the two use cases, to explain it more along those lines, are:

  1. Literally have a gate output version of fluid.ampslice~ where you get a 1 when it crosses the thresh, and a 0 when it returns. Fast and real-time, like a variable duration click.
  2. When I’m interested in having more precise onset (relative to the incoming signal) and don’t mind some extra samples in latency. Again, fast and real-time.

The features of the split up objects don’t seem to allow either of these use cases.

I don’t think the joined up object allowed for these use cases either, but it was harder to tell. This is another situation where it would be powerful if objects that make curves are split up from objects that segment curves I guess.

(1) is trivial to build in Max, except for the signal rate debouncing, which I think would need gen~ if you wanted a gate-like output. (2) is harder, because it sounds like you want all the complex state management of ampgate~, but working on the difference of two envelopes.

I didn’t really play with the original one enough to understand what it actually did, but did the original allow those use cases? I understand the topology of what is now fluid.ampslice~ but never understood where what is now fluid.ampgate~ fit into the equation.

The first use case would be covered with something like an output flag in fluid.ampslice~ where you can set @output trigger or @output gate or something like that. Presumably, like the Max-version of the algorithm, the threshold exceeds the value and then drops below it in a way that is reportable, the object just does the edge~ thing and says it has passed on does nothing after that.

thresh~ would do it in Max-land, but then needs the debounce stuff, so it would be basically rewriting the object, in Max, to get the access to the full state of the object.

For the second one, I can see myself using something like that sometimes, though not very often. It was mainly me spitballing because that’s what I thought the purpose of the object was meant to be (the onset stuff but with crazy time management things).

Other than functioning something like a noise gate with funky time stuff, I don’t really understand the point of fluid.ampgate~. I mean, a noise gate is cool and all, but the time stuff seems wasted on that as a function, particularly in the overall scheme of the fluid.verse~.

With the introduction of fluid.ampfeature~ I’m reminded by how useful it would be to have the “offset” of the internal thresholding be the output of fluid.ampslice~ instead of just the onset.

Most things in MSP-land will be just as happy with a gate as they are a trigger, and edge~ doesn’t care one way or another, and it would add richer output as the variable gate length, in-and-of-itself, could make for a useful pseudo-descriptor or for plugging right into an adsr or something.

But now that you have [fluid.ampfeature~] you can just stick [thresh~] afterwards and have that…

Would have to manage my own lockout though, which isn’t the end of the world.

Just curious if it’s a technical funky thing to implement, a “not enough hours in the day” kind of thing, or an interface/differentiation thing. Just seems like nothing is lost by having it output the off threshold (which is also used for this sort of thing) instead of just the on.

Well, it’s definitely not enough hours in the day thing besides anything else. I also suspect that without adding a Schmitt trigger a la [thresh] (which ampslice doesn’t currently have, IIRC), the offset output would be really (really) noisy. Part of the motivation for breaking out the <x>feature objects was that it leaves advanced users like you much more room to experiment, without needing to keep adding more to the original objects.

Does it not use a Schmitt? I thought that’s what the separate on/off threshes were doing either way. Which is why I was pushing on this back then (and again now) as I thought it was just a matter of changing what variable gets output, rather than adding anything else in since presumably the offthresh is computed to get passed onto the thresholding regardless.

My mistake, it does have a Schmitt trigger.

Here is gen code to do what ampslice~ does, plus output the state

History state(0); 
History debounce(0); 
History prevValue(0); 

Param onThresh(6); 
Param offThresh(-3); 
Param minSlice(4410); 

if(state == 0 && in1 > onThresh && prevValue < onThresh && debounce == 0) 
  out1 = 1;   
  debounce = minSlice; 	
  state = 1; 
  if(debounce > 0) debounce = debounce - 1; 
if(state == 1 && in1 < offThresh && debounce == 0) 
  state = 0; 
prevValue = in1; 
out2 = state; 
1 Like

Suuuper useful!

Here is an updated version (using the same variable names as fluid.ampslice~ for the sake of my sanity) as well as a “musical example”.


I can see myself using this in just about all of my use cases…