interesting. pure curiousity at this point: is the “stack-allocated” Delay family just buggy, or just DelayC, and/or is this a bug specifically related to using an unreasonably small value for maxDelayTime?

i am glad we aren’t freakily introducing some unexpected large delay in the JACK process loopback.

[ed]
oh, i should also mention that if you ever become specifically interested in very short delay times (e.g. as resonators) then you will probably want to look at the Allpass and Comb families as well.

3 Likes

absolutely, i was curious too. i tried using DelayN/C/L using the default maxdelaytime (0.2s) which gave me 0.2s of delay even though i set the delayTime < 0.2s. so it seems that its not due to my small value of maxdelaytime(but weirdly that wouldn’t clamp it down), and its the same across all of Delay class.

interestingly, unlike Delay, the Comb works well if i set the maxdelaytime to the delay time and turn down the decay all the way. for instance, this seems to give a delay of 50 ms:

CombC.ar(input,maxdelaytime: 0.05, delayTime: 0.05, decaytime:0, mul:volume)

but Comb doesn’t give me a 50 ms delay if the maxdelaytime is set higher. that’s nice, then i don’t need to allocate those buffers with BufDelayN as i can replace with Comb!

1 Like

okay one more update. it turns out there were two issues.

  • issue #1: it turns out i had foolishly set fade_time to a constant 0.2s, so no matter what strategies i used to catch transients, they were getting faded (if you started recording from a stopped position). i took out fade_time completely now because its redundant, as the vol pinch parameter effectively slews the rec_level which has the same effect on both the beginning and end. this can also be adjusted in parameters and not set to a constant (30 ms by default).

  • issue #2: even with issue #1 fixed, i still miss transients on initial hits. now this is fixed with adding the lag by turning the catch transients w lag to on (off by default).

to show how the parameters can manifest, here’s three different recordings, all with different parameters, all starting with a single snare hit:

image

in the first panel, the volume pinch is turned up so you see it fade in, all transients are missing.

in the second panel the vol pinch is turned to 0, but you can see (compared to the third panel) its missing the initial transients.

in the third panel, vol pinch is turned to 0 and the catch transients w lag is turned on. you can see all the transients now, though it introduces a little more lag. i’ve used the magic number of 35 ms for the lag time, and this seems to work really well usually, except that i think there is a little jitter that causes this to flucutate +/- few ms.

@kingmetal i encourage you to try now if you’d like, and if everything seems good i’ll release! if not i’ll give it a few days to see if any more bugs shake out.

1 Like

i’m sorry to be weighing in yet again but might be good to know:

fade_time has an entirely different purpose from the level slew times. the former actually controls the length of the crossfading window while looping. the ability to apply this crossfade with arbitrary record and preserve levels is really the one feature of softcut that can’t be easily replicated with much simpler DSP structures in supercollider… so i recommend using it

1 Like

thanks @zebra, please weigh in! that’s great to know, i couldn’t find a lot of fade_time info so i was making a lot of assumptions.

i think i’m still missing something about fade_time. when i use fade_time, the playback after recording a loop has seemingly a lot of fade when starting the loop over. here is an example:

image

these are two recordings of playing back a loop without (top) and with (bottom) the use of fade_time. the bottom (with fade_time) shows a deeply faded region at the start of the second play of the loop (highlighted region).

i began to think this might be because i had fade_time on during recording, but i got similar results toggling fade_time after recording had finished, so it seems to mainly affect playback.

also - the audio above is a drum beat, quantized to the loop , so its not a continuous held-out note which might really good use out of a crossfade. although, i’m not sure it if this is how the cross-fade should look?

the crossfade affects both playback and recording.

yes, the start/end of the loop is the beginning of the crossfade.

under the hood, there are two sub-heads.

description assuming rate > 0:

let’s call the position of the sub-heads P and Q,
the start and loop points are A and B, the loop length is B-A = l, and the fade time is f.

let’s call the wall-clock time t, and say t=0 when the loop first starts playing/recording.

let’s also say that full overdub is enabled (rec_level=1.)
so on lua side you have done something like

sc.loop_start(1, a)
sc.loop_end(1, b)
sc.loop(1, 1)
sc.fade_time(1, f)
sc.rec(1, 1)
sc.rec_level(1, 1)
sc.pre_level(1, 1)
sc.position(1, a)
sc.enable(1, 1)

on the first pass through the loop, you will hear subhead 1 fading up between t=0 and t=f as P advances from A to A+f.
as soon as P=B, subhead 1 begins fading out and subhead 2 begins fading in.

as subhead 1 fades out, it is still recording, adding incoming signal to the buffer in [B, B+f].

so on the second pass through the loop, from t=l to t=l+f you should hear a combination three things, with P = [B, B+f] and Q = [A, A+f]

    1. original contents of the buffer past the loop endpoint, from B to B+f, being played by subhead 1, fading out
    1. original buffer contents at start of the loop, from A to A+f, played by subhead 2, fading in.
    1. new signal from the time interval t=[0,f], which has been mixed with the original in the first pass, also fading in.

then from A+f to B its a straight combination of the original contents and the signal from the first pass.


so, in a nutshell:

  • the xfade is only audible at the loop start. this is just simpler to implement than having it centered on the loop start, and is easy to reason about even if it’s not always the most convenient thing for the application.

  • so, for something like drum loops you may want to actually place the loop start before the first transient.

  • you also want to be careful to have some post-roll in any samples you load or buffer regions that you copy if you want the fade time to have a useful effect.

4 Likes

Uh I should say in case it’s not clear: if you stop recording before the loop end, by using the rec level (recommended) or the rec flag (not), the fade out of the write level will not occur. (Rec level is applied before fade, and there is no command that says “stop recording after the current loop+fade has completed,” although that would probably be a nice command to have )

2 Likes

@zebra again thank you! this is really really helpful. one more quick question while i digest everything.

the docs for softcut indicates that fade_time is a position in time (like loop_start) and not a duration as indicated by your analysis (and also by CroneDefs):

fade_time (voice, pos)
    set fade time.
    Parameters:

        voice int : voice index
        pos number : loop start position in seconds

is this correct?

No, thats a typo / copypasta, it should say fade duration or whatever. Seconds is the correct unit though

Cronedefs doesn’t actually relate… if there is a comment indicating a connection, it’s stale

… oh I see. You mean the \play_fade synthdef. This is not used in the system, but it could be useful in an engine if you need something like that

1 Like

version 1.4.0! - no more missing transients

  • new feature (thanks to @kingmetal who wrote this!): more options to record from: left, or right, or both, or tape (PARAMS -> recording -> input type)
  • new feature: triggers for stopping and playing for each loop (PARAMS -> loop X -> play/stop trig)
  • new feature: improved recording performance when arming, don’t miss transients by activating (PARAMS -> recording -> catch transients w lag) (demo: https://www.instagram.com/p/CIwWQTJBqED/)
  • bug fix: setting volume to 0 should keep volume lfo from rising above 0
  • under the hood: cleaned up volume pinching to use recpre slewing

as always, lmk if there are :bug:

extra thanks to @zebra for all the supercollider and softcut explanations.

9 Likes

ok, i think something like this should be added to the API:
[ https://github.com/monome/softcut-lib/issues/39 ]

could also consider optional arbitrary simple delay on softcut input, for general “lookahead” effects. (but i would do this in the mixer client, not the softcut process.)

and i was going to add something like “start without fade”
that’s still possible, but i’m less sure its totally necessary:

what happens if you set fade = 0, then start, then set fade to whatever you want for xfaded looped play and/or rec.

sc.fade_time(0)
sc.position(A)
sc.fade_time(1)

i think it should result in a “cold start” of the loop and then new fade value starting at next loop point.

2 Likes

if i do this, then i still get a pinched start for each subsequent loop after the fade is set. the following are two recordings, recorded with fade at 0, and recpre slew at 0:

the top is played with no fade, and then the bottom was when i set fade to ~200 ms during that playback (dropped underneath for comparison). smaller amounts of fade give shorter amounts of attenuation as expected.

but… as i thought over what you said i think i might be doing something wrong to utilize fade…i don’t do any post-roll recording! i set loop_start and loop_end and then hitting record will loop through that section over and many times as set (default is 1 loop, but you can continue to overdub loops if setting >1).

so i think all of this has been my great misunderstanding, because if i add post-roll (by subsequently copying the beginning of the buffer to the end of the loop after recording), i can set xfade-time to 0.5s and that “fade in” behavior completely disappears and i get nice continuous drum loops:

image

top is without fade, bottom is with fade_time of 0.5s, after i copied the buffer from the beginning of loop to the end of the loop.

so i really need to be adding in the post-roll!

Had a really busy time, so I got a bit behind with the updates in this thread.
There’s one thing I’m missing I think: how do I make sure all my MIDI mappings survive the next power cycle?

@infinitedigits finally had some time to sit down and play with the new changes and it’s working great! I’m not missing transients now with the catch transients w lag option set, which is incredible!

Out of curiosity, is the monitoring path delayed when the script is recording and catch transients w lag is on? I ask because it didn’t feel delayed to me, which is great, but I wondered if that was just me not being awake enough to notice a 30ms or so delay, lol. Not a criticism just curious for my own learning.

I was going to start mapping controls to my MIDI Fighter Twister and report back with a mapping that works for me but my MFT mysteriously died over the weekend, lol. Going to spend some time with the script this week and enjoy it! Thank you so much for spending so much of your weekend hacking on it.

1 Like

minor update: v1.4.1

  • post-roll is added to the end of loops during recording of loops to enable crossfading between start and end points.

@kingmetal great! glad its working. when you turn on catch transients... it will turn off the monitor and play a delayed signal. the delay is ~30 ms, so its pretty subtle. but you can definetly hear it if you record a loop of drums - after the recording is over leave your drums input playing as the normal monitor switches back on and it will sound a little off from the recorded loop.

that would be great! right now i’m using a coroutine to add ~1 sec of postroll at the end of the loop using buffer_copy_mono after the recording passes the 1 sec mark. this probably qualifies as clunky (though most my code might qualify).

i have to admit i don’t know - i haven’t used the MAP function…if there is something i need to add to make it work lmk.

4 Likes

so much good energy on this thread! :sparkles:

currently, midi mapping (PMAP) only get saved when you save a PSET. since there’s only one PMAP per script, i’ve got a PR queued up to initiate this save whenever a parameter is mapped – this will de-necessitate saving a PSET and will ensure these maps are retained between power cycles :slight_smile:

9 Likes

thanks a lot @dan_derks! I see, so basically I save a PSET for the MIDI mappings and use oooooo’s saving mechanism to save parameters/loops, correct?
Indeed norns’ MIDI mapping functionality is a bit weird when it comes to saving in general, but maybe that’s something for another topic.
Is there already a discussion on the matter somewhere? I can’t seem to find one.

2 Likes

Thanks for the explainer @infinitedigits, that makes perfect sense! This is so well implemented and I’m so grateful that you add these features as toggles. I love how non-prescriptive oooooo is!

I’ve got some ideas I may try to fiddle with in over the holiday break around the ability to over-ride the global recording input settings per loop for extra flexibility, and then some ideas around being able to stereo link loops. If they pan out and you like what they bring to oooooo, cool, if not I’ll learn something.

1 Like

@infinitedigits you probably have seen this already but I was background-watching some Youtube on my lunchbreak and couldn’t help but notice that Elin Piel talked about Norns during BoBeats’ Best of 2020 video and both Barcode & Oooooo are both featured in it.

Keep up the great work!

16 Likes

oooooo needs to be featured in any best-of-2020 list imho! :slight_smile:

7 Likes