Chaining onset detectors (novelty -> ampslice)

I’ve revisiting this ancient idea here (it’s been on my list for a while), particularly in the context of manually hunting for a zero-crossing post-amplitude-based onset detection (thread) with the idea to build a 3 layer offline onset detector.

  1. fluid.bufonsetslice~ (it seems like @metric 9 is most generic/useful here?)
  2. fluid.bufampslice~ based on the returned results of step 1 (if no onset is detected default to results from step 1)
  3. a faux @lookback crawling through n amount of samples before the onsets from step 2 to find a zero crossing

With that in mind, a few questions.

///////////////////////////////////////////////////////

I guess the idea of using something other than fluid.bufampslice~ would be to catch onsets that aren’t amplitude based, so fluid.bufonsetslice~ seems like the idea choice. Is @metric 9 (RComplexDev) a good all-around choice?

It’s defined in the reference in a kind of reverse-chronological cascade of info:

6 = ‘6’
PhaseDev takes the past 2 frames, projects to the current, as anticipated if it was a steady state, then compute the sum of the differences, on which it thresholds (like Onsets phase)

8 = ‘8’
ComplexDev same as PhaseDev, but in the complex domain - the anticipated amp is considered steady, and the phase is projected, then a complex subtraction is done with the actual present frame. The sum of magnitudes is used to threshold (like Onsets complex)

9 = ‘9’
RComplexDev same as above, but rectified (like Onsets rcomplex)

I had a poke/test in the “parameter combinations” tab in the fluid.bufonsetslice~ helpfile and @metric 9 seemed to give the results that were most useful across the listed examples. That being said I don’t really understand the (technical) distinction between metrics 6/8/9.

///////////////////////////////////////////////////////

The second question is about the cascading of time. So if I understand the way onsets are reported back from fluid.bufonsetslice~ given the temporal wiggle room of fft-based onset detection, the returned slices are quantized to the start of an fft window and the “real” onset occured at any point within that fft window.

So if I have a slice at sample 1000 and I have @fftsettings 1024 -1 -1 then the “real” onset would be anywhere between samples 1000 and 2024.

Is that correct? Or more specifically, does the hop not matter here? Like would it instead be anywhere inside the [window - hop] such that the “real” onset would be anywhere between samples 1000 and 1512 instead?

The reason this bit matters as it would define the @numframes that fluid.bufampslice~ will be checking for.

///////////////////////////////////////////////////////

The third question is more a thinking-out-loud type thing. Once I have a returned slice that has either gone from fluid.bufonsetslice~fluid.bufampslice~ or just from fluid.bufonsetslice~ (if fluid.bufampslice~ fails to return a value), and I then want to look for a zero crossing ala @lookback in fluid.bufampgate~ (as discussed in this thread), would I search for a zero crossing before and after the detected onset?

Or rather, I’m currently thinking something like this, where the process would fork at the last step.

If [fluid.bufonsetslice~fluid.bufampslice~] returns a value, then search for a zero-crossing only before the detected onset as fluid.bufampslice~ is quite temporally accurate and I just want to add a kind of @lookback functionality to the detected onset position. In this case it would be a fairly small amount of samples (~16 samples).

If [fluid.bufonsetslice~fluid.bufampslice] then search for a zero-crossing only after the detected onset from fluid.bufonsetslice~ since it will, by definition, happen after that point. In this case it would also be a fairly large amount of samples (~256 samples) and then picking the latest position if multiple zero crossings are detected since the temporal slop here would be intrinsically higher.