playbenny / benny

a live music environment
Other
65 stars 3 forks source link

a proper looper block #84

Open jamesholdenmusic opened 9 months ago

jamesholdenmusic commented 9 months ago

softcut-like flexible reading and writing rates would be really fun with the polyphonic part of this

look at ipoke?

old notes: looper: either use softcut or ape it - multiple heads on one buffer, crossfading, smooth, varispeed writes

jamesholdenmusic commented 8 months ago

parameters:

jamesholdenmusic commented 8 months ago

midi trigger input should start playing from the nth slice i think undo isn't possible in this architecture (it's one loop in the buffer, not many iterations of the loop across it)

the patch should handle making a new wave if you select a blank one (it should wait a second or something? to avoid creating loads if you move the slider?) or just scan that param infrequently..

a midi looper is a separate but very similar project - it needs to store a timed list of events, but store a note's vel at its off so you can play it backwards too.

jamesholdenmusic commented 8 months ago

needs to be able to reliably link voices with an offset (eg one is playing 2 beats behind the record one, etc)

jamesholdenmusic commented 8 months ago

there are two challenging parts to the playback/record engine - variable speed and xfades record and play both do both of these, as the heads are together.

a xfade happens when you jump (inc looping) during a xfade there are two play heads, two record heads. rather than calculating position of the second one using speed etc it'd be simpler to take a snapshot of the offset between them at the moment of jumping and then use that to calculate the 2nd head's position.

ok xfades not too hard then.

varispeed: if speed <1 then you end up writing lots of samples into one buffer sample. i think you do an average? that way if preserved level is up you end up replacing it with the same amount? AH only write the buffer once the head has moved forward, otherwise just keep accumulating (otherwise the 2nd sample you write into a buffer sample then reads back a bit of the 1st sample as it's 'preserved' value) if speed >1 then each sample you receive is going to write multiple samples in the buffer. the start and end may not align with buffer-sample boundaries, so those ones are kind of like the <1 case. if speed ==1 then you don't know you're aligned with buffer-sample boundaries anyway so you kind of do it like the <1 case

jamesholdenmusic commented 8 months ago

normal varispeed playback - you'd interpolate, taking a single reading per output sample i think there's no benefit to reading all the samples? (and lowpassing, resampling downwards) as what's there is likely upsampled anyway

writing, if speed <1 then that's downsampling the input, and an optional AA filter here would help. the nyquist is nyq*speed? so if you're at 0.25 speed, running at 48k, nyquist would be 6khz.

recording is expensive, disable it if fader at zero.

read head is one whole sample ahead of the write head in the buffer, so the two never interfere

jamesholdenmusic commented 8 months ago

actually that doesn't work if you change direction. so they have to be the same sample. it's only written when the head moves off that sample so..

jamesholdenmusic commented 8 months ago

have a proof of concept working and pushed. in2 - rate mod or position mod (ie you set rate to 0)

need to fix waves page drawing need a way for this block to clear the draw-wave accrued peaks? or flag some of it for redrawing at least?

jamesholdenmusic commented 8 months ago

upsampling covers a multitude of sins lol, i'll enable it at the end (it's easier to tune it with upsampling off)

jamesholdenmusic commented 8 months ago

ui: a buffer viewer, playhead indicator buttons for common rates, a reverse button?

jamesholdenmusic commented 8 months ago

working - with linear interp on buffer fill and on read. to do: jumping (midi triggers), xfades, implement loop as a xfade jump instead, metadata writing, waves page fixes, ui

add a parameter to enable aa filter add a parameter to follow global tempo variations (like my old looper used to)

this does such a good BBD i'm tempted to wrap it up as an m4l device

jamesholdenmusic commented 8 months ago

xfades: ideally you want to do all the work at the moment a jump is triggered so - the main playhead jumps, volume starts at 0 and ramps up the secondary one has its offset set so that it goes where the main one would've. volume starts at 1 and ramps down. so there are two variables - fade_offset and fade_level, if fade_level>0 that head gets processed. and the xfade parameter is converted into the coefficient of the linear fade - ie the gradient that gets subtracted every sample. ignore jump requests during a fade? wouldn't be impossible or expensive to queue up one to start straight after fade but..

jamesholdenmusic commented 8 months ago

so xfades for looping is done, it can only do it on read - if you xfaded the tail of the write it would stack up in weird ways?

jamesholdenmusic commented 8 months ago

xfade jumping done, there's a trigger in, a jump in (midi mapped accross the length of the sample, kind of useless). need to do slice jump (looks up in slices list) metadata writing - it could add beats to the slices list? aa filter global tempo variation following

a UI - buttons for common rates, and to set loop length to preset beat interval (at the current rate), or to set rate so that loop is a preset beat interval (at current loop length), reverse button. probably draw the buffer too? how do you handle multiple voices for all the ui stuff?

jamesholdenmusic commented 8 months ago

real BBDs have a lowpass working as a 'reconstruction filter' at the playback end of things. i think that's what my linear interpolation is doing too though?

read and write and all the xfading stuff done now. but i think some bugs remain with looping:

ui can have wipe button?

jamesholdenmusic commented 8 months ago

initialisation! it should start at the loop start point, otherwise it could take 15 minutes to get there!

jamesholdenmusic commented 8 months ago

it's totally possible to keep track of where writing is probably going to occur and have all the read heads duck if they're going to cross that, to remove the last source of clicks here. sounds quite long but doable. thanks tehn :(

jamesholdenmusic commented 8 months ago

ok there's a buffer called intervoice. there are 8 positions per voice. if a voice is writing, it should write where it will be writing (+ a crossfade length) in the next vector if a voice is reading, it should check a list once per vector, the list of points in intervoice to look at is made by the max bit of the patch and stored in gen data