Closed andr-ew closed 2 years ago
Have you done any benchmarking? I'm concerned about CPU capabilities.
On Sat, Jan 15, 2022, 1:43 PM andr-ew @.***> wrote:
not something off the issues list, so apologies if these changes are unwanted, but I’ve been enjoying this feature & I wanted to see if it made sense for upstream norns softcut : )
it’s a command for setting recording/playback interpolation. it also disables resampling entirely on the lowest setting (like max/msp poke~ & friends). right now it works like this:
softcut.interpolation(0) -- no interpolation, no resampling
softcut.interpolation(1) -- no interpolation
softcut.interpolation(2) -- linear interpolation
softcut.interpolation(4) -- cubic interpolation
I tried keeping the interface as simple as possible to start off, but I could see a couple of changes to make it more intuitive / flexible
- text flags instead of integers (I copied the GrainBuf interpolation arg for the current format)
- since it’s one command controlling write interpolation, read interpolation, and resampling on/off I could see splitting it up into 2 or 3 separate commands.
I’ve more or less just translated some of the preprocessor directives for this into runtime flags, so apologies if there’s some obvious optimizations I’m missing. the main changes are in the Resampler & Subhead classes, where I added a few alternate pathways based on the 4 states of the command. testing
use this norns PR for this softcut-lib PR: [TODO]
build procedure (copied this from zack's last PR, hopefully I got it right):
get everything
cd ~/norns
git remote set-url origin @.***:andr-ew/norns.git
git pull
git checkout sc-interpolation-modes
cd ~/norns/crone/softcut/softcut-lib
git remote set-url origin @.***:andr-ew/softcut-lib.git
git pull
git checkout sc-interpolation-modes
compile everything
cd ~/norns/crone/softcut/softcut-lib && ./waf && cd ~/norns && ./waf
restart norns
sudo systemctl restart norns-jack.service; sudo systemctl restart norns-matron.service; sudo systemctl restart norns-crone.service
test script (requires some audio input):
-- softcut interpolation options test
--
-- E1: pre_level
-- E2: rate (course)
-- E3: rate (fine)
-- K1: clear
-- K2: record into half second loop
-- K3: interpolation mode
tab = require 'tabutil'
interps = {
0, 1, 2, 4,
}
interp_names = {
'none, noresamp', 'none', 'linear', 'cubic',
}
interp = 4
rate = 1
pre = 0
rec = false
dur = 0.5
function update_interp()
softcut.interpolation(1, interps[interp])
end
function set_rec(v)
rec = v softcut.rec_level(1, v and 1 or 0) softcut.pre_level(1, v and pre or 1)
end
function set_rate(v)
rate = v softcut.rate(1, v) -- softcut.loop_end(1, util.clamp(0, dur, rate * dur))
end
function init()
audio.level_cut(1.0) audio.level_adc_cut(1) softcut.level_input_cut(1, 1, 1) softcut.level_input_cut(2, 1, 1) softcut.buffer_clear() softcut.enable(1, 1) softcut.rec(1, 1) softcut.play(1, 1) softcut.recpre_slew_time(1, 0.1) softcut.fade_time(1, 0.1) softcut.buffer(1, 1) softcut.level(1, 1) softcut.rate(1, rate) softcut.loop(1, 1) softcut.loop_start(1, 0) softcut.loop_end(1, dur) softcut.position(1, 0) softcut.rate_slew_time(1, 0.1) update_interp() set_rec(false) set_rate(rate) redraw()
end
function key(n, z)
if z==1 then if n==1 then softcut.buffer_clear() elseif n==2 then set_rec(not rec) elseif n==3 then interp = interp % #interp_names + 1 update_interp() end redraw() end
end
function enc(n, d)
if n==1 then pre = util.clamp(pre + (d*0.01), 0, 1) set_rec(rec) elseif n==2 then if d > 0 then set_rate(rate * 2) elseif d < 0 then set_rate(rate / 2) end elseif n==3 then set_rate(rate + d*0.01) end redraw()
end
function redraw()
screen.clear() screen.move(2,64 * 1/4) screen.text("pre: " ..pre) screen.move(2,64 * 1/2) screen.text("rate: " .. rate) screen.move(2, 64 * 3/4) screen.text("rec: " .. (rec and "on" or "off")) screen.move(128 * 1/3, 64 * 3/4) screen.text("interp: " .. interp_names[interp]) screen.update()
end
(also, I can’t get the command to show up in the api docs when I build it, not sure what I did wrong)
You can view, comment on, or merge this pull request online at:
https://github.com/monome/softcut-lib/pull/61 Commit Summary
- 7c0bb98 https://github.com/monome/softcut-lib/pull/61/commits/7c0bb98965541c32d707aca7ce6c20306001de75 test RESAMPLER_INTERPOLATE_LINEAR directive
- c2312e9 https://github.com/monome/softcut-lib/pull/61/commits/c2312e96bb36f7b454a389b3dbc6040e75218767 back to normal
- ae12f51 https://github.com/monome/softcut-lib/pull/61/commits/ae12f516e6b549e0b6c1c2ef5b69781f02f417c5 command setters, input interpolation switch (before I attempt to compile)
- 0f8830a https://github.com/monome/softcut-lib/pull/61/commits/0f8830ac52127a987d1c15b17295fd2448b83856 fix build errors (typos)
- 0570e17 https://github.com/monome/softcut-lib/pull/61/commits/0570e17289f29b490dfd60c797a9f97a9ec0f697 fix NoResampling buffer index
- a3b8d9c https://github.com/monome/softcut-lib/pull/61/commits/a3b8d9c2324a99b3dd37d5ede566f5881ec42b37 playback inerpolation modes
File Changes
(9 files https://github.com/monome/softcut-lib/pull/61/files)
- M clients/softcut_jack_osc/src/OscInterface.cpp https://github.com/monome/softcut-lib/pull/61/files#diff-33138df15bd73c4cb73292ab5a20426f8e82041a45030daadd00c2e070ff217a (5)
- M softcut-lib/include/softcut/ReadWriteHead.h https://github.com/monome/softcut-lib/pull/61/files#diff-a40cf152414a79a1b125d8487274ae94dac7847ce5ac5212243c02f566eb8047 (3)
- M softcut-lib/include/softcut/Resampler.h https://github.com/monome/softcut-lib/pull/61/files#diff-944fa284bd36c669470d052234340d743dfa52a0e4a886e2a436f709954f16a6 (41)
- M softcut-lib/include/softcut/Softcut.h https://github.com/monome/softcut-lib/pull/61/files#diff-1cb87826baa28427be352037ff9cbab622871f9ff82bf7ab59e3324d20a982d5 (4)
- M softcut-lib/include/softcut/SubHead.h https://github.com/monome/softcut-lib/pull/61/files#diff-f7b9062457af2795986e6d2c71732ebc6f48bc7bc2faa14d77e9b2b7d0904bb3 (16)
- M softcut-lib/include/softcut/Voice.h https://github.com/monome/softcut-lib/pull/61/files#diff-d1a449a571a709534b94f90938d9049c0bcba07aa4ae37389253afdc952a434e (2)
- M softcut-lib/src/ReadWriteHead.cpp https://github.com/monome/softcut-lib/pull/61/files#diff-9dca2dc84cd80c69f525f7b2221726bd658d5529cda9ede9efcbd1df357306c1 (5)
- M softcut-lib/src/SubHead.cpp https://github.com/monome/softcut-lib/pull/61/files#diff-38dd49cc3d59b1308e71cd6ee48a4f378d5c4cd29dd42eff954dd3143b30e262 (58)
- M softcut-lib/src/Voice.cpp https://github.com/monome/softcut-lib/pull/61/files#diff-3cffe2c57b805ecd24f05031f1ae5af1fbc7b59b0583130f8f4e405036953347 (4)
Patch Links:
- https://github.com/monome/softcut-lib/pull/61.patch
- https://github.com/monome/softcut-lib/pull/61.diff
— Reply to this email directly, view it on GitHub https://github.com/monome/softcut-lib/pull/61, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAB4I4CNW2ZMTB2BF2ENEA3UWG53VANCNFSM5MBJCJCA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.
You are receiving this because you are subscribed to this thread.Message ID: @.***>
Have you done any benchmarking? I'm concerned about CPU capabilities.
no, I'd love some pointers on how do do that if you have any. I can work on an A/B comparison with the main branch.
no, I'd love some pointers on how do do that if you have any. I can work on an A/B comparison with the main branch.
I did some A/B testing by maxing out all six voices to be recording+playing at a fast rate (8x) and then monitoring crone CPU usage using top
. here's a script that I use: https://github.com/schollz/gatherum/blob/master/softcut-recmax.lua
thanks @schollz !
after adding an optimization to writes (details), I ran this test on main
& sc-interpolation-modes
. stock norns, all 6 voices recording + playing back from tape at rates 1 & 8, cubic interpolation on both branches.
here are my results after the latest commits: | branch | rate | %CPU | %MEM |
---|---|---|---|---|
main | 1 | 51.7 | 23.7 | |
main | 8 | 71.3 | 23.7 | |
sc-interpolation-modes | 1 | 52.3 | 23.7 | |
sc-interpolation-modes | 8 | 70.5 | 23.7 |
(not sure why my branch is getting a slight edge on the high rates only, but I did recompile & run the test a couple times just to be sure, results were very consistent. the gist comments have a few more details if you want em)
@catfact - to clarify (when I should have earlier) - a command to bypass resampling & setting read/write interpolation are both features I'd like to add to softcut for a norns script I'm working on. combining them into one command was an arbitrary choice on my part and I think it makes more sense to split them up. I get that disabling resampling is going to be a niche/weird feature so if you don't want the overhead of maintaining that feature upstream I can use my own fork of softcut instead. but I'm happy to spend more time on this PR to minimize the impact of additional features.
I'll research alternatives to per-sample switches and see if I can find the technique you're referring to, thanks for bearing with me & for taking the time to review.
I'm saying you can move the switch out of the per sample update func (and : please do if you want this to be merged)
And c++ lang provides ways to do this with less code than you have
Basically I think you can implement this change without nontrivial changes to SubHead
, and without adding instructions to the per sample update functions. I'm not trying to cold golf or prematurely optimize, just giving same advice I would have received 15 years ago.
...ok I think I just misunderstood your first comment.
main culprit == over-reliance on switch/case in the sample loop ?
yes, if you look carefully you will notice that some pains have been taken to avoid switch/branch in sample loop (by factoring into different sample update funcs.)
in your change, the resampling / interpolation mode never changes from one sample to another, only from one block to another. so structure the change in such a way that it is applied at the top of the block and never tested again during the block. i don't much care how you do this, there are several ways, but the way i would do it is to add a template parameter to Resampler
which takes a mode value, specialize the interpolate
function based on mode value, and switch between three different specialized Resampler
objects.
the "no resampling" mode adds an extra wrinkle. to be honest, i am not stoked on this mode. it looks like you just write every Nth sample and leave intermediate samples untouched. that is of course a very glitchy effect. i am not convinced it needs to be in the main branch of this library.
ok cool, thank you for sharing more details. that sounds like enough of a direction for me to start finding a solution.
re: the "no resampling" thing it's of course up to you. for me it brings back some of what I like about trying to build buffer-based stuff in max/puredata/SC/etc (where there were less options to do things the "right" way so you end up doing glitchy stuff with a phasor heading into a write head) but mixing in everything that softcut can do. if you don't think it fits in the main branch I can just drop it from this PR and work toward including my own fork of softcut with my script instead.
@andr-ew i'm sorry i overreacted there. rough day, rough week, rough year i guess, but no excuse.
i don't want to merge this yet. i appreciate the effort and i think we should use parts of this and most/all of your other PR on norns. but particularly i question whether the "no resampling" mode is a good thing to add. (really let's call it what it is, which is "no interpolation" as opposed to zero-order, 1st order, or 3rd order interpolation.) i don't mind adding the effect, but the architecture doesn't efficiently support swapping peek/poke functions so i would want a substantial refactor around that.
and truly, the idea that you will attempt to bundle a forked softcut if i don't merge that part, is off-putting; if you are going to make that attempt on this basis, then i won't spend time correcting and merging your other changes here.
re: profiling, here is a comparison collecting many batched samples of top
, and also collecting samples of jack_cpu_load
. you can see that the former is basically noise within 5%.
this is N=100, which is honestly still not really enough (see those outliers,) but it is enough to illustrate the small (1%) but definite (see first quartile range) trend towards higher JACK CPU loads with the proposed change. this is the effect of adding more per-sample logic (even a small amount.)
not something off the issues list, so apologies if these changes are unwanted, but I’ve been enjoying this feature & I wanted to see if it made sense for upstream norns softcut : )
it’s a command for setting recording+playback interpolation. it also disables resampling entirely on the lowest setting (like max/msp poke~ & friends). right now it works like this:
I tried keeping the interface as simple as possible to start off, but I could see a couple of changes to make it more intuitive / flexible
GrainBuf
interpolation arg for the current format)I’ve more or less just translated some of the preprocessor directives for this into runtime flags, so apologies if there’s some obvious or needed optimizations I’m missing. the main changes are in the
Resampler
&Subhead
classes, where I added a few alternate pathways based on the 4 states of the command.testing
use this
norns
PR for thissoftcut-lib
PR: https://github.com/monome/norns/pull/1503build procedure (copied this from zack's last PR, hopefully I got it right):
test script (requires some audio input):
(also, I can’t get the command to show up in the api docs when I build it, not sure what I did wrong)