Norns: softcut studies

softcut studies

softcut is a multi-voice sample playback and recording system build into the norns environment. it features level-smoothing, interpolated overdubbing, and matrix mixing. it was written by @zebra.

for an introduction to scripting see the norns studies and tutorial.

1. basic playback

  • see/run softcut-studies/1-basic (source)

first, some nomenclature:

  • voice — a play/record head. mono. each has its own parameters (ie rate, level, etc). there are 6 voices.
  • buffer — digital tape, there are 2 buffers. mono. just about 5 minutes each.

softcut parameters are reset when a script is loaded. to get a looping sound we need at a minimum the following, where the arguments are (voice, value):

softcut.enable(1,1)
softcut.buffer(1,1)
softcut.level(1,1.0)
softcut.loop(1,1)
softcut.loop_start(1,1)
softcut.loop_end(1,2)
softcut.position(1,1)
softcut.play(1,1)

the buffers are blank. load a file (wav/aif/etc):

softcut.buffer_read_mono(file, start_src, start_dst, dur, ch_src, ch_dst)
  • file — the filename, full path required ie "/home/we/dust/audio/spells.wav"
  • start_src — start of file to read (seconds)
  • start_dst — start of buffer to write into (seconds)
  • dur — how much to read/write (seconds, use -1 for entire file)
  • ch_src — which channel in file to read
  • ch_dst — which buffer to write

2. multivoice and more parameters

  • see/run softcut-studies/2-multi (source)

enable more voices, then set their parameters using the first argument in the various param functions. here are a few more playback parameters:

softcut.pan(voice,position)
softcut.level_slew_time(voice,time)
softcut.rate_slew_time(voice,time)

3. cut and poll

  • see/run softcut-studies/3-cut (source)

softcut cross-fades nicely when cutting to a new position and looping. specify the fade time:

softcut.fade_time(voice,time)

we can read the playback position of a voice by setting up a poll.

function update_positions(voice,position)
  print(voice,position)
end

and then inside init():

softcut.phase_quant(voice,time)
softcut.event_phase(update_positions)
softcut.poll_start_phase()

phase_quant specifies the time quantum of reporting. for example, if voice 1 is set to 0.5, softcut will call the update_positions function when the playback crosses a multiple of 0.5.

4. record and overdub

5. routing

6. files


reference

contributions welcome: github/monome/softcut-studies

33 Likes

Perfect. Thank you so much for this,
I will dive into softcut!

4 Likes

added #2 and #3.

these little sample scripts sound nice— #3 is sortof a mlr-ish auto-cutter. use them as a basis to make little things:

  • change the soundfile
  • change rates, panning, etc
6 Likes

I get how to play a sample and jump across various position, what I am most interested in, now that I am properly teased, is:

  • How to change the pitch of the sample.
  • How to use the filters, especially mod.
  • Start a new recording of what comes through audio L/R.
  • How to replay that sample.

Where I want to go

What is a good way to make let say a sampler that can hold more than one sample at a time, a large number of samples, think Ableton Drum Rack. Let say I have a bank of 64 samples. Right now the buffer seems to always overwrite the previous sample.

change pitch with rate

the buffer is super super long, so use cut positions as storage points for clips. think about it like tape or RAM

recording is up next!

4 Likes

Oh that is clever!! Okay yes I think I know just how to make myself the perfect sampler.

5 Likes

in upcoming version (current master branch), there are now two controllable filter stages: one before the write head, one after the playback head.

both have identical topologies: 2nd-order state variable. each filtering stage puts out an arbitrary blend of lowpass, highpass, bandpass, band-reject and dry signal.

the “mod” parameter is a little weird and deserves a special explanation. it applies only to the pre-filter. the preFcMod parameter (normally in [0, 1]) allows the cutoff frequency to be affected by the rate parameter. this “modulation” can only decrease the filter FC, not increase it.

psuedo-code:

fcModValue = min(fcBase, fcBase * abs(rate))
fcValue = (fcModValue * fcModAmount) + (fcBase * (1 - fcModAmount))

there is a specific reason to have this: if you set the rate below 1.0 when writing to the buffer, you are effectively decreasing the sample rate of the material in the buffer.

at the extreme, when (say) sweeping the rate from positive to negative, rate ~=0 means effectively
sr ~=0, and any values written there will cause a nasty click when played back at higher speeds.

so, if you set up the pre-filter as follows:

  • dry mix = 0
  • lowpass mode = 1
  • all other modes = 0
  • base frequency at, say, 12k or 16k or something (well below nyquist, to accommodate shallow rolloff of 2nd-order SVF)
  • rq = 1
  • fcMod = 1

then the pre-filter acts as a fairly effective antialiasing device when changing the rate.

this isn’t “needed” in the post-filter.


also i should note that there’s no smoothing of filter parameters at the moment. this could be added… problem is the coefficient update method is rather heavy. so will take some work, like (a) profiling individual ops in coeff update on ARM (tan function) and making approximations / refactors as appropriate, and/or (b) downsampling the filter parameter signals (except when modulated, so… kinda tricky)… or maybe weirder things like smoothing the coefficients directly after calculation… i intend to try some of this stuff on the postfilter (no mod) and profile.

4 Likes

with this new version, does halfsecond.lua need an update from filter_dry to pre_filter_dry or post_filter_dry (along with other filter_ variables?)

Yes indeed. But I suppose we could add aliases in lua