From 503160d0fd1fb6f0b566e331fe50574c443fd96b Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Fri, 12 Feb 2021 16:46:25 -0800 Subject: [PATCH 01/10] add grid????????????????????????? --- lib/grido.lua | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++ oooooo.lua | 4 +- 2 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 lib/grido.lua diff --git a/lib/grido.lua b/lib/grido.lua new file mode 100644 index 0000000..545de71 --- /dev/null +++ b/lib/grido.lua @@ -0,0 +1,151 @@ +-- grido + +local Grido={} + +function Grido:new(args) + local m=setmetatable({},{__index=Grido}) + local args=args==nil and {} or args + + -- setup visual + m.visual={} + for i=1,8 do + m.visual[i]={} + for j=1,16 do + m.visual[i][j]=0 + end + end + + -- debouncing and blinking + m.blink_count=0 + m.blinky={} + for i=1,16 do + m.blinky[i]=1 -- 1 = fast, 16 = slow + end + + -- grid uses pre-defined max loop length + m.loopMax=(60/clock.get_tempo())*params:get("start length") + + m.pressed_buttons={} + -- initiate the grid + -- grid specific + m.g=grid.connect() + m.g.key=function(x,y,z) + m:grid_key(x,y,z) + end + print("grid columns: "..m.g.cols) + + -- grid refreshing + m.grid_refresh=metro.init() + m.grid_refresh.time=0.05 + m.grid_refresh.event=function() + m:grid_redraw() + end + m.grid_refresh:start() + + return m +end + +function Grido:grid_key(x,y,z) + self:key_press(y,x,z==1) + self:grid_redraw() +end + +function Grido:key_press(row,col,on) + if on then + self.pressed_buttons[row..","..col]=true + else + self.pressed_buttons[row..","..col]=nil + end + + if row <= 6 then + if on then + self:change_loop(row) + end + end +end + +function Grido:change_loop(row) + local loopStart = 0 + local loopEnder = 0 + for i=1,16 do + if self.pressed_buttons[row..","..i]==true then + if loopStart == 0 then + loopStart = i + else + loopEnder = i + end + end + end + + + loopStart = util.linlin(1,16,0,self.loopMax,loopStart) + params:set(row.."start",loopStart) + if loopEnder > 0 then + loopEnder = util.linlin(1,16,0,self.loopMax,loopEnder) + params:set(row.."length",loopEnder-loopStart) + else + params:delta(row.."reset trig",1) + end +end + +function Grido:get_visual() + --- update the blinky thing + self.blink_count=self.blink_count+1 + if self.blink_count>1000 then + self.blink_count=0 + end + for i,_ in ipairs(self.blinky) do + if i==1 then + self.blinky[i]=1-self.blinky[i] + else + if self.blink_count%i==0 then + self.blinky[i]=0 + else + self.blinky[i]=1 + end + end + end + + -- clear visual + for row=1,8 do + for col=1,16 do + self.visual[row][col]=0 + end + end + + -- get the current state for the loops + for i=1,6 do + local row=i + -- get current position + local colStart=util.linlin(uC.bufferMinMax[i][2],uC.bufferMinMax[i][2]+self.loopMax,1,16,uP[i].loopStart+uC.bufferMinMax[i][2]) + local colEnder=util.linlin(uC.bufferMinMax[i][2],uC.bufferMinMax[i][2]+self.loopMax,1,16,uP[i].loopStart+uP[i].loopLength+uC.bufferMinMax[i][2]) + local colPoser=util.linlin(uC.bufferMinMax[i][2],uC.bufferMinMax[i][2]+self.loopMax,1,16,uP[i].position+uC.bufferMinMax[i][2]) + for col=colStart,colEnder do + self.visual[row][col]=5 + end + self.visual[row][colPoser]=15 + end + + -- illuminate currently pressed button + for k,_ in pairs(self.pressed_buttons) do + row,col=k:match("(%d+),(%d+)") + self.visual[tonumber(row)][tonumber(col)]=15 + end +end + +function Grido:grid_redraw() + self.g:all(0) + local gd=self:get_visual() + rows=#gd + cols=#gd[1] + for row=1,rows do + for col=1,cols do + if gd[row][col]~=0 then + self.g:led(col,row,gd[row][col]) + end + end + end + self.g:refresh() +end + +return Grid diff --git a/oooooo.lua b/oooooo.lua index d4cc76d..7c7583a 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -24,7 +24,7 @@ -- E3 adjusts parameter local Formatters=require 'formatters' - +local grido=include("oooooo/lib/grido") engine.name="SimpleDelay" @@ -428,6 +428,8 @@ function init() update_softcut_input_lag(false) + grido:new() + -- DEV comment this out -- params:set("choose mode",3) -- activate_mode() From a6e3223eceee094a173349ad8c88ac385693aae2 Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sat, 13 Feb 2021 09:50:45 +0700 Subject: [PATCH 02/10] working? --- lib/grido.lua | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/lib/grido.lua b/lib/grido.lua index 545de71..aeac293 100644 --- a/lib/grido.lua +++ b/lib/grido.lua @@ -77,15 +77,11 @@ function Grido:change_loop(row) end end - - loopStart = util.linlin(1,16,0,self.loopMax,loopStart) - params:set(row.."start",loopStart) if loopEnder > 0 then - loopEnder = util.linlin(1,16,0,self.loopMax,loopEnder) - params:set(row.."length",loopEnder-loopStart) - else - params:delta(row.."reset trig",1) + params:set(row.."start",(loopStart-1)/16*self.loopMax) + params:set(row.."length",(loopEnder-loopStart+1)/16*self.loopMax) end + softcut.position(row,(loopStart-1)/16*self.loopMax+uC.bufferMinMax[row][2]) end function Grido:get_visual() @@ -115,15 +111,17 @@ function Grido:get_visual() -- get the current state for the loops for i=1,6 do - local row=i -- get current position - local colStart=util.linlin(uC.bufferMinMax[i][2],uC.bufferMinMax[i][2]+self.loopMax,1,16,uP[i].loopStart+uC.bufferMinMax[i][2]) - local colEnder=util.linlin(uC.bufferMinMax[i][2],uC.bufferMinMax[i][2]+self.loopMax,1,16,uP[i].loopStart+uP[i].loopLength+uC.bufferMinMax[i][2]) - local colPoser=util.linlin(uC.bufferMinMax[i][2],uC.bufferMinMax[i][2]+self.loopMax,1,16,uP[i].position+uC.bufferMinMax[i][2]) - for col=colStart,colEnder do - self.visual[row][col]=5 + local colStart=params:get(i.."start")/self.loopMax*16+1 + local colEnder=(params:get(i.."start")+params:get(i.."length"))/self.loopMax*16 + local colPoser=(uP[i].position-uP[i].loopStart)/(uP[i].loopLength)*(colEnder-colStart+1) + colStart = math.floor(colStart) + colEnder = math.floor(colEnder) + colPoser = math.floor(colPoser)+colStart + for j=colStart,colEnder do + self.visual[i][j]=5 end - self.visual[row][colPoser]=15 + self.visual[i][colPoser]=15 end -- illuminate currently pressed button @@ -131,16 +129,16 @@ function Grido:get_visual() row,col=k:match("(%d+),(%d+)") self.visual[tonumber(row)][tonumber(col)]=15 end + + return self.visual end function Grido:grid_redraw() self.g:all(0) local gd=self:get_visual() - rows=#gd - cols=#gd[1] - for row=1,rows do - for col=1,cols do - if gd[row][col]~=0 then + for row=1,8 do + for col=1,16 do + if gd[row][col]~=0 then self.g:led(col,row,gd[row][col]) end end @@ -148,4 +146,4 @@ function Grido:grid_redraw() self.g:refresh() end -return Grid +return Grido From f92eaaa40952a95d615210a5409e6246e7498ddb Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sat, 13 Feb 2021 12:07:45 +0700 Subject: [PATCH 03/10] add todo --- lib/grido.lua | 105 +++++++++++++++++++++++++++++++++++++++++++------- oooooo.lua | 5 ++- 2 files changed, 94 insertions(+), 16 deletions(-) diff --git a/lib/grido.lua b/lib/grido.lua index aeac293..3673e78 100644 --- a/lib/grido.lua +++ b/lib/grido.lua @@ -2,10 +2,17 @@ local Grido={} +local rates = {-400,-200,-150,-100,-75,-50,-25,-12.5,12.5,25,50,75,100,150,200,400} +local periods = {96,48,24,12,10,8,6,4,3.2,2.4,1.6,1.2,0.8,0.4,0.2,0.1} + function Grido:new(args) local m=setmetatable({},{__index=Grido}) local args=args==nil and {} or args + -- selection + m.selection = 1 + m.selection_lfo = 1 + -- setup visual m.visual={} for i=1,8 do @@ -57,11 +64,52 @@ function Grido:key_press(row,col,on) self.pressed_buttons[row..","..col]=nil end - if row <= 6 then - if on then + if row <= 6 and on then + if self.selection == 1 then self:change_loop(row) + elseif self.selection == 2 then + self:change_rate(row) + end + elseif row==7 and on then + if self.selection > 1 then + self:change_lfo(col) + end + elseif row==8 and on then + self:change_selection(col) + end +end + +function Grido:change_selection(selection) + if selection <= 2 then + self.selection = selection + end +end + +function Grido:change_lfo(selection) + self.selection_lfo = selection +end + +function Grido:change_rate(row) + local loopStart = 0 + local loopEnder = 0 + for i=1,16 do + if self.pressed_buttons[row..","..i]==true then + if loopStart == 0 then + loopStart = i + else + loopEnder = i + end end end + if loopEnder > 0 then + loopCenter = math.floor((loopStart+loopEnder)/2) + params:set(row.."rate lfo center",loopCenter) + params:set(row.."rate lfo amp",(loopEnder-loopCenter)/16) + params:set(row.."rate lfo period",periods[self.selection_lfo]) + else + params:set(row.."rate lfo amp",0) + end + params:set(row.."rate",loopStart) end function Grido:change_loop(row) @@ -109,21 +157,50 @@ function Grido:get_visual() end end - -- get the current state for the loops - for i=1,6 do - -- get current position - local colStart=params:get(i.."start")/self.loopMax*16+1 - local colEnder=(params:get(i.."start")+params:get(i.."length"))/self.loopMax*16 - local colPoser=(uP[i].position-uP[i].loopStart)/(uP[i].loopLength)*(colEnder-colStart+1) - colStart = math.floor(colStart) - colEnder = math.floor(colEnder) - colPoser = math.floor(colPoser)+colStart - for j=colStart,colEnder do - self.visual[i][j]=5 + if self.selection == 1 then + -- get the current state for the loops + for i=1,6 do + -- get current position + local colStart=params:get(i.."start")/self.loopMax*16+1 + local colEnder=(params:get(i.."start")+params:get(i.."length"))/self.loopMax*16 + local colPoser=(uP[i].position-uP[i].loopStart)/(uP[i].loopLength)*(colEnder-colStart+1) + colStart = math.floor(colStart) + colEnder = math.floor(colEnder) + colPoser = math.floor(colPoser)+colStart + for j=colStart,colEnder do + self.visual[i][j]=5 + end + self.visual[i][colPoser]=15 + end + elseif self.selection == 2 then + -- set current rate for the loops + for i=1,6 do + if params:get(i.."rate lfo amp") > 0 and params:get(i.."rate lfo period") > 0 then + local closestRate = {1,10000} + for j,rate in ipairs(rates) do + local diff = math.abs(100*uP[i].rate-rate) + if diff < closestRate[2] then + closestRate = {j,diff} + end + end + self.visual[i][closestRate[1]] = 15 + else + self.visual[i][params:get(i.."rate")] = 15 + end end - self.visual[i][colPoser]=15 end + -- show lfo period scale if selection > 1 + if self.selection > 1 then + for i=1,16 do + self.visual[7][i]=i-1 + end + self.visual[7][self.selection_lfo]=15*self.blinky[17-self.selection_lfo] + end + + -- illuminate selection + self.visual[8][self.selection] = 15 + -- illuminate currently pressed button for k,_ in pairs(self.pressed_buttons) do row,col=k:match("(%d+),(%d+)") diff --git a/oooooo.lua b/oooooo.lua index 7c7583a..9128fb5 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -82,7 +82,7 @@ uC={ recArmThreshold=0.03, backupNumber=1, lfoTime=1, - discreteRates={-400,-200*1.498,-200,-100*1.498,-100,-50*1.498,-50,-25*1.498,-25,0,25,1.498*25,50,1.498*50,100,1.498*100,200,1.498*200,400}, + discreteRates= {-400,-200,-150,-100,-75,-50,-25,-12.5,12.5,25,50,75,100,150,200,400}, discreteBeats={1/4,1/2,1,2}, pampfast=0.02, timeUntilLagInitiates=0.1, @@ -255,6 +255,7 @@ function init() softcut.post_filter_fc(i,value) end } + -- TODO: add filter LFO! params:add { type='control', id=i..'filter_reso', @@ -483,7 +484,7 @@ function init_loops(j,ignore_pan) params:set(i.."vol lfo amp",0.3) params:set(i.."vol lfo period",0) params:set(i.."vol lfo offset",0) - params:set(i.."rate",15) + params:set(i.."rate",13) params:set(i.."rate adjust",0) params:set(i.."rate reverse",2) params:set(i.."rate lfo center",10) From 2d908650cdeba525ff50b91d81d1b17ceb70de57 Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sat, 13 Feb 2021 21:18:47 +0700 Subject: [PATCH 04/10] allow setting specific tones in the rate using 'rate tone' --- oooooo.lua | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/oooooo.lua b/oooooo.lua index 9128fb5..93d1fbc 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -25,7 +25,8 @@ local Formatters=require 'formatters' local grido=include("oooooo/lib/grido") - +local MusicUtil = require "musicutil" +local intonation=require "intonation" engine.name="SimpleDelay" @@ -54,6 +55,7 @@ uS={ lagActivated=false, timeSinceArming=0, lastOnset=0, + toneRates = {}, } -- user constants @@ -91,6 +93,7 @@ uC={ DATA_DIR=_path.data.."oooooo/" PATH=_path.audio..'oooooo/' +local scale_names={} function init() @@ -181,7 +184,11 @@ function init() params:add_text('save_message',">","") - params:add_group("all loops",8) + for i = 1, #MusicUtil.SCALES do + table.insert(scale_names, string.lower(MusicUtil.SCALES[i].name)) + end + + params:add_group("all loops",9) params:add_option("pause lfos","pause lfos",{"no","yes"},1) params:add_control("destroy loops","destroy loops",controlspec.new(0,100,'lin',1,0,'% prob')) params:add_control("vol ramp","vol ramp",controlspec.new(-1,1,'lin',0,0,'',0.01/2)) @@ -206,6 +213,12 @@ function init() softcut.rate_slew_time(i,x) end end) + params:add{type = "option", id = "scale_mode", name = "scale mode", + options = scale_names, default = 5, + action = function() + build_ji_rates() + uS.rateUpdate=true + end} params:add_option("expert mode","expert mode",{"no","yes"},1) params:set_action("expert mode",update_parameters) @@ -213,7 +226,7 @@ function init() filter_resonance=controlspec.new(0.05,1,'lin',0,1,'') filter_freq=controlspec.new(20,20000,'exp',0,20000,'Hz') for i=1,6 do - params:add_group("loop "..i,35) + params:add_group("loop "..i,36) -- id name min max default k units params:add_control(i.."start","start",controlspec.new(0,uC.loopMinMax[2],"lin",0.01,0,"s",0.01/uC.loopMinMax[2])) params:add_control(i.."start lfo amp","start lfo amp",controlspec.new(0,1,"lin",0.01,0.2,"",0.01)) @@ -229,6 +242,8 @@ function init() params:add_control(i.."vol lfo offset","vol lfo offset",controlspec.new(0,60,"lin",0,0,"s",0.1/60)) params:add_option(i.."rate","rate (%)",uC.discreteRates,#uC.discreteRates) params:add_control(i.."rate adjust","rate adjust (%)",controlspec.new(-400,400,"lin",0.1,0,"%",0.1/800)) + params:add_control(i.."rate tone","rate tone",controlspec.new(0,16,"lin",1,0,"")) + params:set_action(i.."rate tone",function(v) uP[i].rateUpdate = true end) params:add_option(i.."rate reverse","reverse rate",{"on","off"},2) params:add_option(i.."rate lfo center","rate lfo center (%)",uC.discreteRates,#uC.discreteRates) params:add_control(i.."rate lfo amp","rate lfo amp",controlspec.new(0,1,"lin",0.01,0.25,"",0.01)) @@ -539,6 +554,21 @@ function init_loops(j,ignore_pan) softcut.pre_filter_fc(i,20100) end end + + build_ji_rates() +end + +function build_ji_rates() + notes = MusicUtil.generate_scale_of_length(60, params:get("scale_mode"), 24) + tones = intonation.normal() + uS.toneRates = {} + for i,note in ipairs(notes) do + ratio_index = note - 60 + 1 + if ratio_index > #tones then + break + end + table.insert(uS.toneRates,tones[ratio_index]) + end end function activate_mode() @@ -831,7 +861,8 @@ function update_timer() currentRateIndex=util.clamp(params:get(i.."rate lfo center")+round(util.linlin(-1,1,-#uC.discreteRates,#uC.discreteRates,params:get(i.."rate lfo amp")*calculate_lfo(uS.currentTime,params:get(i.."rate lfo period"),params:get(i.."rate lfo offset")))),1,#uC.discreteRates) -- currentRateIndex=util.clamp(round(util.linlin(-1,1,0,1+#uC.discreteRates,params:get(i.."rate lfo amp")*calculate_lfo(uS.currentTime,params:get(i.."rate lfo period"),params:get(i.."rate lfo offset")))),1,#uC.discreteRates) end - uP[i].rate=(uC.discreteRates[currentRateIndex]+params:get(i.."rate adjust"))*(params:get(i.."rate reverse")*2-3)/100.0 + local toneRate = uS.toneRates[params:get(i.."rate tone")%#uS.toneRates+1] + uP[i].rate=(uC.discreteRates[currentRateIndex]+params:get(i.."rate adjust"))*(params:get(i.."rate reverse")*2-3)/100.0*toneRate softcut.rate(i,uP[i].rate) end if uP[i].panUpdate or (params:get(i.."pan lfo period")>0 and params:get("pause lfos")==1 and params:get(i.."pan lfo amp")>0) then From 6598d17c7e0acc32e6010bb12da4ef9d8d8d32e9 Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sat, 13 Feb 2021 21:32:08 +0700 Subject: [PATCH 05/10] add filter lfo --- oooooo.lua | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/oooooo.lua b/oooooo.lua index 93d1fbc..2ee6401 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -226,7 +226,7 @@ function init() filter_resonance=controlspec.new(0.05,1,'lin',0,1,'') filter_freq=controlspec.new(20,20000,'exp',0,20000,'Hz') for i=1,6 do - params:add_group("loop "..i,36) + params:add_group("loop "..i,39) -- id name min max default k units params:add_control(i.."start","start",controlspec.new(0,uC.loopMinMax[2],"lin",0.01,0,"s",0.01/uC.loopMinMax[2])) params:add_control(i.."start lfo amp","start lfo amp",controlspec.new(0,1,"lin",0.01,0.2,"",0.01)) @@ -267,7 +267,7 @@ function init() controlspec=filter_freq, formatter=Formatters.format_freq, action=function(value) - softcut.post_filter_fc(i,value) + uP[i].filterUpdate=true end } -- TODO: add filter LFO! @@ -280,6 +280,9 @@ function init() softcut.post_filter_rq(i,value) end } + params:add_control(i.."filter lfo amp","filter lfo amp",controlspec.new(0,1,"lin",0.01,0.25,"",0.01)) + params:add_control(i.."filter lfo period","filter lfo period",controlspec.new(0,60,"lin",0,0,"s",0.1/60)) + params:add_control(i.."filter lfo offset","filter lfo offset",controlspec.new(0,60,"lin",0,0,"s",0.1/60)) params:add{type='binary',name="play trig",id=i..'play trig',behavior='momentary', action=function(v) if v==1 then @@ -420,6 +423,7 @@ function init() params:set_action(i.."rate",function(x) uP[i].rateUpdate=true end) params:set_action(i.."rate reverse",function(x) uP[i].rateUpdate=true end) params:set_action(i.."rate adjust",function(x) uP[i].rateUpdate=true end) + params:set_action(i.."filter_frequency",function(x) uP[i].filterUpdate=true end) end redraw() @@ -484,6 +488,7 @@ function init_loops(j,ignore_pan) uP[i].pan = previous_uP[i].pan end uP[i].panUpdate=false + uP[i].filterUpdate=false uP[i].lfoWarble={} uP[i].destroying=false if i<7 then @@ -512,6 +517,9 @@ function init_loops(j,ignore_pan) params:set(i.."pan lfo amp",0.5) params:set(i.."pan lfo period",0) params:set(i.."pan lfo offset",0) + params:set(i.."filter lfo amp",0.5) + params:set(i.."filter lfo period",0) + params:set(i.."filter lfo offset",0) params:set(i.."reset every beat",0) params:set(i.."isempty",2) end @@ -900,6 +908,16 @@ function update_timer() softcut.loop_start(i,uP[i].loopStart+uC.bufferMinMax[i][2]) softcut.loop_end(i,uP[i].loopStart+uC.bufferMinMax[i][2]+uP[i].loopLength) end + if uP[i].filterUpdate or (params:get(i.."filter lfo period")>0 and params:get("pause lfos")==1 and params:get(i.."filter lfo amp")>0) then + uP[i].filterUpdate=false + local fc=params:get(i.."filter_frequency") + if fc>0 and params:get(i.."filter lfo period")>0 and params:get("pause lfos")==1 then + fc = util.linlin(20,20000,-1,1,fc) + fc=fc+params:get(i.."filter lfo amp")*calculate_lfo(uS.currentTime,params:get(i.."filter lfo period"),params:get(i.."filter lfo offset")) + fc = util.linlin(-1,1,150,20000,util.clamp(fc,-1,1)) + end + softcut.post_filter_fc(i,fc) + end end end From f3b5a39af1629205d1379d0e661d24f999fb8d0e Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sun, 14 Feb 2021 01:44:37 +0700 Subject: [PATCH 06/10] add glyphs --- lib/glyphs.json | 1 + lib/glyphs.lua | 31 ++++ lib/grido.lua | 210 +++++++++++++++++++++++--- lib/json.lua | 388 ++++++++++++++++++++++++++++++++++++++++++++++++ oooooo.lua | 8 +- 5 files changed, 612 insertions(+), 26 deletions(-) create mode 100644 lib/glyphs.json create mode 100644 lib/glyphs.lua create mode 100644 lib/json.lua diff --git a/lib/glyphs.json b/lib/glyphs.json new file mode 100644 index 0000000..a650abe --- /dev/null +++ b/lib/glyphs.json @@ -0,0 +1 @@ +[{"glyph":" ","positions":[]},{"glyph":"!","positions":[{"x":1,"y":0},{"x":1,"y":1},{"x":1,"y":3}]},{"glyph":"\"","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1}]},{"glyph":"#","positions":[{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":2},{"x":0,"y":3}]},{"glyph":"$","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"%","positions":[{"x":0,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":3}]},{"glyph":"\u0026","positions":[{"x":1,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":1,"y":3}]},{"glyph":"'","positions":[{"x":1,"y":0},{"x":1,"y":1}]},{"glyph":"(","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":0,"y":2},{"x":1,"y":3}]},{"glyph":")","positions":[{"x":1,"y":0},{"x":2,"y":1},{"x":2,"y":2},{"x":1,"y":3}]},{"glyph":"*","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":1,"y":1},{"x":0,"y":2},{"x":2,"y":2}]},{"glyph":"+","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":1,"y":2}]},{"glyph":",","positions":[{"x":2,"y":2},{"x":1,"y":3}]},{"glyph":"-","positions":[{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1}]},{"glyph":".","positions":[{"x":1,"y":3}]},{"glyph":"/","positions":[{"x":2,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":0,"y":3}]},{"glyph":"0","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"1","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":1,"y":2},{"x":1,"y":3}]},{"glyph":"2","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"3","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"4","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":2,"y":3}]},{"glyph":"5","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"6","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"7","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":2,"y":1},{"x":1,"y":2},{"x":1,"y":3}]},{"glyph":"8","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"9","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":":","positions":[{"x":1,"y":1},{"x":1,"y":3}]},{"glyph":";","positions":[{"x":1,"y":0},{"x":2,"y":2},{"x":1,"y":3}]},{"glyph":"\u003c","positions":[{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":2}]},{"glyph":"=","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2}]},{"glyph":"\u003e","positions":[{"x":0,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2}]},{"glyph":"?","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":1,"y":3}]},{"glyph":"@","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"A","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"B","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"C","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":0,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"D","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"E","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":0,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"F","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3}]},{"glyph":"G","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"H","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"I","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":1,"y":1},{"x":1,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"J","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3}]},{"glyph":"K","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"L","positions":[{"x":0,"y":0},{"x":0,"y":1},{"x":0,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"M","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"N","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"O","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"P","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3}]},{"glyph":"Q","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"R","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"S","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"T","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":1,"y":1},{"x":1,"y":2},{"x":1,"y":3}]},{"glyph":"U","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"V","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"W","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"X","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":1,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"Y","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":1,"y":3}]},{"glyph":"Z","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":2,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"[","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":0,"y":1},{"x":0,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"\\","positions":[{"x":0,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":2,"y":3}]},{"glyph":"]","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":2,"y":1},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"^","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1}]},{"glyph":"_","positions":[{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"`","positions":[{"x":0,"y":0},{"x":1,"y":1}]},{"glyph":"a","positions":[{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"b","positions":[{"x":0,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"c","positions":[{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":0,"y":3},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"d","positions":[{"x":2,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"e","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"f","positions":[{"x":2,"y":0},{"x":1,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":1,"y":3}]},{"glyph":"g","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"h","positions":[{"x":0,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"i","positions":[{"x":0,"y":0},{"x":1,"y":2},{"x":1,"y":3}]},{"glyph":"j","positions":[{"x":1,"y":0},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3}]},{"glyph":"k","positions":[{"x":0,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"l","positions":[{"x":1,"y":0},{"x":1,"y":1},{"x":1,"y":2},{"x":2,"y":3}]},{"glyph":"m","positions":[{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"n","positions":[{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"o","positions":[{"x":1,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"p","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":0,"y":3}]},{"glyph":"q","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":2,"y":3}]},{"glyph":"r","positions":[{"x":1,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":0,"y":3}]},{"glyph":"s","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"t","positions":[{"x":1,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":2,"y":3}]},{"glyph":"u","positions":[{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"v","positions":[{"x":0,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":2,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"w","positions":[{"x":0,"y":1},{"x":2,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"x","positions":[{"x":0,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":0,"y":3},{"x":2,"y":3}]},{"glyph":"y","positions":[{"x":0,"y":0},{"x":2,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":2,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"z","positions":[{"x":0,"y":1},{"x":1,"y":1},{"x":1,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"{","positions":[{"x":1,"y":0},{"x":2,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":1,"y":2},{"x":1,"y":3},{"x":2,"y":3}]},{"glyph":"|","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":0,"y":1},{"x":1,"y":1},{"x":0,"y":2},{"x":1,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"}","positions":[{"x":0,"y":0},{"x":1,"y":0},{"x":1,"y":1},{"x":2,"y":1},{"x":1,"y":2},{"x":0,"y":3},{"x":1,"y":3}]},{"glyph":"~","positions":[{"x":0,"y":1},{"x":1,"y":1},{"x":1,"y":2},{"x":2,"y":2}]}] \ No newline at end of file diff --git a/lib/glyphs.lua b/lib/glyphs.lua new file mode 100644 index 0000000..a8a82ef --- /dev/null +++ b/lib/glyphs.lua @@ -0,0 +1,31 @@ +local Glyphs={} +local json=include("oooooo/lib/json") +local f=io.open(_path.code.."oooooo/lib/glyphs.json","rb") +local content=f:read("*all") +f:close() + +local glyphs=json.decode(content) + + +function Glyphs.pixels(name) + pixels={} + for i=1,#name do + if i==5 then + break + end + letter=name:sub(i,i) + for _,glyph in ipairs(glyphs) do + if glyph.glyph==letter then + for _,position in ipairs(glyph.positions) do + table.insert(pixels,{position.y+1,position.x+(i-1)*4+1,15}) + end + break + end + end + end + + + return pixels +end + +return Glyphs diff --git a/lib/grido.lua b/lib/grido.lua index 3673e78..626a88b 100644 --- a/lib/grido.lua +++ b/lib/grido.lua @@ -1,8 +1,12 @@ -- grido +local json=include("oooooo/lib/json") +local graphic_pixels=include("oooooo/lib/glyphs") local Grido={} +-- copied from ooooooo local rates = {-400,-200,-150,-100,-75,-50,-25,-12.5,12.5,25,50,75,100,150,200,400} +local octave_index = {9,10,11,13,15,16} local periods = {96,48,24,12,10,8,6,4,3.2,2.4,1.6,1.2,0.8,0.4,0.2,0.1} function Grido:new(args) @@ -11,7 +15,9 @@ function Grido:new(args) -- selection m.selection = 1 - m.selection_lfo = 1 + m.selection_scale = 10 + m.current_octave = {4,4,4,4,4,4} + m.show_graphic = {nil,0} -- setup visual m.visual={} @@ -68,28 +74,75 @@ function Grido:key_press(row,col,on) if self.selection == 1 then self:change_loop(row) elseif self.selection == 2 then + self:change_volume(row) + elseif self.selection == 3 then + self:change_pan(row) + elseif self.selection == 4 then self:change_rate(row) + elseif self.selection == 5 then + self:change_rate_ji(row,col) + elseif self.selection == 6 then + self:change_filter(row) + elseif self.selection == 7 then + self:change_play_status(row,col) end elseif row==7 and on then if self.selection > 1 then - self:change_lfo(col) + self:change_selection_scale(col) end elseif row==8 and on then self:change_selection(col) end end +function Grido:change_play_status(row,col) + if col==1 then + -- stop + params:set(row.."stop trig",0) + params:set(row.."stop trig",1) + elseif col==2 then + -- play + params:set(row.."play trig",0) + params:set(row.."play trig",1) + elseif col==3 then + -- arm + params:set(row.."arming trig",0) + params:set(row.."arming trig",1) + elseif col==4 then + -- rec + params:set(row.."recording trig",0) + params:set(row.."recording trig",1) + end +end + function Grido:change_selection(selection) - if selection <= 2 then + if selection == 2 then + self:show_text("volume") + elseif selection == 3 then + self:show_text("pan") + elseif selection == 4 then + self:show_text("rate") + elseif selection == 5 then + self:show_text("tone") + elseif selection == 6 then + self:show_text("freq") + elseif selection == 7 then + self:show_text(" REC") + end + if selection <= 7 then self.selection = selection end end -function Grido:change_lfo(selection) - self.selection_lfo = selection +function Grido:change_selection_scale(selection) + self.selection_scale = selection + if self.selection == 5 then + -- change slew rate if on rate ji + params:set("slew rate",util.linlin(1,16,0.1,10,selection)) + end end -function Grido:change_rate(row) +function Grido:get_touch_points(row) local loopStart = 0 local loopEnder = 0 for i=1,16 do @@ -101,30 +154,80 @@ function Grido:change_rate(row) end end end + return loopStart,loopEnder +end + +function Grido:change_volume(row) + loopStart,loopEnder = self:get_touch_points(row) + if loopEnder > 0 then + loopCenter = (loopStart+loopEnder)/2 + params:set(row.."vol",(loopCenter)/15) + params:set(row.."vol lfo amp",(loopEnder-loopStart)/15/2) + params:set(row.."vol lfo period",periods[self.selection_scale]) + else + params:set(row.."vol lfo amp",0) + params:set(row.."vol",(loopStart-1)/15) + end +end + +function Grido:change_pan(row) + loopStart,loopEnder = self:get_touch_points(row) + if loopEnder > 0 then + loopCenter = (loopStart+loopEnder)/2 + params:set(row.."pan",util.linlin(1,16,-1,1,loopCenter)) + params:set(row.."pan lfo amp",(loopEnder-loopStart)/15) + params:set(row.."pan lfo period",periods[self.selection_scale]) + else + params:set(row.."pan lfo amp",0) + params:set(row.."pan",util.linlin(1,16,-1,1,loopStart)) + end +end + +function Grido:change_filter(row) + loopStart,loopEnder = self:get_touch_points(row) + if loopEnder > 0 then + loopCenter = (loopStart+loopEnder)/2 + params:set(row.."filter_frequency",util.linlin(1,16,50,18000,loopCenter)) + params:set(row.."filter lfo amp",(loopEnder-loopStart)/15) + params:set(row.."filter lfo period",periods[self.selection_scale]) + else + params:set(row.."filter lfo amp",0) + params:set(row.."filter_frequency",util.linlin(1,16,50,18000,loopStart)) + end +end + +function Grido:change_rate(row) + loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then loopCenter = math.floor((loopStart+loopEnder)/2) params:set(row.."rate lfo center",loopCenter) - params:set(row.."rate lfo amp",(loopEnder-loopCenter)/16) - params:set(row.."rate lfo period",periods[self.selection_lfo]) + params:set(row.."rate lfo amp",(loopEnder-loopCenter)/15) + params:set(row.."rate lfo period",periods[self.selection_scale]) else params:set(row.."rate lfo amp",0) end params:set(row.."rate",loopStart) end -function Grido:change_loop(row) - local loopStart = 0 - local loopEnder = 0 - for i=1,16 do - if self.pressed_buttons[row..","..i]==true then - if loopStart == 0 then - loopStart = i - else - loopEnder = i - end - end +function Grido:change_rate_ji(row,col) + if col > 4 then + params:set(row.."rate tone",col-5) + elseif col == 2 then + -- octave down + self.current_octave[row]= util.clamp(self.current_octave[row]-1,1,#octave_index) + params:set(row.."rate",octave_index[self.current_octave[row]]) + elseif col == 3 then + -- octave up + self.current_octave[row] = util.clamp(self.current_octave[row]+1,1,#octave_index) + params:set(row.."rate",octave_index[self.current_octave[row]]) + elseif col == 4 then + -- reverse + params:set(row.."rate reverse",3-params:get(row.."rate reverse")) end +end +function Grido:change_loop(row) + loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then params:set(row.."start",(loopStart-1)/16*self.loopMax) params:set(row.."length",(loopEnder-loopStart+1)/16*self.loopMax) @@ -149,6 +252,9 @@ function Grido:get_visual() end end end + if self.show_graphic[2]>0 then + self.show_graphic[2]=self.show_graphic[2]-1 + end -- clear visual for row=1,8 do @@ -157,8 +263,17 @@ function Grido:get_visual() end end + if self.show_graphic[2]>0 then + local pixels=graphic_pixels.pixels(self.show_graphic[1]) + if pixels~=nil then + for _,p in ipairs(pixels) do + self.visual[p[1]+1][p[2]]=4 + end + end + end + if self.selection == 1 then - -- get the current state for the loops + -- show the current state for the loops for i=1,6 do -- get current position local colStart=params:get(i.."start")/self.loopMax*16+1 @@ -173,7 +288,17 @@ function Grido:get_visual() self.visual[i][colPoser]=15 end elseif self.selection == 2 then - -- set current rate for the loops + -- show current volume for the loops + for i=1,6 do + self.visual[i][util.round(uP[i].vol*15,1)+1] = 15 + end + elseif self.selection == 3 then + -- show current pan for the loops + for i=1,6 do + self.visual[i][util.round(util.linlin(-1,1,1,16,uP[i].pan),1)] = 15 + end + elseif self.selection == 4 then + -- show current rate for the loops for i=1,6 do if params:get(i.."rate lfo amp") > 0 and params:get(i.."rate lfo period") > 0 then local closestRate = {1,10000} @@ -188,14 +313,44 @@ function Grido:get_visual() self.visual[i][params:get(i.."rate")] = 15 end end + elseif self.selection == 5 then + -- show current rate ji for the loops + for i=1,6 do + self.visual[i][params:get(i.."rate tone")+5] = 15 + -- show current octave + local closestIndex = {1,10000} + for j,index in ipairs(octave_index) do + local diff = math.abs(params:get(i.."rate")-index) + if diff < closestIndex[2] then + closestIndex = {j,diff} + end + end + self.visual[i][1]=closestIndex[1]*2 + if params:get(i.."rate reverse") == 1 then + self.visual[i][4]=15 + end + end + elseif self.selection == 6 then + -- show current filter fc for the loops + for i=1,6 do + self.visual[i][util.round(util.linlin(50,18000,1,16,uP[i].fc),1)] = 15 + end + elseif self.selection == 7 then + -- show current play status + for i=1,6 do + self.visual[i][1] = uP[i].isStopped and 15 or 0 + self.visual[i][2] = uP[i].isStopped and 0 or 15 + self.visual[i][3] = uS.recording[i] == 1 and 15 or 0 + self.visual[i][4] = uS.recording[i] == 2 and 15 or 0 + end end -- show lfo period scale if selection > 1 - if self.selection > 1 then + if self.selection ~= 1 and self.selection ~= 7 then for i=1,16 do self.visual[7][i]=i-1 end - self.visual[7][self.selection_lfo]=15*self.blinky[17-self.selection_lfo] + self.visual[7][self.selection_scale]=15*self.blinky[17-self.selection_scale] end -- illuminate selection @@ -223,4 +378,13 @@ function Grido:grid_redraw() self.g:refresh() end +function Grido:show_text(text,time) + print("show_text "..text) + if time==nil then + time=40 + end + self.show_graphic={text,time} +end + + return Grido diff --git a/lib/json.lua b/lib/json.lua new file mode 100644 index 0000000..40e592f --- /dev/null +++ b/lib/json.lua @@ -0,0 +1,388 @@ +-- +-- json.lua +-- +-- Copyright (c) 2020 rxi +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy of +-- this software and associated documentation files (the "Software"), to deal in +-- the Software without restriction, including without limitation the rights to +-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +-- of the Software, and to permit persons to whom the Software is furnished to do +-- so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +local json = { _version = "0.1.2" } + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local encode + +local escape_char_map = { + [ "\\" ] = "\\", + [ "\"" ] = "\"", + [ "\b" ] = "b", + [ "\f" ] = "f", + [ "\n" ] = "n", + [ "\r" ] = "r", + [ "\t" ] = "t", +} + +local escape_char_map_inv = { [ "/" ] = "/" } +for k, v in pairs(escape_char_map) do + escape_char_map_inv[v] = k +end + + +local function escape_char(c) + return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) +end + + +local function encode_nil(val) + return "null" +end + + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if rawget(val, 1) ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= "number" then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + if n ~= #val then + error("invalid table: sparse array") + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + + +local function encode_string(val) + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' +end + + +local function encode_number(val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) +end + + +local type_func_map = { + [ "nil" ] = encode_nil, + [ "table" ] = encode_table, + [ "string" ] = encode_string, + [ "number" ] = encode_number, + [ "boolean" ] = tostring, +} + + +encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") +end + + +function json.encode(val) + return ( encode(val) ) +end + + +------------------------------------------------------------------------------- +-- Decode +------------------------------------------------------------------------------- + +local parse + +local function create_set(...) + local res = {} + for i = 1, select("#", ...) do + res[ select(i, ...) ] = true + end + return res +end + +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") + +local literal_map = { + [ "true" ] = true, + [ "false" ] = false, + [ "null" ] = nil, +} + + +local function next_char(str, idx, set, negate) + for i = idx, #str do + if set[str:sub(i, i)] ~= negate then + return i + end + end + return #str + 1 +end + + +local function decode_error(str, idx, msg) + local line_count = 1 + local col_count = 1 + for i = 1, idx - 1 do + col_count = col_count + 1 + if str:sub(i, i) == "\n" then + line_count = line_count + 1 + col_count = 1 + end + end + error( string.format("%s at line %d col %d", msg, line_count, col_count) ) +end + + +local function codepoint_to_utf8(n) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = math.floor + if n <= 0x7f then + return string.char(n) + elseif n <= 0x7ff then + return string.char(f(n / 64) + 192, n % 64 + 128) + elseif n <= 0xffff then + return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, + f(n % 4096 / 64) + 128, n % 64 + 128) + end + error( string.format("invalid unicode codepoint '%x'", n) ) +end + + +local function parse_unicode_escape(s) + local n1 = tonumber( s:sub(1, 4), 16 ) + local n2 = tonumber( s:sub(7, 10), 16 ) + -- Surrogate pair? + if n2 then + return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end +end + + +local function parse_string(str, i) + local res = "" + local j = i + 1 + local k = j + + while j <= #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + + elseif x == 92 then -- `\`: Escape + res = res .. str:sub(k, j - 1) + j = j + 1 + local c = str:sub(j, j) + if c == "u" then + local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) + or str:match("^%x%x%x%x", j + 1) + or decode_error(str, j - 1, "invalid unicode escape in string") + res = res .. parse_unicode_escape(hex) + j = j + #hex + else + if not escape_chars[c] then + decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") + end + res = res .. escape_char_map_inv[c] + end + k = j + 1 + + elseif x == 34 then -- `"`: End of string + res = res .. str:sub(k, j - 1) + return res, j + 1 + end + + j = j + 1 + end + + decode_error(str, i, "expected closing quote for string") +end + + +local function parse_number(str, i) + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then + decode_error(str, i, "invalid number '" .. s .. "'") + end + return n, x +end + + +local function parse_literal(str, i) + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x +end + + +local function parse_array(str, i) + local res = {} + local n = 1 + i = i + 1 + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then break end + if chr ~= "," then decode_error(str, i, "expected ']' or ','") end + end + return res, i +end + + +local function parse_object(str, i) + local res = {} + i = i + 1 + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then break end + if chr ~= "," then decode_error(str, i, "expected '}' or ','") end + end + return res, i +end + + +local char_func_map = { + [ '"' ] = parse_string, + [ "0" ] = parse_number, + [ "1" ] = parse_number, + [ "2" ] = parse_number, + [ "3" ] = parse_number, + [ "4" ] = parse_number, + [ "5" ] = parse_number, + [ "6" ] = parse_number, + [ "7" ] = parse_number, + [ "8" ] = parse_number, + [ "9" ] = parse_number, + [ "-" ] = parse_number, + [ "t" ] = parse_literal, + [ "f" ] = parse_literal, + [ "n" ] = parse_literal, + [ "[" ] = parse_array, + [ "{" ] = parse_object, +} + + +parse = function(str, idx) + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then + return f(str, idx) + end + decode_error(str, idx, "unexpected character '" .. chr .. "'") +end + + +function json.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + local res, idx = parse(str, next_char(str, 1, space_chars, true)) + idx = next_char(str, idx, space_chars, true) + if idx <= #str then + decode_error(str, idx, "trailing garbage") + end + return res +end + + +return json diff --git a/oooooo.lua b/oooooo.lua index 2ee6401..132697e 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -214,7 +214,7 @@ function init() end end) params:add{type = "option", id = "scale_mode", name = "scale mode", - options = scale_names, default = 5, + options = scale_names, default = 1, action = function() build_ji_rates() uS.rateUpdate=true @@ -476,6 +476,7 @@ function init_loops(j,ignore_pan) end uP[i].loopUpdate=false uP[i].position=0 + uP[i].fc=20000 uP[i].recordedLength=0 uP[i].isStopped=true uP[i].vol=0.5 @@ -912,10 +913,11 @@ function update_timer() uP[i].filterUpdate=false local fc=params:get(i.."filter_frequency") if fc>0 and params:get(i.."filter lfo period")>0 and params:get("pause lfos")==1 then - fc = util.linlin(20,20000,-1,1,fc) + fc = util.linlin(50,18000,-1,1,fc) fc=fc+params:get(i.."filter lfo amp")*calculate_lfo(uS.currentTime,params:get(i.."filter lfo period"),params:get(i.."filter lfo offset")) - fc = util.linlin(-1,1,150,20000,util.clamp(fc,-1,1)) + fc = util.linlin(-1,1,50,18000,util.clamp(fc,-1,1)) end + uP[i].fc = fc softcut.post_filter_fc(i,fc) end end From 708e806b580b291c983b3d6af501a35e0a3701cb Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sun, 14 Feb 2021 03:48:33 +0700 Subject: [PATCH 07/10] working? --- lib/grido.lua | 64 +++++++++++++++++++++++++-------------------------- oooooo.lua | 6 +++++ 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/lib/grido.lua b/lib/grido.lua index 626a88b..ee33353 100644 --- a/lib/grido.lua +++ b/lib/grido.lua @@ -18,6 +18,7 @@ function Grido:new(args) m.selection_scale = 10 m.current_octave = {4,4,4,4,4,4} m.show_graphic = {nil,0} + m.selected_loop = 1 -- setup visual m.visual={} @@ -80,38 +81,38 @@ function Grido:key_press(row,col,on) elseif self.selection == 4 then self:change_rate(row) elseif self.selection == 5 then - self:change_rate_ji(row,col) - elseif self.selection == 6 then self:change_filter(row) - elseif self.selection == 7 then - self:change_play_status(row,col) + elseif self.selection == 6 then + self:change_rate_ji(row,col) end elseif row==7 and on then if self.selection > 1 then self:change_selection_scale(col) + else + self:change_play_status(self.selected_loop,col) end elseif row==8 and on then self:change_selection(col) end end -function Grido:change_play_status(row,col) +function Grido:change_play_status(i,col) if col==1 then -- stop - params:set(row.."stop trig",0) - params:set(row.."stop trig",1) + params:set(i.."stop trig",0) + params:set(i.."stop trig",1) elseif col==2 then -- play - params:set(row.."play trig",0) - params:set(row.."play trig",1) + params:set(i.."play trig",0) + params:set(i.."play trig",1) elseif col==3 then -- arm - params:set(row.."arming trig",0) - params:set(row.."arming trig",1) + params:set(i.."arming trig",0) + params:set(i.."arming trig",1) elseif col==4 then -- rec - params:set(row.."recording trig",0) - params:set(row.."recording trig",1) + params:set(i.."recording trig",0) + params:set(i.."recording trig",1) end end @@ -123,13 +124,11 @@ function Grido:change_selection(selection) elseif selection == 4 then self:show_text("rate") elseif selection == 5 then - self:show_text("tone") - elseif selection == 6 then self:show_text("freq") - elseif selection == 7 then - self:show_text(" REC") + elseif selection == 6 then + self:show_text("tone") end - if selection <= 7 then + if selection <= 6 then self.selection = selection end end @@ -227,6 +226,7 @@ function Grido:change_rate_ji(row,col) end function Grido:change_loop(row) + self.selected_loop = row loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then params:set(row.."start",(loopStart-1)/16*self.loopMax) @@ -283,10 +283,18 @@ function Grido:get_visual() colEnder = math.floor(colEnder) colPoser = math.floor(colPoser)+colStart for j=colStart,colEnder do - self.visual[i][j]=5 + if i==self.selected_loop then + self.visual[i][j]=7 + else + self.visual[i][j]=3 + end end self.visual[i][colPoser]=15 end + self.visual[7][1] = uP[self.selected_loop].isStopped and 15 or 0 + self.visual[7][2] = uP[self.selected_loop].isStopped and 0 or 15 + self.visual[7][3] = uS.recording[self.selected_loop] == 1 and 15 or 0 + self.visual[7][4] = uS.recording[self.selected_loop] == 2 and 15 or 0 elseif self.selection == 2 then -- show current volume for the loops for i=1,6 do @@ -314,6 +322,11 @@ function Grido:get_visual() end end elseif self.selection == 5 then + -- show current filter fc for the loops + for i=1,6 do + self.visual[i][util.round(util.linlin(50,18000,1,16,uP[i].fc),1)] = 15 + end + elseif self.selection == 6 then -- show current rate ji for the loops for i=1,6 do self.visual[i][params:get(i.."rate tone")+5] = 15 @@ -330,19 +343,6 @@ function Grido:get_visual() self.visual[i][4]=15 end end - elseif self.selection == 6 then - -- show current filter fc for the loops - for i=1,6 do - self.visual[i][util.round(util.linlin(50,18000,1,16,uP[i].fc),1)] = 15 - end - elseif self.selection == 7 then - -- show current play status - for i=1,6 do - self.visual[i][1] = uP[i].isStopped and 15 or 0 - self.visual[i][2] = uP[i].isStopped and 0 or 15 - self.visual[i][3] = uS.recording[i] == 1 and 15 or 0 - self.visual[i][4] = uS.recording[i] == 2 and 15 or 0 - end end -- show lfo period scale if selection > 1 diff --git a/oooooo.lua b/oooooo.lua index 132697e..3216c7a 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -523,6 +523,12 @@ function init_loops(j,ignore_pan) params:set(i.."filter lfo offset",0) params:set(i.."reset every beat",0) params:set(i.."isempty",2) + params:set(i.."play trig",0) + params:set(i.."arming trig",0) + params:set(i.."recording trig",0) + params:set(i.."reset trig",0) + params:set(i.."stop trig",0) + params:set(i.."rate tone",0) end for j=1,3 do uP[i].lfoWarble[j]=math.random(1,60) From 6506ab91ebbb69003ecb8eb46bedc47e17ce9867 Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sun, 14 Feb 2021 05:44:54 +0700 Subject: [PATCH 08/10] better --- lib/grido.lua | 12 ++++++++---- oooooo.lua | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/grido.lua b/lib/grido.lua index ee33353..f29b4bb 100644 --- a/lib/grido.lua +++ b/lib/grido.lua @@ -135,7 +135,7 @@ end function Grido:change_selection_scale(selection) self.selection_scale = selection - if self.selection == 5 then + if self.selection == 6 then -- change slew rate if on rate ji params:set("slew rate",util.linlin(1,16,0.1,10,selection)) end @@ -162,7 +162,8 @@ function Grido:change_volume(row) loopCenter = (loopStart+loopEnder)/2 params:set(row.."vol",(loopCenter)/15) params:set(row.."vol lfo amp",(loopEnder-loopStart)/15/2) - params:set(row.."vol lfo period",periods[self.selection_scale]) + params:set(row.."vol lfo period",periods[self.selection_scale]+math.random(1,100)/100) + params:set(row.."vol lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."vol lfo amp",0) params:set(row.."vol",(loopStart-1)/15) @@ -175,7 +176,8 @@ function Grido:change_pan(row) loopCenter = (loopStart+loopEnder)/2 params:set(row.."pan",util.linlin(1,16,-1,1,loopCenter)) params:set(row.."pan lfo amp",(loopEnder-loopStart)/15) - params:set(row.."pan lfo period",periods[self.selection_scale]) + params:set(row.."pan lfo period",periods[self.selection_scale]+math.random(1,100)/100) + params:set(row.."pan lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."pan lfo amp",0) params:set(row.."pan",util.linlin(1,16,-1,1,loopStart)) @@ -188,7 +190,8 @@ function Grido:change_filter(row) loopCenter = (loopStart+loopEnder)/2 params:set(row.."filter_frequency",util.linlin(1,16,50,18000,loopCenter)) params:set(row.."filter lfo amp",(loopEnder-loopStart)/15) - params:set(row.."filter lfo period",periods[self.selection_scale]) + params:set(row.."filter lfo period",periods[self.selection_scale]+math.random(1,100)/100) + params:set(row.."filter lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."filter lfo amp",0) params:set(row.."filter_frequency",util.linlin(1,16,50,18000,loopStart)) @@ -202,6 +205,7 @@ function Grido:change_rate(row) params:set(row.."rate lfo center",loopCenter) params:set(row.."rate lfo amp",(loopEnder-loopCenter)/15) params:set(row.."rate lfo period",periods[self.selection_scale]) + params:set(row.."rate lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."rate lfo amp",0) end diff --git a/oooooo.lua b/oooooo.lua index 3216c7a..540ba39 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -450,6 +450,7 @@ function init() grido:new() + params:set("scale_mode",9) -- DEV comment this out -- params:set("choose mode",3) -- activate_mode() From f77699a7a662a4fdb74324e6b546847dbab2492a Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Sun, 14 Feb 2021 22:44:50 +0700 Subject: [PATCH 09/10] works with grid 8x8? --- lib/grido.lua | 205 ++++++++++++++++++++++++++++---------------------- oooooo.lua | 9 ++- 2 files changed, 122 insertions(+), 92 deletions(-) diff --git a/lib/grido.lua b/lib/grido.lua index f29b4bb..05fbab2 100644 --- a/lib/grido.lua +++ b/lib/grido.lua @@ -5,26 +5,50 @@ local graphic_pixels=include("oooooo/lib/glyphs") local Grido={} -- copied from ooooooo -local rates = {-400,-200,-150,-100,-75,-50,-25,-12.5,12.5,25,50,75,100,150,200,400} -local octave_index = {9,10,11,13,15,16} -local periods = {96,48,24,12,10,8,6,4,3.2,2.4,1.6,1.2,0.8,0.4,0.2,0.1} +local page_loops = 1 +local page_tones = 2 +local page_volume = 3 +local page_pan = 4 +local page_rate = 5 +local page_frequency = 6 function Grido:new(args) local m=setmetatable({},{__index=Grido}) local args=args==nil and {} or args + -- initiate the grid + m.g=grid.connect() + m.g.key=function(x,y,z) + m:grid_key(x,y,z) + end + + -- 16 or 8 width + m.grid_width = 16 + m.rates = {-400,-200,-150,-100,-75,-50,-25,-12.5,12.5,25,50,75,100,150,200,400} + m.rates_index = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16} + m.octave_index = {9,10,11,13,15,16} + m.periods = {96,48,24,12,10,8,6,4,3.2,2.4,1.6,1.2,0.8,0.4,0.2,0.1} + if m.g.cols == 8 then + m.grid_width = 8 + m.rates = {-200,-100,-50,-25,25,50,100,200} + m.rates_index = {2,4,6,7,10,11,13,15} + m.octave_index = {5,6,7,8} + m.periods = {60,30,15,7,4,2,1,0.3} + end + -- selection m.selection = 1 - m.selection_scale = 10 + m.selection_scale = math.floor(2/3*m.grid_width) m.current_octave = {4,4,4,4,4,4} m.show_graphic = {nil,0} m.selected_loop = 1 -- setup visual + m.shown_text={false,false,false,false,false,false,false} m.visual={} for i=1,8 do m.visual[i]={} - for j=1,16 do + for j=1,m.grid_width do m.visual[i][j]=0 end end @@ -32,7 +56,7 @@ function Grido:new(args) -- debouncing and blinking m.blink_count=0 m.blinky={} - for i=1,16 do + for i=1,m.grid_width do m.blinky[i]=1 -- 1 = fast, 16 = slow end @@ -40,13 +64,6 @@ function Grido:new(args) m.loopMax=(60/clock.get_tempo())*params:get("start length") m.pressed_buttons={} - -- initiate the grid - -- grid specific - m.g=grid.connect() - m.g.key=function(x,y,z) - m:grid_key(x,y,z) - end - print("grid columns: "..m.g.cols) -- grid refreshing m.grid_refresh=metro.init() @@ -72,17 +89,17 @@ function Grido:key_press(row,col,on) end if row <= 6 and on then - if self.selection == 1 then + if self.selection == page_loops then self:change_loop(row) - elseif self.selection == 2 then + elseif self.selection == page_volume then self:change_volume(row) - elseif self.selection == 3 then + elseif self.selection == page_pan then self:change_pan(row) - elseif self.selection == 4 then + elseif self.selection == page_rate then self:change_rate(row) - elseif self.selection == 5 then + elseif self.selection == page_frequency then self:change_filter(row) - elseif self.selection == 6 then + elseif self.selection == page_tones then self:change_rate_ji(row,col) end elseif row==7 and on then @@ -117,16 +134,31 @@ function Grido:change_play_status(i,col) end function Grido:change_selection(selection) - if selection == 2 then - self:show_text("volume") - elseif selection == 3 then - self:show_text("pan") - elseif selection == 4 then - self:show_text("rate") - elseif selection == 5 then - self:show_text("freq") - elseif selection == 6 then - self:show_text("tone") + if selection == page_volume then + if not self.shown_text[page_volume] then + self:show_text("volume") + self.shown_text[page_volume] = true + end + elseif selection == page_pan then + if not self.shown_text[page_pan] then + self:show_text("pan") + self.shown_text[page_pan] = true + end + elseif selection == page_rate then + if not self.shown_text[page_rate] then + self:show_text("rate") + self.shown_text[page_rate] = true + end + elseif selection == page_frequency then + if not self.shown_text[page_frequency] then + self:show_text("freq") + self.shown_text[page_frequency] = true + end + elseif selection == page_tones then + if not self.shown_text[page_tones] then + self:show_text("tone") + self.shown_text[page_tones] = true + end end if selection <= 6 then self.selection = selection @@ -135,16 +167,16 @@ end function Grido:change_selection_scale(selection) self.selection_scale = selection - if self.selection == 6 then + if self.selection == page_tones then -- change slew rate if on rate ji - params:set("slew rate",util.linlin(1,16,0.1,10,selection)) + params:set("slew rate",util.linlin(1,self.grid_width,0.1,10,selection)) end end function Grido:get_touch_points(row) local loopStart = 0 local loopEnder = 0 - for i=1,16 do + for i=1,self.grid_width do if self.pressed_buttons[row..","..i]==true then if loopStart == 0 then loopStart = i @@ -160,13 +192,13 @@ function Grido:change_volume(row) loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then loopCenter = (loopStart+loopEnder)/2 - params:set(row.."vol",(loopCenter)/15) - params:set(row.."vol lfo amp",(loopEnder-loopStart)/15/2) - params:set(row.."vol lfo period",periods[self.selection_scale]+math.random(1,100)/100) + params:set(row.."vol",util.linlin(1,self.grid_width+1,0,1,loopCenter)) + params:set(row.."vol lfo amp",(loopEnder-loopStart)/(self.grid_width-1)/2) + params:set(row.."vol lfo period",self.periods[self.selection_scale]+math.random(1,100)/100) params:set(row.."vol lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."vol lfo amp",0) - params:set(row.."vol",(loopStart-1)/15) + params:set(row.."vol",util.linlin(1,self.grid_width+1,0,1,loopStart)) end end @@ -174,13 +206,13 @@ function Grido:change_pan(row) loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then loopCenter = (loopStart+loopEnder)/2 - params:set(row.."pan",util.linlin(1,16,-1,1,loopCenter)) - params:set(row.."pan lfo amp",(loopEnder-loopStart)/15) - params:set(row.."pan lfo period",periods[self.selection_scale]+math.random(1,100)/100) + params:set(row.."pan",util.linlin(1,self.grid_width,-1,1,loopCenter)) + params:set(row.."pan lfo amp",(loopEnder-loopStart)/(self.grid_width-1)) + params:set(row.."pan lfo period",self.periods[self.selection_scale]+math.random(1,100)/100) params:set(row.."pan lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."pan lfo amp",0) - params:set(row.."pan",util.linlin(1,16,-1,1,loopStart)) + params:set(row.."pan",util.linlin(1,self.grid_width,-1,1,loopStart)) end end @@ -188,13 +220,13 @@ function Grido:change_filter(row) loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then loopCenter = (loopStart+loopEnder)/2 - params:set(row.."filter_frequency",util.linlin(1,16,50,18000,loopCenter)) - params:set(row.."filter lfo amp",(loopEnder-loopStart)/15) - params:set(row.."filter lfo period",periods[self.selection_scale]+math.random(1,100)/100) + params:set(row.."filter_frequency",util.linlin(1,self.grid_width,50,18000,loopCenter)) + params:set(row.."filter lfo amp",(loopEnder-loopStart)/(self.grid_width-1)) + params:set(row.."filter lfo period",self.periods[self.selection_scale]+math.random(1,100)/100) params:set(row.."filter lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."filter lfo amp",0) - params:set(row.."filter_frequency",util.linlin(1,16,50,18000,loopStart)) + params:set(row.."filter_frequency",util.linlin(1,self.grid_width,50,18000,loopStart)) end end @@ -202,28 +234,28 @@ function Grido:change_rate(row) loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then loopCenter = math.floor((loopStart+loopEnder)/2) - params:set(row.."rate lfo center",loopCenter) - params:set(row.."rate lfo amp",(loopEnder-loopCenter)/15) - params:set(row.."rate lfo period",periods[self.selection_scale]) + params:set(row.."rate lfo center",self.rates_index[loopCenter]) + params:set(row.."rate lfo amp",(loopEnder-loopCenter)/(self.grid_width-1)) + params:set(row.."rate lfo period",self.periods[self.selection_scale]) params:set(row.."rate lfo offset",math.random(10,60)/math.random(1,6)) else params:set(row.."rate lfo amp",0) end - params:set(row.."rate",loopStart) + params:set(row.."rate",self.rates_index[loopStart]) end function Grido:change_rate_ji(row,col) - if col > 4 then - params:set(row.."rate tone",col-5) - elseif col == 2 then + if col > 3 then + params:set(row.."rate tone",col-4) + elseif col == 1 then -- octave down - self.current_octave[row]= util.clamp(self.current_octave[row]-1,1,#octave_index) - params:set(row.."rate",octave_index[self.current_octave[row]]) - elseif col == 3 then + self.current_octave[row]= util.clamp(self.current_octave[row]-1,1,#self.octave_index) + params:set(row.."rate",self.octave_index[self.current_octave[row]]) + elseif col == 2 then -- octave up - self.current_octave[row] = util.clamp(self.current_octave[row]+1,1,#octave_index) - params:set(row.."rate",octave_index[self.current_octave[row]]) - elseif col == 4 then + self.current_octave[row] = util.clamp(self.current_octave[row]+1,1,#self.octave_index) + params:set(row.."rate",self.octave_index[self.current_octave[row]]) + elseif col == 3 then -- reverse params:set(row.."rate reverse",3-params:get(row.."rate reverse")) end @@ -233,10 +265,10 @@ function Grido:change_loop(row) self.selected_loop = row loopStart,loopEnder = self:get_touch_points(row) if loopEnder > 0 then - params:set(row.."start",(loopStart-1)/16*self.loopMax) - params:set(row.."length",(loopEnder-loopStart+1)/16*self.loopMax) + params:set(row.."start",(loopStart-1)/self.grid_width*self.loopMax) + params:set(row.."length",(loopEnder-loopStart+1)/self.grid_width*self.loopMax) end - softcut.position(row,(loopStart-1)/16*self.loopMax+uC.bufferMinMax[row][2]) + softcut.position(row,(loopStart-1)/self.grid_width*self.loopMax+uC.bufferMinMax[row][2]) end function Grido:get_visual() @@ -262,7 +294,7 @@ function Grido:get_visual() -- clear visual for row=1,8 do - for col=1,16 do + for col=1,self.grid_width do self.visual[row][col]=0 end end @@ -276,12 +308,12 @@ function Grido:get_visual() end end - if self.selection == 1 then + if self.selection == page_loops then -- show the current state for the loops for i=1,6 do -- get current position - local colStart=params:get(i.."start")/self.loopMax*16+1 - local colEnder=(params:get(i.."start")+params:get(i.."length"))/self.loopMax*16 + local colStart=params:get(i.."start")/self.loopMax*self.grid_width+1 + local colEnder=(params:get(i.."start")+params:get(i.."length"))/self.loopMax*self.grid_width local colPoser=(uP[i].position-uP[i].loopStart)/(uP[i].loopLength)*(colEnder-colStart+1) colStart = math.floor(colStart) colEnder = math.floor(colEnder) @@ -299,50 +331,47 @@ function Grido:get_visual() self.visual[7][2] = uP[self.selected_loop].isStopped and 0 or 15 self.visual[7][3] = uS.recording[self.selected_loop] == 1 and 15 or 0 self.visual[7][4] = uS.recording[self.selected_loop] == 2 and 15 or 0 - elseif self.selection == 2 then + elseif self.selection == page_volume then -- show current volume for the loops for i=1,6 do - self.visual[i][util.round(uP[i].vol*15,1)+1] = 15 + self.visual[i][util.round(uP[i].vol*self.grid_width)+1] = 15 end - elseif self.selection == 3 then + elseif self.selection == page_pan then -- show current pan for the loops for i=1,6 do - self.visual[i][util.round(util.linlin(-1,1,1,16,uP[i].pan),1)] = 15 + self.visual[i][util.round(util.linlin(-1,1,1,self.grid_width,uP[i].pan),1)] = 15 end - elseif self.selection == 4 then + elseif self.selection == page_rate then -- show current rate for the loops for i=1,6 do - if params:get(i.."rate lfo amp") > 0 and params:get(i.."rate lfo period") > 0 then - local closestRate = {1,10000} - for j,rate in ipairs(rates) do - local diff = math.abs(100*uP[i].rate-rate) - if diff < closestRate[2] then - closestRate = {j,diff} - end + local closestRate = {1,10000} + for j,rate in ipairs(self.rates) do + local diff = math.abs(100*uP[i].rate-rate) + if diff < closestRate[2] then + closestRate = {j,diff} end - self.visual[i][closestRate[1]] = 15 - else - self.visual[i][params:get(i.."rate")] = 15 end + self.visual[i][closestRate[1]] = 15 end - elseif self.selection == 5 then + elseif self.selection == page_frequency then -- show current filter fc for the loops for i=1,6 do - self.visual[i][util.round(util.linlin(50,18000,1,16,uP[i].fc),1)] = 15 + self.visual[i][util.round(util.linlin(50,18000,1,self.grid_width,uP[i].fc),1)] = 15 end - elseif self.selection == 6 then + elseif self.selection == page_tones then -- show current rate ji for the loops for i=1,6 do - self.visual[i][params:get(i.."rate tone")+5] = 15 + self.visual[i][params:get(i.."rate tone")+4] = 15 -- show current octave local closestIndex = {1,10000} - for j,index in ipairs(octave_index) do + for j,index in ipairs(self.octave_index) do local diff = math.abs(params:get(i.."rate")-index) if diff < closestIndex[2] then closestIndex = {j,diff} end end self.visual[i][1]=closestIndex[1]*2 + self.visual[i][2]=closestIndex[1]*2 if params:get(i.."rate reverse") == 1 then self.visual[i][4]=15 end @@ -350,11 +379,11 @@ function Grido:get_visual() end -- show lfo period scale if selection > 1 - if self.selection ~= 1 and self.selection ~= 7 then - for i=1,16 do + if self.selection ~= page_loops then + for i=1,self.grid_width do self.visual[7][i]=i-1 end - self.visual[7][self.selection_scale]=15*self.blinky[17-self.selection_scale] + self.visual[7][self.selection_scale]=15*self.blinky[self.grid_width+1-self.selection_scale] end -- illuminate selection @@ -373,7 +402,7 @@ function Grido:grid_redraw() self.g:all(0) local gd=self:get_visual() for row=1,8 do - for col=1,16 do + for col=1,self.grid_width do if gd[row][col]~=0 then self.g:led(col,row,gd[row][col]) end diff --git a/oooooo.lua b/oooooo.lua index 540ba39..f07537b 100644 --- a/oooooo.lua +++ b/oooooo.lua @@ -1,4 +1,4 @@ --- oooooo v1.5.2 +-- oooooo v1.6.0 -- 6 x digital tape loops -- -- llllllll.co/t/oooooo @@ -26,10 +26,12 @@ local Formatters=require 'formatters' local grido=include("oooooo/lib/grido") local MusicUtil = require "musicutil" -local intonation=require "intonation" engine.name="SimpleDelay" +-- from https://github.com/monome/norns/blob/main/lua/lib/intonation.lua +local intonation = {1/1, 16/15, 9/8, 6/5, 5/4, 4/3, 45/32, 3/2, 8/5, 5/3, 16/9, 15/8, 2*1/1, 2*16/15, 2*9/8, 2*6/5, 2*5/4, 2*4/3, 2*45/32, 2*3/2, 2*8/5, 2*5/3, 2*16/9, 2*15/8} + -- user parameters uP={ -- initialized in init @@ -458,7 +460,6 @@ end function init_loops(j,ignore_pan) audio.level_adc(1) -- input volume 1 - audio.level_cut(1) -- Softcut master level (same as in LEVELS screen) i1=j i2=j @@ -576,7 +577,7 @@ end function build_ji_rates() notes = MusicUtil.generate_scale_of_length(60, params:get("scale_mode"), 24) - tones = intonation.normal() + tones = intonation uS.toneRates = {} for i,note in ipairs(notes) do ratio_index = note - 60 + 1 From 34a12fb9c23130ec5ec7327307969194039199bd Mon Sep 17 00:00:00 2001 From: Zack Scholl Date: Mon, 15 Feb 2021 00:53:26 +0700 Subject: [PATCH 10/10] update --- lib/grido.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/grido.lua b/lib/grido.lua index 05fbab2..0918964 100644 --- a/lib/grido.lua +++ b/lib/grido.lua @@ -373,7 +373,7 @@ function Grido:get_visual() self.visual[i][1]=closestIndex[1]*2 self.visual[i][2]=closestIndex[1]*2 if params:get(i.."rate reverse") == 1 then - self.visual[i][4]=15 + self.visual[i][3]=15 end end end