How to change different instruments' sleep rhythms live?

Sure. That’s where set_sched_ahead_time! comes in :slightly_smiling_face:
(Of course, the larger the value, the more latency there is between hitting Run and hearing the result).

1 Like

Thanks for helping with feature discovery. I hardly know the language beyond what the tutorial series covers.

The original problem continues: because my typing of the M-r is virtually always asynchronous, the jumbled sync problem is just offset by the parameter sent to set_sched_ahead_time!. The issue is that the parameter specifies absolute time, but sync is based on number of ticks.

My rhythm vectors—even if their values sum to the same number of beats—have different numbers of components (differing vector lengths), e.g. 2 eighth notes = 1 quarter note. Am I right that it would be unwieldy to track the current active location in a rhythm vector, particularly when there are several vectors active and changing at the same time?

The behavior I want is for the M-r to effect a reset of all ticks’ values to zero. It doesn’t matter to me to be precise as to when this change happens, everything staying in sync after the M-r being the primary musical thing.

A FEW MINUTES LATER :grinning::
After that last paragraph, I thought of scouring the reference for tick-related functions, and found tick_reset_all. A quick test in my toy example above shows that this small change will make M-r’s behave exactly the way I need:

in_thread do
  sleep 1
  cue :metronome
  tick_reset_all  # added
end

Thanks for encouraging self-initiated feature discovery. :+1: If you have any suggestions related to handling ticks in my context, please post.

Hi I’ve only got time this morning to scan read all this - I may not understand want you want (caveat, caveat…) but I’d point you at looking at doing the playing bit from within another thread, inside the live_loop I mean.

That allows you to kick off a sequence, however long, and it just runs in parallel with everything else and stops. Something I use a lot - you don’t have to do the mental maths to make sure your loops has the right combo of sleep lengths in.

Take a look and see if that does it for you :smile:

This kind of thing, that live loop :a is going to go round every 4 beats come what may…

live_loop :a, sync: :foo do
  in_thread do
     # play something in here, as long/short as you like!
  end
  sleep 4
end

I want to implement changing rhythms, which means changing sleep times. I don’t see where your sleep time changes or allows for changes that stay in sync. Perhaps fill in your sketch?

For example, my bdRhythm’s rhythm is sometimes all 1/4 notes and sometimes it’s 1/4 plus two 1/8 notes. I want to shift between the two patterns by live coding the difference, then reloading so that my kick loop’s associated sleep value is sometimes [1] and sometimes [1, 0.5, 0.5]. Where are you implementing the sleep pattern changes?

OK I did understand correctly. You see the bit where it says: play something in here, as long/short as you like? In there :smile: Whatever you put there and edit will get kicked off every four beats. Then you can have other loops sync’d off that live_loop :a, same format and they’ll all stay in sync.

I must say, I don’t exactly follow you. I do this:

##| ToneRhythm = [1, 1]
ToneRhythm = [1, 0.333, 0.333, 0.333]
ToneRhythmLength = ToneRhythm.length

live_loop :a, sync: :metronome do
  in_thread do
    play :e2, attack: 0, release: 0.5, cutoff: 100
    sleep ToneRhythm[tick(:tone_tick) % ToneRhythmLength]
  end
  sleep 4
end

in_thread do
  sleep 1
  cue :metronome
end

The tempo is rather slow, but I guess I could adjust for that by setting bpm. Then I uncomment the first ToneRhythm, comment out the second ToneRhythm, do M-r, and there’s no change in the rhythm. It also seems weird to me that there are nested sleeps.

That’s probably because you are currently playing one note from the melody per cycle of the loop, instead of a complete bar’s worth, and then waiting for 4 beats.

That’s just a quirk of nesting two threads - the live_loop on the outside and the in_thread on the inside.
The inner thread is playing your melody, but since that is a separate thread, as far as the live_loop’s thread is concerned, it has nothing causing it to wait/sync unless we add a further sleep/sync adjacent to the in_thread block, still inside the live_loop.

1 Like

This is the kind of thing, I’ve put in some percussion so you can hear what’s going on

##| ToneRhythm = [1, 1]
use_bpm 100
ToneRhythm = [1, 0.333, 0.333, 0.333, 1]
ToneRhythmLength = ToneRhythm.length

live_loop :a, sync: :metronome do
  sample :perc_snap, amp: 0.1
  in_thread do
    ToneRhythmLength.times do
      play :e2, attack: 0, release: 0.5, cutoff: 100
      sleep ToneRhythm[tick(:tone_tick) % ToneRhythmLength]
    end
  end
  sleep 4
end

live_loop :metronome do
  sample :drum_cymbal_pedal, amp: 0.1
  sleep 1
end
2 Likes

Simplyfing a bit, that tick is in its own thread so just needs…

Now you can live code the ToneRhythm to your heart’s content :smile:

##| ToneRhythm = [1, 1]
use_bpm 100
ToneRhythm = [1,0.75,0.5,1]
ToneRhythmLength = ToneRhythm.length

live_loop :a, sync: :metronome do
  sample :perc_snap, amp: 0.1
  in_thread do
    ToneRhythmLength.times do
      play :e2, attack: 0, release: 0.5, cutoff: 100
      sleep ToneRhythm.tick
    end
  end
  sleep 4
end

live_loop :metronome do
  sample :drum_cymbal_pedal, amp: 0.1
  sleep 1
end
1 Like

Sorry to go on and on but my meeting finished early and successfully so I’m in a good mood. You can put the other rhythm parts in the same loop, just a different thread…

##| ToneRhythm = [1, 1]
use_bpm 100
ToneRhythm = [0.5,1,0.5,0.5,0.25,0.25]
ToneRhythmLength = ToneRhythm.length

ToneRhythm2 = [1,1,0.5,0.5,0.75,0.5]
ToneRhythmLength2 = ToneRhythm2.length

live_loop :a, sync: :metronome do
  sample :perc_snap, amp: 0.1
  
  in_thread do
    ToneRhythmLength.times do
      play :e2, attack: 0, release: 0.5, cutoff: 100
      sleep ToneRhythm.tick
    end
  end
  
  in_thread do
    ToneRhythmLength2.times do
      play :b2, attack: 0, release: 0.5, cutoff: 100, amp: 0.5
      sleep ToneRhythm2.tick
    end
  end
  
  sleep 4
end

live_loop :metronome do
  sample :drum_cymbal_pedal, amp: 0.1
  sleep 1
end

Or in a different loop with different loop speed for some polyrhythm fun…

##| ToneRhythm = [1, 1]
use_bpm 100
ToneRhythm = [0.5,1,0.5,0.5,0.25,0.25]
ToneRhythmLength = ToneRhythm.length

ToneRhythm2 = [1,1,0.5,0.5,0.75,0.5]
ToneRhythmLength2 = ToneRhythm2.length

live_loop :a, sync: :metronome do
  sample :perc_snap, amp: 0.1
  
  in_thread do
    ToneRhythmLength.times do
      play :e2, attack: 0, release: 0.5, cutoff: 100
      sleep ToneRhythm.tick
    end
  end
  sleep 4
end

live_loop :b, sync: :metronome do
  in_thread do
    ToneRhythmLength2.times do
      play :b2, attack: 0, release: 0.5, cutoff: 100, amp: 0.5
      sleep ToneRhythm2.tick
    end
  end
  sleep 3
end

live_loop :metronome do
  sample :drum_cymbal_pedal, amp: 0.1
  sleep 1
end
1 Like

Thanks for spoonfeeding a n00b. I see my first and biggest problem is not comprehending the utility of in_thread, so I’ll study up on that. From the tutorial, I adopted the idea of writing complex functions, then calling those functions from within loops. I live code by making alterations in the function declarations, leaving the live loops alone. I’d totally forgotten about in_thread. Can you comment on any costs and benefits between the two paradigms? I guess I’m basically asking about the difference between a function and an in_thread called from inside a live_loop. Also, my messing with the tick flow seems a good way to lose visual track of what your doing, so maybe you have a comment on tick operations?

Putting the functions in loops requires me to be mindful of the functions’ order in the loop, which is sometimes useful. It can be constraining, though, so to keep individual functions independent, I was putting one function in each loop. That does seem like unnatural coding.

In the in_thread paradigm, which I take it is SPI’s basic way of doing things (before you try to get weird), I see that I must fill out my rhythm vectors to at least as many components as the sleep number in the live_loop, or else the rhythm gets padded out with rests. I’d gotten used to that automagically not happening, so that the rhythm vector [1] gave the same performance as [1, 1, 1, 1], a shorthand that may also have no real benefit. I’m actually a bit confused, though, as to what happens in your example when you have ToneRhythm2 = [1,1,0.5,0.5,0.75,0.5], whose six components add up to 4.25 beats, while needing to get ticked through six times to get to the end. When I squared it off to [1,1,0.5,0.5], it did what I expect, getting padded out to 4 with an invisible rest. Invisible can be confusing though; my original method always plays exactly what you see, and it’s up to you to ensure the durations add up to what you want.

I’m just trying to come up with a working method where I can keep track visually of the temporal modulations I’m working on in addition to keeping track aurally. Eventually, I want to put these rhythm vectors in matrices, then do matrix operations to not only transform a matrix’s values, but also to modulate its dimensions. It seems the number of columns should always match the loop’s sleep number.

Taking the easy one first…what is happening is that the rhythm defined by your sleep vectors is getting kicked off in a new thread at the start of each bar, and stops when it’s gone through all the elements - whenever they happen. So if you have sleeps adding up to more than a bar, you’ll get notes playing over each other - which can be a good or bad thing depending on what you want.

Think of each created thread as it’s own entitiy, which it is, and everything follows. Each journey through the live loop, a new thread is created and goes away on its own until it’s done its work.

If the total sleeps are less than a bar, then it will stop short of the bar and wait for the next bar. That’s what you call ‘padding with rests’. Again, you might want that or not.

If you want the rhythm to just go round and round regardless - that’s where we came in, and that’s the easy thing to do but it’ll get out of sync, depending on the specific pattern. Again, might be good.

If you want some kind of failsafe, so that you can code the sleeps to be whatever, but they cut off at the end of a bar, I’m sure a bit of logic would sort that out. Not tried it but I’d add up the sleeps and only play notes if they are inside the current bar.

The more complicated question - threads and functions. They’re not mutually exclusive, you can call a function from within a thread, so you could put all your ‘playing code’ in a function if you like. This is the joy and the pain of SPi - there’s lots of ways to acheive the same effects.

I feel we are moving towards a eureka moment. I had that when I started with Spi. Aaaah, now I see :smile:

1 Like

Yes, some aha’s over here already. The other issue is that I don’t precisely know what I want, so I’ll have to mess with different ways to see what they lead to, how they feel to live code, etc. I understand there are no mistakes, just sounds you either want or don’t want, but I do need to make sure that I’m not n00b’ly writing some sort of super-inefficient code that’s going to waste a lot of CPU compared to some other way of achieving the same thing more efficiently. As long as there’s no obtrusive performance hit, though, I’m happy to sacrifice CPU efficiency for a more visually obvious and trackable representation of what’s going on.

1 Like

Spi is pretty light on resources, the only problem I’ve had when using effects and with some of those you have to be careful about stucture. For the kind of thing we’re talking about here though, I doubt it’ll be a problem. Famous last words.

Ultimately, I expect I’m heading toward outputting to external MIDI modules, using SPI as a sketch pad and when away from the external hardware. VST’s are also likely, I guess, but in either case I wouldn’t be using SPI’s effects, which the tutorial warns can become CPU hogs. I haven’t put SPI’s synths through the studio monitors yet, so who knows.

1 Like

That’s a plan. That’s my setup, although I do use a lot of SPi sounds - there’s good stuff in there. And the sample playing abilities are really good. For instance, drum/percussion patterns - just as good playing samples from within SPi as on a separate box - unless it’s to use a more delicate thing like Aria Player with lots of multi-level samples.

You’ve not asked for this but if you’re interested in percussion a pattern stuff, I’ll point you a this (long) topic General Purpose Drumkit - #51 by soxsa which ends I think with something like the current code I’m using - and it’s proved it’s worth. Myself, I’m a lot happier coding with this than tapping button combinations on some dedicated box. SPi is just so much more flexible too.

Might spark some ideas, and incidentally uses functions called from within threads within loops :smile:

1 Like

Ha ha, that’s the thing: I’ve never been much on samples, and my whole thing is to jam live on percussion with whatever I have going on electronically. So, unlike most people, I pretty much never sequence percussion in any form because it steps on my toes! I’m trying SPI as something that I can make live adjustments to without putting down my main instrument.

1 Like

Ah right! I’m predominantly a drummer myself, but I’ve basically abandonded the kit during lockdown to concentrate on this. After trying a lot of other electronic kit, I’ve found that whatever the question, SPi is the answer - so it’s worth the effect IMO.

No, live coding from a drum kit: no way. Me, I exclusively use the mobile yet fully expressive ZenDrum and aFrame. I linked to a couple of interesting demos there and don’t know any of those folks personally.

2 Likes

They look fantastic! What things there are these days eh?