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?


----------begin_max5_patcher----------
785.3ocwVs0bhBCE9Y8WASdVc3V.69j+O1YmcBP.RMlvlDT6zo829lKhEuTq
K1g8EI4vANmuKdHuNcBHiuGKAd+v6mdSl75zISrgLAlbX+DvFz9bJRZSCHy4
M32Ayb2h0tg2pnXk8l9GhRJroxydddRWlMHUdMgU8aANW4JXXzxE9y7hRSMW
Bh7Odw6We79IrtWenI1aSmZ9Y1c1tL7Ncaz0DJ7dasAkzVRwBzllJjB+t2Jg
dYaiWfaUAeGyrVWaIkjioXVkp1KNNv2aEurTUKvxZNsvyegNMNqe.n2pZRUc
itCJE3+34ectJ7RtB1koKM0KMXGQAjjJFhBl4A.GolaPngPykjXKuFF9Y7Yv
.3y+Q4O5FxOLbAzzdv+epuUce+LAaoVjKobtvadpVLKQR0A6QucVKRXnuNjj
x2cHAmCoKfMGWnybRg9wKO02D.OyYEbz2PILbNuko5aa9RyT32jYJHI1bABe
xZsfemloLaYwhtVcKAuaKQRxHTh5E6asCDkkR7gFatsglG0qSn7703hBApRl
K3TZe2Xtl2Wq4UdaUc+3XFJihqu7Ab2X6k2HqZCu.eRDtnP298hbptz09LzF
6yAJva3RsVVrPSDZzCtT3R+Jg6HpQhpCitAqJITrQRyDsxZrbAhTZ1tR6iaz
KBLq2xMpt+cH5wI1IGAVMe4Sell6O.MGoThVB3lzUO13i+FbAcba2ap0kDlZ
gP.z+KFE1Qo5tyU6d+WDLBvL3wfIDNPXxFUTFMLTFG+j8SECEktoyiA.ieL.
NT2Z2WaFCHBGFDSBReHMTOHaMpFiFEeZxigwgJiFLlgxW2Gh1G0dFfyNlts9
l3mha8WWD4cca2nkdC8KvREggTDNqWRPWNWkZu6B4OREBdG0I4anNg2Od5rK
GONviwjQikjEOVEBNVEJYjJz8XNhtt4H3gJb5cT3vK.naLBpoYKVHOjrsD5A
kOyss0xY1sDlaqc5DPfMGH2kejMBRnG6ozy7ZEtyztOw8UOf4zwBldNqCiZv
M0b7a8PXy4ekMHGNrypm91z+Brs5reC
-----------end_max5_patcher-----------

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~?

Hello

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; 
} 
else 
{
  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”.


----------begin_max5_patcher----------
1894.3ocuYszbaaCD9r7uBN7PGmN1Z..e2zjI8VuzY5odotiGJRHIzPAxRB4
GMS5u8t.ffDTlTg1lNdhkL2E.6qu8AX9xEqb2T9.sw04mb9SmUq9xEqVoHII
rp84UtGReHqHsQsL2B1cz0MYkUz+y8JMeVthS4l+9ZBxPje7.iWPEpcQ5IVd
TXnhaopIIdrhpUD2Mo7ctN+UK6pTQ1dFe2s0zLgdE9jn0nqbBQpu7P9xuvXu
0ntcUCmQ64gTbk7jr95EWH+3pmi8ll2TejYLsTgHM6y21TTp03qQqIgdgAdj
jXTXLwCObgB1A057ViwIx+EFhR78i8aWVywFQJiKWBZsGJBGmD3S7i8BCepG
FmLpGFiLdKZ5m0mTRhmenOJfPhiBhmHDX1VMsfl1P6zU75jn.TnePHBS7CPl
sy3LAKsPKArWDIvOAgSfHQbvTgS2qlwucAtsLt47QHvoFj3GEA5.NtcA4zrz
GG588CwAdwseFcFbCNZcXh0OwI.7IIQBPHIAfy25GLFPMA1XJsjMtnDz5POH
FkD34GQRhBIm3K6TQz5HTPL.LBHI9XH7+Bvgb58P32D6EzGDCgmilMhGGqfG
EK3YB.kbQC6e0v.v7mJr1v1IiTWY+WmMq0KVktFDpRW8HgJ2t2KLwj9u4oYi
Y033mQMHzzpKN.qJx3q9xOv74KRamH78iiZ.QKQQz1fx4BHFKDQTQh.kIRHu
lJkS1Y.G9coyPKFSlLG.oudw8Qs2hFCSatdeWLWbL46j4lUd3.kKdBBlDLl0
SFuvyyMELJQGM87LHS3SzBaAnwL.7hZ.dgpDMbxqx.NOhK96BfKHRWjPizHD
MlR9zjHNxKsl4Fk7o0FC6NF896XMrMrBl3wA1w1sMzVM7ZU2kqs6aWTl8YZd
dc5tlr5xhB6fVVAK6yh80kG2s2lNkmtoft+oaPy3tmxPKESmSC0M6NTlOjRY
cNXTVTRq20NCt6m1xJnxNoapO1rm1rNksU93mJJKqf+.K+66JkMa6sOKTv3s
NLBhmpGcAlj4PYS4Qd9ZviCtY2ku2htxjmJZPzH+3jks64NJ++b9DLtx9ZZy
9xhbniF771s8DfmA+PCDjggx36D6c78wcY7ELNMCbCB6TE6AndFUAHydNo4T
P2W+EQOpjcAcSRg1a05tjiMCfRZcCqjaoi.1ppxh7JqsH8w+co5fZGsVRhw0
j75HUSkIcCOVIlETcAn2Gq0PpGB8c6OF.yWygqLI2ilHDsaUIUbUBEapRyZw
iMUqgno4.b6cHvMqTkX70HHbfdDRTuGQlioy8rf5PbnhxY7J.H.E8SEsFPG6
b51ziEhaGLsKY8n721plixrKm5WpkWLxrjc0r7RtTIFDNjjMhSN.lx1BrMF0
J3oUirY.RB9kIXB2gTbrYSZspDYA0BSBoLkkECY0suB5VQK6JFmehWTTVMMy
Z1t8mYuaJAlGN2Yq3zb6Qtl6s.vPbaS5cC81hzhh1J.CO9GR4rCoBp4FYDTG
ymT9FOjycivIG.5Yz6Y4h8s2+rmCrbVkAD41EkyY6nMhgzDo5p48TZDOpc5V
jNZ5tcqfdnp.rhgK.RQXMBnH18MsKz.zrc.8u6F6Da6xoCnetxpCKsBUwbH1
brpJFZSerJiSOizjyIoaP3G2Uuy9hHqLcLZqi7BM2LnnjbkVpxXY2mXsAu.q
kbhHFo.yzuojtRAlc2Ug4m+MHuWUzL+itm2g5qF8JTMkVXh5KuDqYhzEhamL
w8WAnVY8iNxZHzKQu68N2vMzj9ri7rSICEVu6ORKNZneC+2SqSO3X0M9RrlU
KCq1xWFXwXX+4Kk8maOQ11KUZjyG9.LsyO7CNLtyGskfjVmh37ymxxn5p8+N
3H+xMbGGvqic9fC98NNNxm6WEb9hKGpNRMQHWUqhH21M7uB+RKZn5i7FAnnc
mxGkhx5L69yqa26P6BqsKrT6s7PuybzF4hZ2au4JUW0IBFDAdRsx263tfYLm
o.ADJvSTe.OYFCZxLF7YSKFfZmDsi0yZF+lT73aTqbJeg+xWqTar9IAynTY+
XExQcmnYgRFR9i6Ff6ITmYBEswWmgERf9iBFuaHq+rqxoybCBOWcPd13YnCg
uw5vb7C9SpCsD0TVl6Dss3HKec5gpszT4r4vEj1CioUAaaaM8ejyHgb9zV35
j0NWG.WOZKLyETFt5XkiW+S4k2yc7hAJMEk22xGttePOA0Rjjl6sovK6soly
8nvApWxrWDp+dTV9Z0ILL0P65OEJzF.dBDHZv8vGO7OHcws+x+iFymqfwyPv
wuEBlLGK1qGw+lZgjwsP7q1BweKKL7sPxyBMMbQGX4UkvXKs3WbBV8hf80+O
hBWS17zRqq3YorwuEto4KZ7hm1kLGTIVK5WujvyQRjkPRj4HIukPRdyQR9Kg
j7mijBVBIELGIEtDRJbNRJZIjTzbjT7RHo34HojkPRIyJy8014hfmacb7h26
ZVcMQuUCFLq5UeCIqmI6jWasTnm75pO4UU+zWS8zuh5Se8zpWM8XuVZ4D3W7
0K9e.3LCj1C
-----------end_max5_patcher-----------

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

2 Likes

I guess having a similar output with fluid.bufampslice~ is not so hackable, since the curve/computation is not exposed?

Would be handy to do a similar process to above, but on buffer-based samples.

Well, in the first instance, you could use the code above with the non-tilde gen and peek~ your way through.

I guess I meant more for the context of having a longer buffer analyzed and getting the separate ‘onsets’ and ‘offsets’ like you get out of fluid.bufampgate~.

That’s what I had in mind


----------begin_max5_patcher----------
2048.3ocyZs0aaaCE9YmeEDFCCo.oAhTWr7ZSQ1a6kALfArWVJBjsnsUqLkf
DUtzh1e6i7PIYYYJK5XYg8PiKOjh77ctyKe+pISWj7BMeJ52P+KZxjue0jI.
IIgIksmLcavKKiCxggMcYx1sTFe5Mp93zW3.8+NNZIEklDw34UcFEBckr3Ku
m3WQjUrMofGS4v7YURMMfubSDa8iYzkbE+3L29VqaPdV9xevtVxeHh+h97to
JhUMSXIseb0Ux+bigfYKMOOXM8.v7z2RR1h9ErFfzANvkTUj3ulRUfX5zZtU
GDcckfx0i.HECHjzEBIuADxnOK35C.32hQ1jYd9n7jLN585.pmdfR5Dn2XHX
sA847K.XeN3I5pjrs+TCbbzCGutfyp3j.tDSG4+DGky2G1KJVshlwB1RUKLK
OY0pZ+AcBEePN3Y6.hCWOnkSCoBXKJGri0beG4vzHsbGXok8+qkVyrHmpzZ4
l.lbxo7lFwmsLrC2qUwEQg2JALQfQz8gzbdDKfGkvPsPYCgtK4DisnjeG0oa
N3r44ARJr+ri6zgGUQ.59bdPFWpZP5h.4hGd4AVD2wU5uMGjDp.uih.IZWRy
lgkrOQLJml9U3Nk4RlKA6njQgmrlFtKUZyXI9mXljEAr0x.EvuFfUeHuxbmi
iU7EH645rjhTcPlLzIOKsacmC1s99iekBchU7PiUkZc7gpdOTaqA1CsRU5XO
xtn5wGY9EIBzEDdKJ37DlNn3sq1gLQUEbZ1iTVvhXZysZXZxj9h9LSAPHyJw
o5uW7fObTDJRC3wyOQ+PvbvT0IwG7FwD6QKg4ZpNcL9TylzpPTCxmnToyl0S
9jlyirpC.ek.bxzUQwzmnY4h5cZL5ISCRSaPdRiOQJU9RBLQ92TSJhoHYWSJ
i9TT02aUSMHSfFt.JEYppoewqZKOxoIITTncQTsJBzOkrDnIjUgmmFrT8wg4
o2tS9KWyJYjqZ63NtpcwMypdO5UhIg5dcbxxuRCaxgSSRorHVZFMmx3Pcf60
cHcUPQL+wUILddz2.1.StUa+qJYSscVucheOKJHtFAqyhBSXRlXO0gjb0xIB
M6pNpglfAFAKHUyGKrGExkN5TTaKuHeQPlTaUFBhT0IOIId+tp+tX5JdY2oQ
LVKoHOIs6NyhVu4He6hDQmaO1bC8j+XAS06iBCC9i4h8It+3BhiK8Y2e5eIf
EsMfS4QJU.wptSUX3M4KyRhi2CupddRSOgBC8kzmiB4afEpowfX3QoUFQSq0
xgQqE6yXeZ7f046SIm+pRn2fTwhRG4G4zsowBTr+.DtHh8xjuI447xAVYn0T
.r6rzZ5X2L.3dzOVfv14tq2hzAQDaRWaxsdNYptBGpNwMrJ01AkgcXDQqJ5P
7+xXLCunP.DDoCYAoaYgUOfUcfTtDHOmsa+vEOfvcoH9rbj5Qkc2nhznmNBc
1eQ30eccryO9mhHZP5fvOM0DqDG3jE77gTmNNdsDb5hZ2UFUUNIg.A3j+P30
kj8JRFNkds069.5AVEMoLqfsrMYQNlm9mf3hJ5Ov9KY4fHAOrQj9YSRb30XU
WkcrZ0tdbazgf4xkmqcLkslu4ZGGb0LFs5ZfiP2cGxB8q+JR3f9olqfjVMif
9X6tpXc36emXJ+9CLDRnlvn6P3OfPHY6cipEqHXBtb.k7f7Kdf8Cw+nw4T0r
8.Wvi0SvmjqRioq9+99xuceHgUPBKY7FBm2UM0UqqU42tCo2I+LIUAVHhVvH
+.Z53Eanq3jNu8XCfItK3S8lCMrqhh3HVW4IfEW1udwPdRQ1xJW3xHCBcUS9
nw4r0Xbh3hByzOajR3svCVFvCNWPd.aHOX2IOTRTQYX1.SJk90ehBK1t8Uca
jY1ItuaS2+hsOjwV82t2+h8vAzhuEIjpV5.YG2hjsQm8mA6NsbS3pqOwwYzN
qn0TQbMdFJOXaptyxGSNQfCIj66jwTnE6CAh7wi1Vw4nEnXTrNbheiJXSwpi
eCHOJfUrU.QnCdgHAm5BEPxaMRCzsF5C9rDxXEjw1il0rBm5tHv4u0Sao2iO
C6pNv9RWWuQS+VBVTd1Rzhrh7MTsWF2EC3NDW.3ybTpY7HembB7KBZshFHOp
nehtWkLEDG2KVY40wkq893HWF68YpqhScRRipEvt6gTFEWTHEQma9k1PvFOS
cpSyGdAPWOvE4ovo8xN7Fxm2hsKdmB9v5l6NbFLh8qSVg110EVh4CpGzuY8f
GuVvJjkEpNDWKsh4gbgczuv3yZg8LXg82U86adcrIFrP6yLaiBKeWXvHHVkO
4E3GGQ7+YtGzzkLSkV.RR.sFZUkM1.j3RFKYlK9RXVHucu98DvC.Hkk5uaun
8rR5sLlqdfXpq7EaAW.CzZnkJx6zseoBYzjJDSjJp3mJmGn0P6P3XahTYHBh
HuDudkJsXmVREr5IM435zvXAZctNqlvasLfZya91pyRVUgaoJS1ZH3s9zPUt
7mWbCSxmXez37hsrIiqCwws8rpZL3gwMgSw9GiSK4NGrm7mJaIYqy0NetA1R
j8GT6rkX0qEAC1Rk6MDZMzAEMhYAC+9YVP02r0Pq0wljWSdDaCSDbReqzP3y
gMoLAr2X4cStD0ifMonK7fj4Eaf47fn1LoXBLdnfDwPi9N7PcT64z0sQnNn0
4xayNgvvmuahgJ1NxOisUwmTBAO0wFiOegfQNvVGHCTa2s0KCRtFsdQPsdMP
G9Rf59U.09E.Au9Gcu7G44Ib0Ot5+.vC87WG
-----------end_max5_patcher-----------
1 Like

Ah super handy!

These abstractions should end up somewhere in the final distribution (snippets or whatever).