So,

  • Pitch is tied to playback rate in softcut. I’m into it
  • wanted to ask what the code might look like to create a pitch corrected rate adjustment, presumably using a granular scrubbing technique. Haven’t quite wrapped my head around metro functionality yet but assume this would be a place to start, although interested to see different takes if there are some. Time stretch has all sorts of flavors.
  • any performance issues to look out for?

Thanks—hope this can be educational!

1 Like

with one voice, it’s not gonna be granular exactly, there’s no overlap, but you can make a scrubby guy, which moves along at an arbitrary speed uncorrelated with the read/write speed

something like

--- load buffer etc..
sc = softcut -- type less
voice = 1
pos = 1 -- start position
r = 1/4 -- scrub rate
dt = 1/10 -- grain interval
inc = r * dt
grain_func = function()
   sc.position(voice, pos)
   pos = pos + inc
end

m = metro.init(grain_func)
m.time = dt
m:start()   

more things:

  • randomize grain rate / pitch / pos increment
  • use more voices with round-robin assignment

maybe be useful to know that you can set loop to 0 for a voice, and move position, loop_start, loop_end together to make a moving one-shot playback interval

9 Likes

found this thread searching for a way to “scrub” through a section of a softcut buffer.

Ideally I’d like to assign an encoder to scrub through the buffer - at whatever rate the encoder is turned, only when the encoder is turning. How would I stop the playhead in this case? (you can’t detect if enc() is zero I think)

Seems like this would not be a metro/clock solution tho (unless there’s a metro/clock running that turns off the playhead every x ticks?)

Suggestions?

2 Likes
pos = 0
size = 0.01

function enc(n,d) 
    if n=1 then 
       pos = pos + n 
       softcut.loop_start(1, pos)
       softcut.loop_end(1, pos + size)
    end 
end

?

wasn’t sure if you meant granular style or handcrank cassette tape style

1 Like

Thx!

I think more this.

ah - a more interesting challenge. maybe the solution would be more like:

  • function enc(n, d) x = x + d end
  • calculate the rate of change v for x over time interval t
  • set softcut rate to v
  • there will be a lag == t ?

I don’t think that would be too much code but my brain is literally too sleepy to think of it right now

(EDITS)

2 Likes

You tried Reels? It does exactly this.

Not sure it does - I want to manually advance the playhead

(but gonna go dig in the reels code now…)

2 Likes

Are you looking for constant pitch or proportional to rotation?

proportional to rotation, but that part seems easier to manipulate with rate(voice,rate)

1 Like

Ah, yes, you’re right, you actually control the speed, not the position, which makes it sound similar, but in practice, is not. Sorry for the red herring.

1 Like

I do believe Manglr does this, though. I’m using it with my arc now, it even has a feature called Scrub, where you simply slide across the sample back and forth with the first arc wheel. Not sure that experience translates without an arc, tho.

1 Like

seems like different things:

@circuitghost , angl / mangl are using supercollider GrainBuf under the hood in a straightforward way.

upthread, i pointed out that softcut isn’t designed for “granular” processing in the sense of large polyphony, but for 2-voice polyphony it is simple: use a short loop_start / loop_end, set loop flag to zero for one-shot mode, send pos(voice, x) repeatedly, advancing x. you could get more polyphony by using multiple voices in round robin. but the API surely doesn’t make this very convenient.

@okyeron i think you mean an something like the scrubbing playhead in Reaper or other DAWs, which is like a jog wheel for a virtual audio tape. there exists the concept of an “edit cursor.”

i would just use the encoder to select the rate, with rate smoothing. (probably using a JI table.)

and if you wish, use the phase poll with a small quantum, to update the “edit cursor” position from the actual softcut voice phase.

(alt mode: set the cursor position independently, so by default it restarts from same pos each time; this is also a common DAW option.)

when rate specified by the rate encoder goes from zero to nonzero[^], send pos(voice, cursor).

(alt mode: require explicit keypress or something to restart.)

another thing that the API doesn’t really provide is the ability to just say, “stop playback now (after fadeout.)” which could be added.


[^] here is where you might want some timing logic; e.g. only restart if timeout has elapsed between encoder movements. metro seems fine for this…

3 Likes

Aha! I didn’t think of using rate = 0 to stop the playback. Excellent. Thx!

Yes. This is what I was thinking of.

Sounds handy. Would that be different than just setting play to zero?

Would an optional parameter to play for a specific amount of time be useful? Or perhaps this could be accomplished with loop points if you could specify the number of loops (like just one)? oops… re-read the above and loop(voice, 0) does the one-shot mode I was thinking of. doh!

(still trying to wrap my head around all the options here - deep stuff!)