Synced tempo change like ritardando/accelerando

Miles Davies's icon

I'm looking to implement a procedure to trigger a function/curve object with exponential/logarithmic shapes to start at a specific synced chosen division of tempo in ms to change to another chosen division of tempo in ms. I can probably put this together, but the maths to keep the beginning and end divisions in sync over a chosen period of time in ms with the original tempo is beyond my capability.

I know this is a big ask, but can anyone give me a starting place on this, or is there a standard way to implement the maths.

Thanks.

Miles Davies's icon

Here is where I am at the moment, but there is multiple bangs from the metro object at the trigger bang which I'm not sure why. This is a very simple version of what I want to achieve. Can anyone suggest why i'm getting odd bangs at the beginning of the curve, and perhaps a workaround. Thanks.

Max Patch
Copy patch and select New From Clipboard in Max.

Roman Thilenius's icon


the general idea is that you would need to calculate in advance at which future timepoint(s) you want the curve or ramp to end, and then only allow curves or ramps to be chosen which will end there.

say like ... as if the prior clock would keep running in the background.

Miles Davies's icon

Yes Roman. So, if I'm running a metro at 500ms I would want the curve to start at 500ms and end at a division of 500ms over a desired amount of time. And as you say the original clock is still running in the background. The final bang of the curve would ideally land on the next bang of the original clock at the end of the desired curve length.

Roman Thilenius's icon


havent looked at your patch yet.

if you need do that by user control, simply calculate a list of possible values and put it in an umenu.

and if you need it for realtime, build it for nonrealtime first.

you will see that you have to (that is just an opinion & how i do it) leave the starting point variable.

because one of the 4 values [start, end, new speed, form of the curve/ramp] must be variable, and you dont want it to be the end, the form, or the new speed. :)


Miles Davies's icon

Probably not worth to look at my patch as very basic. Just to give an idea of the ritardando (and it was flawed).

I now see that by varying the curvature will inevitably change the time points, and the probability of landing on the original timing again would be difficult unless it was pre-determined in advance.

Its going to be more difficult than I originally thought.

I see what you mean by variable start, but would that work? Would it sound in time? But as you say, The end time definitely needs to be in time, length/speed needs to be accurate, but perhaps the curve form can be adjusted to meet all other requirements, but may be difficult.

This mechanism may not need be totally variable. Just thinking out loud. Perhaps I just need to store parameters for a few variations to recall, or I save some variations of start/end time, curve, length that work with my timing prior to running the patch at a given interval. That may work.

Roman Thilenius's icon

"I now see that by varying the curvature will inevitably change the time points"

thought so. ;)

can be solved later, again by making restrictions, but first get it to work for a linear ramp.

my suggestion to delay the beginning was not 100% appropiate when i thin k about it.

it will work good when moving from very slow to a very fast speed but not the other way round.

of course it would be ideal to go from beat to beat, and eventually it is unavoidable for what you want to do.

however, also when you want to have it exact it is a good idea to look at he ramp calculation from the end.

Miles Davies's icon

Here is where i'm at so far.

Max Patch
Copy patch and select New From Clipboard in Max.

Still far away from where I need to be.

Anyone have any better implementation for this?

Roman Thilenius's icon


are you already aware that going from 120 BPM to 125 BPM requires a looooong minimum lenght? :)

Miles Davies's icon

Not sure what you mean ;)

double_UG's icon

Max Patch
Copy patch and select New From Clipboard in Max.

Miles Davies's icon

Thanks DOUBLE_UG. I'll see how it works. Initial tests work well. I'll try to see what is going on with these objects.

Roman Thilenius's icon


your curve~ - number~ is not enough to get an accurate result - as well as line-driven envelopes would be.

but maybe a completly different approach is interesting here.

imagine the following:

you let your main metro just run.

then you [delay] single bangs from the metro like so:

0 0 0 0 0 5 10 15 20 25 30 35 40 45 50 55 50 45 40 35 30 25 20 15 10 5 0 0 0 0 0

the time of the first regular bang will be 100% the original, because it is.

the only thing what changes is the theopretical count of tat bang in relation to the non-delayed matro bang at the same position.

(note that for a linear ramp you had to use percentages, not 5+5+5)

Miles Davies's icon

Thanks Roman. You mean like this:

Max Patch
Copy patch and select New From Clipboard in Max.

Miles Davies's icon

Thats pretty useful.

Max Patch
Copy patch and select New From Clipboard in Max.

Miles Davies's icon

Just need to work a way of the number of delays needed between time points.

Miles Davies's icon

@DOUBLE_UG Yes, that works for me well. Not quite sure on whats going on. I looked up the object help files individually, but still not sinking in for me. Are you able to highlight what is happening? Thanks.

Roman Thilenius's icon

for linear fades, their speed is the average speed between start and end speed.

Roman Thilenius's icon

no i meant using only a single del object with variable time. but you got the principle.

number of delays: set a max of 30 or so, the ones which you dont need can be set to 0.

if you first want to get faster, you had to use a delay buffer around it, but we leave that for tomorrow.

Kenneth Newby's icon

Here's the way I do it using modulo divisions of an integer stream. Rock steady sync regardless of tempo change because there's only one master stream that every rhythm player is referenced to. It gets really interesting when the patterns don't sum to the same total value and all kinds of phase relationships can be developed.

Max Patch
Copy patch and select New From Clipboard in Max.

Miles Davies's icon

Okay. Its late here. I'll do more work and pick this up again tomorrow. Thanks for the insight. Very helpful.

Miles Davies's icon

Thanks Kenneth. That sounds and looks very interesting. Unfortunately max console is showing newobj: delta: No such object.

Kenneth Newby's icon

Oops... that's an old personal abstraction. Here's the patch with it's code in a conventional sub-patcher. This one also initializes properly to the beginning of each pattern and can take a time value of 1 as a minimum.

Max Patch
Copy patch and select New From Clipboard in Max.

double_UG's icon

Max Patch
Copy patch and select New From Clipboard in Max.

Miles Davies's icon

@ Kenneth. Thanks for the example. Its good to see alternative ways to sync tempo change. Although I was looking for more of a non-linear speed up slow down.

Miles Davies's icon

@DOUBLE_UG Thats great. Makes sense. Specially with the function for visuals. Thanks again. Very informative.

Miles Davies's icon

@DOUBLE_UG - After testing your patch I can confirm it is perfect for what I need. Exactly what I was looking for. Thanks again.

Miles Davies's icon

@Roman - I would still like to pick up where we left off. Can you elaborate on what you mean by " no i meant using only a single del object with variable time."

It take it a 'del' object is a delay object. I checked all help files for the delay object, but nothing stating it would do like tapout with multiple delays.

If you have Max open would you care to post an example?

Thanks.

Roman Thilenius's icon


yes you´re right, what i suggested only works with [pipe]. let me see...

gotta run. :)

[t 1]
[pipe]
[t b]

for multi-buffer del

Miles Davies's icon

Not sure if this is what you mean:

Max Patch
Copy patch and select New From Clipboard in Max.

It works but still need a lot of ironing out. Length of desired sequence, number of delays to sync.

Roman Thilenius's icon


yep, something like that. feeding it a list is best while developing.

which parameter do you want to have set fixed: total length of ritardando/accelerando in beats/orignal-metro? or speed change of the ritardando/accelerando in BPM/s?

Roman Thilenius's icon


and it will make a difference whether you want to use an even or odd number of beats.

i think even is cooler because then you actually have "end speed" between the two beats in the center so that the "end speed" parameter makes sense.

Roman Thilenius's icon


metro runs at 300
user wants a rit/accel from 300 to 400 and back to 300 in 7 seconds, curve: linear

a 300 ms clock has 7000 / 300 = 23.33... beats during that time
so the user´s choice has to be rounded down to 6,9 seconds / 23 beats

now we need to produce a ramp like that:
6900ms == 300-rit-400 (3250ms) + center (400ms) + 400-accel-300 (3250ms)

the up-part has to perform in ((lenght - max/min speed) / 2) == 3250ms (down will be easy then)
and during this time the periods between the beats have to increase in equal steps so that a-1 is 300 and the last one + 1 is 400.

well, the average speed of the beats during these 3250 ms will be 350ms per beat. but that wont help, because it doesnt fit into 3250. hrmpf.

you can see the smoke coming out of my left ear, do you?

Roman Thilenius's icon


new try.

it should be musically irrelevant how precise the speed parameter can be set.

so we could leave that center thing out and only perform a theoretical triangle shape.

and only allow average speeds of the in/out ramp (which is (starting speed of 300 plus end speed of 400) / 2 = 350) at only 75%, 66,66%, 50% and so on, i.e. like in 1/8, 1/8T, 3/16, 1/16 notes style.

Miles Davies's icon

Yes. :)) I do see the smoke coming out of your left ear. Haha! If its doing it to you, then I have no hope.

Maybe i'm expecting too much. Double_UG pretty much came up with a near perfect solution and this is causing me brain haemorrhage just reading what you suggest. I appreciate it and all.

Just to re-cap. I am looking for a ritt / acc to start with a bang at a certain speed and finish at another speed (say from 500ms to 50ms) over a curve with varied length to meet beats/bars. Some call this bouncing ball etc.

Now, whether this needs to be all so accurate to be musically pleasing is something that I have maybe overlooked. Your delay suggestion with varying delays has given me something to work with and actually, even if the total length of all delays steps over the endpoint and goes into the second loop, you get some very interesting rhythmic patterns evolve, which has opened up a whole new area for me to explore.

I think I can probably live with what we have right now and store fav settings as presets for future recall.

Thanks very much for your patience and generosity with your time.

Miles Davies's icon

But I will look for a way to change total number of output bangs.

Roman Thilenius's icon


"Now, whether this needs to be all so accurate to be musically pleasing is something that I have maybe overlooked."

completely independent from your original application i was about to find something which is so accurate that it would be the perfect general solution.

the most simple quick patch could probably be made using a parallel metro driven by a [line].

but of course the problem with line is that it will cause jitter and we still dont have control over the maximum value; even if you put it on 1 milliseconds grain time, the last bang might off to the original metro.

a free beer for anyone who finds out why this only works for 1000 and 2000 ms, but not for 1500 or 4000.

#P window setfont "Sans Serif" 9.;
#P flonum 412 526 51 9 0 0 0 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P button 52 224 35 0;
#P button 365 100 35 0;
#P user umenu 530 49 100 9109543 1 64 65 1;
#X add 500;
#X add 1000;
#X add 1500;
#X add 2000;
#X add 2500;
#X add 3000;
#X add 3500;
#X add 4000;
#X add 8000;
#X add 11500;
#P window linecount 1;
#P comment 100 50 100 9109513 run;
#P newex 55 479 73 9109513 loadbang;
#P newex 434 211 73 9109513 !- 1.;
#P newex 434 185 73 9109513 abs 0.;
#P comment 240 48 100 9109513 start rita & friends;
#P newex 630 282 73 9109513 t 1 0;
#P flonum 529 433 53 9 5. 0 1 139 0 0 0 40 204 140 222 222 222 0 0 0;
#P flonum 463 433 53 9 5. 0 1 139 0 0 0 40 204 140 222 222 222 0 0 0;
#P flonum 550 78 53 9 0 0 0 139 0 0 0 40 204 140 222 222 222 0 0 0;
#P newex 434 103 126 9109513 pack -1. 777.;
#P newex 434 130 50 9109513 t l 1.;
#P message 672 415 50 9109513 50;
#P message 610 415 50 9109513 500;
#N vpatcher 19 77 619 477;
#P outlet 108 107 15 0;
#P inlet 126 49 15 0;
#P connect 0 0 1 0;
#P pop;
#P newobj 412 308 142 9109513 p insert_curving_here;
#P newex 397 469 142 9109513 expr $f1*($f3-$f2)+$f2;
#P newex 741 395 73 9109513 loadbang;
#P toggle 342 392 15 0;
#P toggle 190 226 15 0;
#P newex 123 261 50 9109513 gate;
#P newex 105 187 68 9109513 onebang;
#P toggle 33 544 15 0;
#P newex 135 523 63 9109513 click~;
#P toggle 105 72 27 0;
#P newex 105 128 73 9109513 metro 500.;
#P newex 136 560 91 9109513 dac~;
#P message 55 505 50 9109513 set 1. -1;
#P newex 217 523 63 9109513 click~;
#P newex 434 159 91 9109513 line 0. 5;
#P newex 372 49 72 9109513 t 0 b 1;
#P button 238 65 35 0;
#P newex 326 503 73 9109513 metro 77.;
#P comment 479 492 83 9109513 110.map-up;
#P connect 12 1 34 0;
#P connect 30 0 6 0;
#P connect 9 0 8 0;
#P connect 8 0 12 0;
#P connect 14 0 13 0;
#P connect 6 0 10 0;
#P connect 13 0 10 0;
#P connect 11 0 7 0;
#P connect 10 0 7 0;
#P connect 2 0 12 1;
#P connect 12 1 13 1;
#P connect 3 0 14 0;
#P connect 26 0 14 0;
#P connect 6 0 5 0;
#P connect 1 0 5 0;
#P connect 5 0 7 1;
#P connect 15 0 1 0;
#P connect 3 2 15 0;
#P connect 26 1 15 0;
#P connect 3 1 33 0;
#P connect 12 0 3 0;
#P connect 17 0 1 1;
#P connect 18 0 17 0;
#P connect 29 0 18 0;
#P connect 17 0 35 0;
#P connect 3 1 22 0;
#P connect 22 0 21 0;
#P connect 21 1 4 0;
#P connect 21 0 4 0;
#P connect 4 0 28 0;
#P connect 28 0 29 0;
#P connect 19 0 24 0;
#P connect 24 0 17 1;
#P connect 20 0 25 0;
#P connect 25 0 17 2;
#P connect 32 1 23 0;
#P connect 23 0 22 1;
#P connect 16 0 19 0;
#P connect 4 1 26 0;
#P connect 16 0 20 0;
#P window clipboard copycount 36;



Miles Davies's icon

Getting very close. But playing alongside a constant beat it doesn't sound rhythmical because of where the clicks fall. Do you think the end point really needs to sync? Or is it better to be flxible so the clicks are better placed to be more pleasing to the ear? I presume with a flexible curve rather than linear, the clicks may fall better. So many variables to consider. Is it generatively possible?

To throw in a curveball, maybe a bezier curve be more suitable :D

I'm not getting that free beer am I?

Miles Davies's icon

btw, originally i was just looking for a mechanism to do a ritardando, or an accelerando. Not both in one pass. Does this become easier to calculate?

Miles Davies's icon

I found a ritt-acc from the RTC library by Essl. Worth a look but no syncing apart from one start/ or end time.

Max Patch
Copy patch and select New From Clipboard in Max.

Roman Thilenius's icon


i think only one is easier. but the issue that [line] doesnt do what one thinks is still present.

as you can see i only use one line for up and down and then just fold its range using abs().

remember: in theory, the average speed between min and max is (min+max)/2 so we should always know the lenght. no idea why it doesnt work as expected in my patch.

Roman Thilenius's icon

yes i think musically it would be best if the end fits to the base speed again.

otherwise it would be too easy to program. this syncing thing is what the whole challenge is about, isnt it?

Roman Thilenius's icon

oh wait i hitnk thats where i made my error. i was using 300 to 400 yesterday but in your patch its 50 to 500.

i think i didnt take that into account (that the average speed is not 500 - but has to be in small integer relation between 500 and 550/2)

lets check that.

Roman Thilenius's icon


damn, closer but not solved.

5500 millliseconds (smallest common multiple or however that is called in foreignish) seems to work fine, but 11000 does not.

*cries*

ritardando - retardedo - retiredo

#P window setfont "Sans Serif" 24.;
#P window linecount 1;
#P comment 864 244 100 9109528 I;
#P comment 864 218 100 9109528 I;
#P comment 860 191 100 9109528 A;
#P window setfont "Sans Serif" 9.;
#P number 889 139 56 9 1 0 1 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P number 799 139 56 9 1 0 1 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P number 896 48 35 9 1 0 1 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P user incdec 868 48 15 15 0 0;
#P number 809 48 35 9 1 0 1 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P user incdec 781 48 15 15 0 0;
#P newex 799 103 50 9109513 * 500;
#P newex 889 103 50 9109513 * 275;
#P button 159 467 35 0;
#P newex 222 305 73 9109513 t 1;
#P newex 222 274 73 9109513 loadbang;
#P flonum 815 355 51 9 0 0 0 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P newex 783 301 117 9109513 expr ($f1+$f2)*0.5;
#P newex 610 387 73 9109513 t b b;
#P flonum 412 526 51 9 0 0 0 139 0 0 0 221 221 221 222 222 222 0 0 0;
#P button 235 469 35 0;
#P button 365 100 35 0;
#P user umenu 530 49 100 9109543 1 64 65 1;
#X add 5500;
#X add 11000;
#P comment 100 50 100 9109513 run;
#P newex 55 479 73 9109513 loadbang;
#P newex 434 211 73 9109513 !- 1.;
#P newex 434 185 73 9109513 abs 0.;
#P comment 240 48 100 9109513 start rita & friends;
#P newex 630 282 73 9109513 t 1 0;
#P flonum 529 433 53 9 5. 0 1 139 0 0 0 40 204 140 222 222 222 0 0 0;
#P flonum 463 433 53 9 5. 0 1 139 0 0 0 40 204 140 222 222 222 0 0 0;
#P flonum 550 78 53 9 0 0 0 139 0 0 0 40 204 140 222 222 222 0 0 0;
#P newex 434 103 126 9109513 pack -1. 777.;
#P newex 434 130 50 9109513 t l 1.;
#P message 672 415 50 9109513 50;
#P message 610 415 50 9109513 500;
#N vpatcher 19 77 619 477;
#P outlet 108 107 15 0;
#P inlet 126 49 15 0;
#P connect 0 0 1 0;
#P pop;
#P newobj 412 308 142 9109513 p insert_curving_here;
#P newex 397 469 142 9109513 expr $f1*($f3-$f2)+$f2;
#P newex 610 356 73 9109513 loadbang;
#P toggle 342 392 15 0;
#P toggle 190 226 15 0;
#P newex 123 261 50 9109513 gate;
#P newex 105 187 68 9109513 onebang;
#P toggle 33 544 15 0;
#P newex 135 523 63 9109513 click~;
#P toggle 105 72 27 0;
#P newex 105 128 73 9109513 metro 500.;
#P newex 136 560 91 9109513 dac~;
#P message 55 505 50 9109513 set 1. -1;
#P newex 217 523 63 9109513 click~;
#P newex 434 159 91 9109513 line 0. 1;
#P newex 372 49 72 9109513 t 0 b 1;
#P button 238 65 35 0;
#P newex 326 503 73 9109513 metro 77.;
#P comment 479 492 83 9109513 110.map-up;
#P window setfont "Sans Serif" 24.;
#P comment 649 68 100 9109528 <-------;
#P connect 5 1 27 0;
#P connect 5 0 29 0;
#P connect 22 1 5 0;
#P connect 22 0 5 0;
#P connect 26 0 18 2;
#P connect 26 0 38 1;
#P connect 25 0 18 1;
#P connect 25 0 38 0;
#P connect 48 0 47 0;
#P connect 48 0 43 0;
#P connect 46 0 45 0;
#P connect 46 0 44 0;
#P connect 43 0 50 0;
#P connect 44 0 49 0;
#P connect 47 0 48 0;
#P connect 45 0 46 0;
#P connect 14 0 11 0;
#P connect 14 0 42 0;
#P connect 2 0 6 0;
#P connect 2 0 35 0;
#P connect 13 1 14 1;
#P connect 31 0 7 0;
#P connect 10 0 9 0;
#P connect 9 0 13 0;
#P connect 15 0 14 0;
#P connect 7 0 11 0;
#P connect 11 0 8 0;
#P connect 12 0 8 0;
#P connect 3 0 13 1;
#P connect 41 0 15 0;
#P connect 27 0 15 0;
#P connect 4 0 15 0;
#P connect 7 0 6 0;
#P connect 6 0 8 1;
#P connect 40 0 41 0;
#P connect 16 0 2 0;
#P connect 27 1 16 0;
#P connect 4 2 16 0;
#P connect 4 1 34 0;
#P connect 13 0 4 0;
#P connect 18 0 2 1;
#P connect 19 0 18 0;
#P connect 30 0 19 0;
#P connect 18 0 36 0;
#P connect 4 1 23 0;
#P connect 23 0 22 0;
#P connect 29 0 30 0;
#P connect 20 0 25 0;
#P connect 21 0 26 0;
#P connect 33 1 24 0;
#P connect 24 0 23 1;
#P connect 17 0 37 0;
#P connect 37 0 20 0;
#P connect 38 0 39 0;
#P connect 37 1 21 0;
#P window clipboard copycount 54;

Roman Thilenius's icon


sorry, beeing dumb again and disobeyed my own theory.

the average of 50 and 500 is of course 450, not 550, you must substract:

expr ($f1-$f2)*0.5

common rhabarber magic number is then 4500 (9*500 == 20*225)

it seems to be a bit more exact at 9000 and 18000 compared to 4500 and 13500 but now it works.


Miles Davies's icon

Its looking pretty good. Nearly on point. As you say, some lengths work better than others. Its sounding more rhythmically aligning now. No need for 11000ms......
1000ms, 2000ms or 3000ms is maximum really. But i guess easier to hear if correct at longer lengths for testing purposes.

Roman Thilenius's icon


i have another idea how one could match the times, which i have to try on my mac in the next days:

- let the first half run to a certain point / certain bang

- now we know how long the duration between the start point (which was triggered by metro 1, like in current patch with that onebang) is - so we also know how long the reversed half will be, when we create the reversed half with an other line object.

- the time which is missing to end on the next 500ms mark will be inserted in the middle between rita and her sister accelera.

Roman Thilenius's icon


in signal domain it only needs 3 MSP objects to realize my proposed model with the "average speed" thing.

https://cycling74.com/forums/mathematical-problem-syncing-variable-speed-phasors

Miles Davies's icon

Thats even better. Tighter timing. I can work with that. Thanks very much.

Max Patch
Copy patch and select New From Clipboard in Max.

Roman Thilenius's icon


yes, unless you derivate messages from it, you wont get those as tight as in scheduler terms then.

but the math is easier and for not-extreme settings it will be sample exact.