Find the last onset within a sample


a question about onset: In my naive mind I thought it should be possible to find the last detectable onset of a given sample with:

sample :loop_amen, rate: -1, onset: 0

This does not seem to be true. The sample is of course played backwards but index 0 still gives the first onset of the sample played from the beginning in non-reversed manner. This is logical but not what I expected.

Is there an easy way to find the last onset?


couple of functions I defined in this post should help
IistOnsets and numOnsets


Thanks Robin, that is helpful!


As with most programming languages, Sonic Pi’s arrays (which are really just Ruby’s arrays) are 0 indexed. Therefore asking for something at index 0 is asking for the first item. To ask for the last item, you should ask for item -1 (this is thanks to the fact that Sonic Pi’s onset opt acts like a ring, so going back one index (from 0 to -1) is the same as jumping to the last item.

Hope that this helps!

sample :loop_amen, rate: -1, onset: -1

Cool That’s much easier!

1 Like

Without wanting to pretend to be as smart as this solution I somehow suspected that there must be an easy way like this :wink: Thanks!

Just to give some background: At Saturday I had the oportunity to present Sonic Pi at an IT fair for kids. I did mess around with Sonic Pi almost the whole day (which was fun and a very good exercise); during one of these sessions I recorded the ‘chord inversion’ examples, because one question was about recording your Sonic Pi tune. Then I loaded this sample to further mess around with it.

The next day I was thinking about an easy way to trim the sample for further manipulation in a quick way and without any external application; this brought me to onset

Neat! Discovered this one on my own as well.

Now… Is there a way to return the index number of that onset? It does show up when we play it.

sample :loop_tabla, onset: -1

 "loop_tabla.flac", {onset: 60, start: 0.9831, finish: 1.0}

I could use the defined functions @robin.newman shared, of course. Just trying to make it easier to explain and bring to different scripts…

@enkerli, hi, glad to see you are still around … :wink:

1 Like

hi, glad to see you are still around … :wink:

Thanks for welcoming me back! Been doing many other musicking projects, mostly on iPad and in Bitwig Studio 3. Hadn’t touched SPi in quite a while when @samaaron sent that update about 3.2b8 for macOS.
Noticed some of the changes before reading the release notes. Rather neat!
The onset stuff is pretty motivating. It’s one of those neat little SPi tricks which are easy to use, learn, and teach. And they provide real value to musickers of all stripes!
The fact that onsets now behave more like samples is what needed to happen, IMHO. Now that we can slice them up, it’s easier to imagine the type of sampling fun people like to do with sample-focused DAW plugins.

At any rate, going back to topic…

Has anyone found an elegant way to return that number of onsets value?
I feel like it’d be pretty useful to have it directly in the ‘notation” so you could randomly pick from all onsets.
If I already know the value, it can be fairly elegant/terse…

loop do
  smpdur=sample_duration(smp, onset: onsnum)
  sample smp, onset: onsnum
  puts onsnum, smpdur
  sleep smpdur


Sounds groovy, a 10yo learner could understand the code fairly easily so she might add it to her own scripts, and it’s fully transparent (unlike defs).

Maybe I should just use @robin.newman’s defs for now. It’s just that I get this nagging feeling that there’s a better way.

To randomly pick from all onsets simply use pick :slight_smile:

sample foo, onset: pick
1 Like

Now, that’s elegant!
Thanks a lot. I’ll update.

Maybe I don’t need to know the number, after all.
Still, my curiosity (hopefully cat-safe) keeps me wondering: isn’t there an obvious Ruby thing to do to reference the index value of the last item in a ring?

Any way to get the onset number from this, so I can get the proper sample_duration? Would make a huge difference!

Ok, getting somewhere…

# Simple Sonic Pi script which randomly picks part of a looping sample to create a groovy rhythm.
# Thanks to Sam Aaron for that “pick” trick!
# I still need to get the duration of that onset so I’m not yet able to fully use that trick.
# In the meantime, I did find a relatively simple way to get the number of onsets in a sample…


l = lambda {|c|;maxons=c.length-1 ; c[0]}
sample smp, onset: l, finish: 0

loop do
  sample smp, onset: ons
  sleep sample_duration(smp, onset: ons)

Still wish I could use the pick trick. Sooooo elegant! It’s just that I really need to know the duration of that onset. Setting them all to the same value really defeats the purpose, making it “quantized”. The groovy thing about this random onset script is that these sections of the sample have different durations.
At first, I thought that, maybe, pick worked like tick and one could look to use the same value. Now, that would work so so well!

Of course, since these are rings, this also works:

loop do
  sample smp, onset: ons
  sleep sample_duration(smp, onset: ons)

It’s extremely unlikely that anyone would ever use a sample with more than 1001 onsets. If they did, it’d probably cause performance issues. I just find this solution a bit… kludgy. Maybe I’m thinking too much about teaching (which I’m not doing with SPi, at this point in time). It feels like something we’d tell learners not to do. Better to find the actual value than think up an arbitrary number.

Ok, next step is to try this with imported loops. Got subscriptions to a few sample libraries so it’s easy to find these.

Hi @enkerli,

this might be what you are looking for:

# get a ring with all onsets
ring_of_onsets = sample_buffer(:loop_amen_full, 0).onset_slices
puts ring_of_onsets

# get the index of the last item of this ring
index_of_last_onset = sample_buffer(:loop_amen_full, 0).onset_slices.last[:index]
puts index_of_last_onset

this might be what you are looking for

Sure is!
Updated my simple code. And applied it to loops from sample libraries. It was interesting to find which loops worked best. There isn’t much that’s intuitive about it. The type of sound does matter. So does the relationships in duration between onsets. In a way, that recording is in decreasing order of preference.

maxons=sample_buffer(smp, 0).onset_slices.last[:index]
loop do
  sample smp, onset: ons
  sleep sample_duration(smp, onset: ons)
1 Like

Great! Glad that helped… but the actual credit for this little trick has to go to Pit, who - quite a while ago - showed it to me. Nice sounds, I mostly like the voice and guitar examples from 3’14 on!

1 Like

Nice. With some code snippets

I mostly like the voice and guitar examples from 3’14 on!

Ha! Should have known that putting them in decreasing order of preference could have the reverse effect. :wink:

I’ll experiment more with melodic and voice stuff. Come to think of it, conversational speech might work fairly well. Will search for such samples.

Something I’ve been thinking about as well is that onsets can help create a “poor person’s groove extractor”. My favourite DAW doesn’t support groove quantization. It might still be fun to trigger things in a groove, sending a MIDI event at each onset (without even needing to play the sample itself).

1 Like

Not quite sure, what you aiming at. Is it about to create ‘humanized variations’? Do you care to elaborate?

I did some experiments with randomized slices (could also be onsets) to create fill-ins, which aim to sound as natural as possible; of course you can use pick on the collection of all slices a sample has been sliced into. Other times a ring with a choice of selected slices and .choose will do a better job. Examples are with the Amen break to not make it work with stock material from Sonic Pi, but its worthwhile to use all sorts of drums loops.

What I meant by “groove extractor” was more about synching another instrument with the onsets of a groovy loop.

And experimenting with random onsets has been an ear-opening experience. As I said, it wasn’t really possible to guess which loops would work best. Applied the same idea to speech samples and it was rather effective in producing unexpected beats. The onsets don’t necessarily correspond to syllables and the speech is impossible to recognize as a specific language. Because there’s a lot of silence in speech, I ended up layering samples from different languages, playing a bit with pitch. Sounded a bit like a “nonsense conversation”.

Haven’t done much in SP but Sam’s notifications of beta releases make me think of some microprojects I might end up doing, with this unusual situation we’re in.