diff --git a/README.md b/README.md index adedaa2..c90fbe6 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ digital tape loops x 6. ![Image](https://user-images.githubusercontent.com/6550035/91628872-c47b8c80-e978-11ea-9d07-df79ef337a0f.gif) +https://vimeo.com/590419704 + i call this script *"oooooo"* because it is composed of six loops. they are like digital tape loops - you can level, pan, speed, slow, shorten, lengthen, dub, overdub, destroy any loop at any time. i was inspired to make this after seeing tape loops circulating (pun sorta intended) all over the place. i like the idea of having multiple independent different loops, with different sizes, played on a different tape players with different eccentricities. i don't have any cassette tapes but i have norns so i wrote this script to try to make digital tape loops. @@ -50,7 +52,7 @@ all parameters are available via the global menu. - in tape mode, press K1+K3 to prime recording. when primed, recording will automatically begin when incoming audio rises above a threshold. the recording threshold can be set by global parameter "`recording -> rec thresh`". - in tape mode, you can force recording by hitting K1+K3 a second time. - recording stops after traversing the whole loop. you can stop it earlier with K2 or K3 (in tape mode) and that will shrink the loop to that point. you can set recording to continue to the next loop by setting the global parameter "`recording -> rec thru loops`" to `yes`. -- by default, volume in "pinched" when starting/stopping recording to avoid pops from discontinuous signals. you can lower/raise the pinching by adjusting the global parameter "`vol pinch`". +- by default, loops will be recorded with a crossfade for gapless playback. if you are recording something with initial transients (like drums), I recommend reducing the crossfade (`loop X -> crossfade`) to 1 ms. - to record a loop over and over, infinitely, change `recording -> stop rec after` to its max value. **quick menu:** @@ -62,6 +64,7 @@ all parameters are available via the global menu. **A loop:** +- the "A" loop is found by turning E1 all the way to the right. - "A" loop can control all loops. the tape mode works as before, but affects all loops. - the quick menu differs from loops but is also activated by K2 or K3, and modulated with E3 @@ -86,11 +89,6 @@ all parameters are available via the global menu.

Demo of playing

-## my other norns - -- [barcode](https://github.com/schollz/barcode): replays a buffer six times, at different levels & pans & rates & positions, modulated by lfos on every parameter. -- [blndr](https://github.com/schollz/blndr): a quantized delay with time morphing -- [clcks](https://github.com/schollz/clcks): a tempo-locked repeater for monome norns ## license diff --git a/lib/Engine_SimpleDelay.sc b/lib/Engine_SimpleDelay.sc index 8245b92..2b52dcc 100644 --- a/lib/Engine_SimpleDelay.sc +++ b/lib/Engine_SimpleDelay.sc @@ -1,5 +1,5 @@ Engine_SimpleDelay : CroneEngine { - //var =4) then - -- print("incoming signal = "..val) - if val>params:get("rec thresh")/10000 then - tape_rec(i) - end - end - end - end - p_amp_in:start() - - -- and initiate recording on incoming on audio input 2 - p_amp_in2=poll.set("amp_in_r") - -- set period low when primed, default 1 second - p_amp_in2.time=1 - p_amp_in2.callback=function(val) - for i=1,6 do - if uS.recording[i]==1 and (params:get("input type")==2 or params:get("input type")>=4) then - -- print("incoming signal = "..val) - if val>params:get("rec thresh")/10000 then - tape_rec(i) - end - end - end - end - p_amp_in2:start() + -- -- and initiate recording on incoming audio on input 1 + -- p_amp_in=poll.set("amp_in_l") + -- -- set period low when primed, default 1 second + -- p_amp_in.time=1 + -- p_amp_in.callback=function(val) + -- for i=1,6 do + -- if uS.recording[i]==1 and (params:get("input type")==1 or params:get("input type")>=4) then + -- -- print("incoming signal = "..val) + -- if val>params:get("rec thresh")/10000 then + -- tape_rec(i) + -- end + -- end + -- end + -- end + -- p_amp_in:start() + + -- -- and initiate recording on incoming on audio input 2 + -- p_amp_in2=poll.set("amp_in_r") + -- -- set period low when primed, default 1 second + -- p_amp_in2.time=1 + -- p_amp_in2.callback=function(val) + -- for i=1,6 do + -- if uS.recording[i]==1 and (params:get("input type")==2 or params:get("input type")>=4) then + -- -- print("incoming signal = "..val) + -- if val>params:get("rec thresh")/10000 then + -- tape_rec(i) + -- end + -- end + -- end + -- end + -- p_amp_in2:start() for i=1,6 do params:set_action(i.."vol",function(x) uP[i].volUpdate=true end) @@ -478,6 +477,10 @@ function init() -- simply setup oooooo grid oooooo_grid = grido:new() end + + -- bang some special parameters + params:delta("rec thresh",1) + params:delta("rec thresh",-1) -- DEV comment this out -- params:set("scale_mode",9) -- params:set("choose mode",3) @@ -586,7 +589,8 @@ function init_loops(j,ignore_pan) -- softcut.fade_time(i,params:get("vol pinch")/1000) softcut.level_slew_time(i,params:get("slew rate")) softcut.rate_slew_time(i,params:get("slew rate")) - softcut.recpre_slew_time(i,params:get("vol pinch")/1000) + softcut.recpre_slew_time(i,params:get(i.."crossfade")/1000) + softcut.fade_time(i,params:get(i.."crossfade")/1000) softcut.rec_level(i,params:get("rec level")) softcut.pre_level(i,params:get("pre level")) @@ -667,7 +671,6 @@ function activate_mode_default() ["pre level"]=1, ["rec level"]=1, ["rec thresh"]=85, - ["catch transients w lag"]=1, ["rec thru loops"]=1, ["stop rec after"]=1, ["input type"]=4, @@ -785,14 +788,14 @@ function update_softcut_input_lag(on) uS.lagActivated=on if on then print("update_softcut_input_lag: activated") - engine.volume(1.0) + -- engine.volume(1.0) -- add lag to recording using a simple delay engine audio.level_monitor(0) -- turn off monitor to keep from hearing doubled audio audio.level_eng_cut(1) audio.level_adc_cut(0) else print("update_softcut_input_lag: deactivated") - engine.volume(0.0) + -- engine.volume(0.0) audio.level_monitor(1) -- turn on monitor audio.level_eng_cut(0) audio.level_adc_cut(1) @@ -843,10 +846,10 @@ function update_timer() else uS.recordingTime[i]=0 end - elseif params:get("vol pinch") > 0 and uS.recordingTime[i]>=uP[i].loopLength/2 and previousRecordingTime 0 and uS.recordingTime[i]>=uP[i].loopLength/2 and previousRecordingTime=4) then - p_amp_in.time=1 - elseif uS.recording[i]==1 and (params:get("input type")==2 or params:get("input type")>=4) then - p_amp_in2.time=1 - end - update_softcut_input_lag(false) + -- if uS.recording[i]==1 and (params:get("input type")==1 or params:get("input type")>=4) then + -- p_amp_in.time=1 + -- elseif uS.recording[i]==1 and (params:get("input type")==2 or params:get("input type")>=4) then + -- p_amp_in2.time=1 + -- end still_armed=(uS.recording[i]==1) uS.recording[i]=0 uS.recordingLoopNum[i]=0 @@ -1136,11 +1138,12 @@ function tape_stop_rec(i,change_loop) end uS.recordingTime[i]=0 -- slowly stop - softcut.rec_level(i,0) - softcut.pre_level(i,1) clock.run(function() -- allow pre level to go down - clock.sleep(params:get("vol pinch")/1000) + clock.sleep(params:get(i.."crossfade")/1000*2) + softcut.rec_level(i,0.0) + softcut.pre_level(i,1) + clock.sleep(params:get(i.."crossfade")/1000) softcut.rec(i,0) -- DEBUGGING PURPOSES -- loop_save_wav(i,"/tmp/save1.wav") @@ -1272,17 +1275,8 @@ function tape_arm_rec(i) print("tape_arm_rec "..i) -- arm recording uS.recording[i]=1 - if params:get("catch transients w lag")==2 then - update_softcut_input_lag(true) - end uS.recordingLoopNum[i]=0 uS.timeSinceArming=clock.get_beats()*clock.get_beat_sec() - -- monitor input - if uS.recording[i]==1 and (params:get("input type")==1 or params:get("input type")>=4) then - p_amp_in.time=uC.pampfast - elseif uS.recording[i]==1 and (params:get("input type")==2 or params:get("input type")>=4) then - p_amp_in2.time=uC.pampfast - end end function tape_rec(i) @@ -1293,24 +1287,24 @@ function tape_rec(i) do return end end print("tape_rec "..i) + -- if uS.recording[i]==1 and (params:get("input type")==1 or params:get("input type")>=4) then + -- p_amp_in.time=1 + -- elseif uS.recording[i]==1 and (params:get("input type")==2 or params:get("input type")>=4) then + -- p_amp_in2.time=1 + -- end + uS.recording[i]=2 -- recording is live + params:set(i.."isempty",1) + -- start recording + softcut.rec_level(i,params:get("rec level")) + softcut.pre_level(i,params:get("pre level")) + softcut.rec(i,1) if uP[i].isStopped then softcut.play(i,1) softcut.rate(i,uP[i].rate) uP[i].volUpdate=true uP[i].isStopped=false end - if uS.recording[i]==1 and (params:get("input type")==1 or params:get("input type")>=4) then - p_amp_in.time=1 - elseif uS.recording[i]==1 and (params:get("input type")==2 or params:get("input type")>=4) then - p_amp_in2.time=1 - end uS.recordingTime[i]=0 - uS.recording[i]=2 -- recording is live - params:set(i.."isempty",1) - -- start recording - softcut.rec_level(i,params:get("rec level")) - softcut.pre_level(i,params:get("pre level")) - softcut.rec(i,1) redraw() end @@ -1955,19 +1949,14 @@ end -- -- -- -- osc -- -- --- function osc_in(path, args, from) --- if path == "onset" then --- cur_onset = args[2] --- print("onset "..cur_onset) --- if (cur_onset-uS.lastOnset) < 0.5 then --- do return end --- end --- print("onset detected") --- uS.lastOnset = cur_onset --- for i=1,6 do --- if uS.recording[i]==1 and (params:get("input type")==1 or params:get("input type")==4) then --- tape_rec(i) --- end --- end --- end --- end +function osc_in(path, args, from) + print(path) + if path == "onset" then + for i=1,6 do + if uS.recording[i]==1 and (params:get("input type")==1 or params:get("input type")>=4) then + -- print("incoming signal = "..val) + tape_rec(i) + end + end + end +end