This is gonna be fun!

I like the buffer-loop suggestion. I think the reason that Reels picks up a little bit more of the transient is because I believe the record buffers are always running, so there’s less transition time between detection --> buffer start. It’s just a guess (I haven’t looked at the code closely) but starting the buffers when a loop is armed seems like it would improve performance. I like the idea of a “buffer loop” that grows vs just running the full buffer like Reels (I think) does. Reels gets into a weird state when you are recording near the end of the buffer, so your solution would account for that edge case.

It seems like @zebra was suggesting that the input sink to softcut could act as a record buffer, but it also sounds like implementing that would be in C which is wayyyyy outside of my skillset.

I am also not sure about how to write a latency-compensated start time to the buffer-loop. I suspect that switching to Onsets.kr is intended to lower the input detection latency to the point where it’s relatively negligible and we can just trust the detection time, but that’s just a guess. If the latency was low enough and there was no record buffer transition latency then the ADC / input latency would be all we’d be contending with, which should be less than 3ms @ 44.1khz according to @zebra’s specs (I don’t know what sample rate the Norns works at, but latency gets better at higher sample rates if I understand correctly).

EDIT: @zebra responded as I was writing this novella
EDIT 2: Was wrong about needing to implement the input sink in C. Glad to be wrong about that!

1 Like

if delaying the input was going to be an always-available feature, it would indeed be best to perform that in crone and implement it in C++. (no, i don’t recommend this as a beginning exercise in norns usage especially without comparable experience. it would be a fine first step towards contributing to / customizing the audio component though.)

2 Likes

Thank you @infinitedigits, your design is so thoughtful! Glad to see such great scripts that can be operated by only norns (while many others are norns + grid/ arc scripts that I cannot use…).

One suggestion on MIDI implementation, it would be great if the resonance parameter is invert, so it is ‘bypass’ in 0, full resonance in 127.

I’m thinking of a good layout on MIDI mapping with Fighter Twister, what @encephalitislethargi suggested is very helpful, maybe I would try out NanoKontrol. Thanks!

2 Likes

infinite thanks for the advice @zebra, wasn’t aware of recpre_slew_time. also your supercollider gists are so helpful - the simpledelay and the onset detector both. can’t wait to play around with those.

@kingmetal the growing buffer loop would actually be a little more complicated if this is the path to go. i want to test it and some other approaches, but this approach might not work if the buffer is already playing (it is feature to arm a loop with a moving playhead, but i have no idea how the tiny loop would work in that case). feel free to experiment though, i will too!

@vinc thanks! i don’t have grid/arc so i make the most of just norns :slight_smile: in the next release i’ll add an option to use the midi mapping library i’ve been using. i like it because it allows you to easily map 6 parameters (e.g. one for each loop) with one statement and it also allows mapping one to many (e.g. one button could start/stop/arm/record all loops simultaneously). i have a nanokontrol and use it a lot with oooooo with this. i feel grid would be really good on this script too but can’t write code for grid (but will take any code someone else writes!).

3 Likes

@infinitedigits yeah I hadn’t thought of what to do if the buffer is already playing. I suspect we’d be better served trying @zebra’s SimpleDelay idea first and if it’s successful this might be a useful feature to advocate for in a future release of crone.

The input delay might at least allow capture of the whole transient and it would be interesting to experiment with.

Luckily, if sampling performance is too much for me to bite off, I have some ideas for easier features to experiment with :sunglasses:

I gotta remind myself that I also have to, you know, actually play some music this week too instead of just puzzling about input latency but oooooo has really gotten me excited about coding. I wrote some (very simple) Python at work this week for the first time in about a year, and it can’t be a coincidence.

1 Like

@zebra tried your input delay and it works wonderfully. thanks again!

I also have to, you know, actually play some music this week too

@kingmetal this is the goal afterall :slight_smile: for me, making music-making things is always means to an end (one reason why my scripts have bugs because some bugs don’t bother me musically…). i’m happy to take over here and make these fixes. i pushed a almost-fixed version here that you can try.

one obvious thing that still needs fixes is toggling the latency in recording/playback so the whole time doesn’t feel laggy from the delay. basically, i’m going to add another fix to switch engine to softcut when arming and turn monitoring off (so you don’t here delay), and then switch it back (monitoring back on, engine->softcut off, adc->softcut on) when recording is over.

there is one more weird bug that when you’ve recorded a nice loop, if you reset the loop to 0 position and press play, the first time it plays it will start in a not-quite-right place, but then it loops perfectly fine.

1 Like

Amazing! I’ll play with this over the weekend and report back. It’s funny just this morning I was thinking about how it might be cool to have some monitoring options during recording (monitor softcut audio from loop on/off for fully destructive overdubs, for example). That actually sounds like exactly what you are describing!

I very rarely overdub so I actually don’t remember if oooooo supports overdubs but would your switch mean that you won’t be able to hear previously recorded content when overdubbing?

another fix to switch engine to softcut when arming and turn monitoring off (so you don’t here delay)

i must be misunderstanding something but don’t specifically follow this. they should be separate paths:

ADC---+-->engine--->cut---+-->DAC
      |	                  |
      +----->monitor------+
       	       	       	       			    

(maybe ‘monitor’ means something else but i couldn’t spot it)

and though switching paths will work, you can also set the delay time to zero.

2 Likes

sorry for my lack of clarity @zebra. what i meant was that it seems that during recording, as you show, there are two paths for the audio: through “monitor” and through “engine->cut”. except, the engine specifically delays by a fraction of a second so in the DAC you hear the realtime sound (ADC -> monitor) as well as the delayed sound being recorded (ADC -> engine). when recording i don’t actually want to hear both - so i was thinking i would need to temporarily turn the monitor route off, until recording is over.

1 Like

ahh shoot, i forgot that engine -> softcut and engine -> dac are still not independent. sorry

2 Likes

@kingmetal okay, all set. it should not miss transients now. try the latest in the master branch. its a little funny when overdubbing though…if you overdub a beat and you think you are on beat, it will sound off while you record (because of the lagged audio) but then after recording it will be on the beat. not sure how to fix that or even if it needs fixing.

Awesome!! I’ll put it through it’s paces!

It’s an interesting problem (the monitoring delay) - I wonder if optimizing the input detection with something like Onset.kr will just eventually let us set the buffer delay to short enough where we don’t notice in the future. Either way, this is exciting progress and I suspect that you might now have the “fastest looper on Norns” :slight_smile:

I’m learning so much from following your PRs, I know I keep thanking you but I really appreciate it. Thanks!!

Pulled down the master branch on my Norns and it does indeed grab the entire transient!! THANK YOU!

It doesn’t seem like the input monitoring is working properly while recording though, I’m getting a highly delayed signal. I’m going to have a look at the code and see if I can fix it.

EDIT: Not sure what the problem is still but I think that we need to leave direct input monitoring on all the time, or else at least on my setup you can’t monitor while not in recording. Basically I think this needs to be 1 at all times: https://github.com/schollz/oooooo/blob/cb6d965b8ec849e451f6dfc5ac48566c59866ab0/oooooo.lua#L586

Do I need to mess with my Softcut parameters at all to get this to work properly?

1 Like

@kingmetal so the issue is that if you don’t turn off the monitor you will hear your signal “double”. i think the problem is something else though…it seems the delay is really high, even when set low. so its catching the transient but now i’ve realized that its too far delayed.

@zebra, do you know if the DelayC has an absolute minimum delay time? i’m trying to set delay times of <10ms, but i consistently get delay times of ~200 ms.

here’s the supercollider code:

Engine_SimpleDelay : CroneEngine {
  var synth;

  *new { arg context, doneCallback;
    ^super.new(context, doneCallback);
  }

  alloc { 
    synth = { arg delay=0.001, volume=0.0;
      var input = SoundIn.ar([0, 1]);
      DelayC.ar(input, maxDelayTime:0.002, delayTime:0.002, mul:volume)
    }.play(context.server);

    this.addCommand("delay", "f", { arg msg; synth.set(\delay, msg[1]); });
    this.addCommand("volume", "f", { arg msg; synth.set(\volume, msg[1]); });
  }
}

here’s a profile of the audio without and with engine activated, using delay time of 2ms, over 4 quarternotes. the first four quartnernotes, without engine, are spaced 0.5 s apart, at 120bpm. the second four quarternotes have a delayed note between them that is 200 ms away from the previous…even though the delay is not set to 200ms, but set to 2ms.

if 200 ms is the minimum delay, is there a better sc class to use to get smaller delays? i probably don’t need to use 2ms, but probably just in the range 10-200ms

3 Likes

Disclaimer: I’m a newcomer to the nornsverse. I’ve been super excited to try this patch, but I’m unable to get any sound from it and also unable to record anything into the loops.

I followed the instructions and have yet to have any luck. On startup I hear a single tone that fades out. After that I cannot record any new loops in. If I pause a loop (even though there’s no sound) I’m unable to get it to play again.

I apologize if this is rambling or unclear. I’m just not sure what the issue is.

1 Like

Welcome! Oooooo makes no sounds on it’s own. Do you have an external sound source plugged in? Are you able to hear your incoming sound source? What are the settings looking like in the mixer/levels page of your norns?

1 Like

I figured it out. Rec input was set to tape only. Changed it to Tape + LR. Thanks!

3 Likes

Hm… I don’t think so. You could try DelayN which has no interpolation.

delayC should require only like 2 extra samples for the interpolation window

There is also I think 1 or 2 extra buffers of latency from looping through crone. (Adc -> crone -> sc -> crone -> DAC.) But that might account for 3-6ms, not 200

Oh but @infinitedigits I see you have maxdelaytime set to 2ms! Which is not what you want. That’s the amount of memory the ugen allocates for the delay buffer, so it might be rather confused)

(Btw also see BufDelay family which is usually preferred, but I wanted to save the complication)

Now that I’m looking I don’t know if I understand the test actually. I’ll see if I can take a moment to take my own measurements…

Are you on factory norns? Did you happen to change your jack config for longer buffers / more periods, or something else?

1 Like

yeah, pretty sure it’s factory. it’s a shield norns flashed with the latest image on the latest update. I didn’t change any jack configs.

I did get similar results using a max delay time of 0.2s with a set delay time of 0.005s, still getting delays of 200ms when it was active.

I’ll try BufDelay classes and DelayN and see if that works out better

thanks, as always. I’ll dig into this some more and explore

update: i explored more. the BufDelayN works as expected - by allocating the max buffer size (100 ms) i get max delays of 100 ms, and can shorten to any delay time. though i found <100ms is too short to catch transients. so everything is working well. i also played with onset detection in supercollider. i hooked up onset detection to osc in lua and it works, but no better/worse than polling the amplitude at ~20 ms (that i can tell). i opted not to use it then, because it just costs extra cpu for no added gain.

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