Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

zebra - vorpal #15

Merged
merged 2 commits into from Mar 2, 2020
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -0,0 +1,163 @@
-- manage abstractions over softcut API
-- significantly, there are three stereo "voices"
-- each of these is a pair of softcut voices with indicies [i, i+3],
-- pointed at opposite buffers, but sharing positional parameters

local sc = softcut

-- dedicated tables for start/end of each world
world_start = {}
world_end = {}

-- loop length per voice,
-- expresed in terms of the current world's fundamental period
voice_loop_len_ratio = { 64, 64, 64 }
-- same, stored as actual duration
voice_loop_len = {}
-- save postions after adding
voice_pos_scaled = { 0, 0, 0 }

for i=1,3 do
local k = world_keys[i]
local r = worlds[k].ratios[2]
voice_loop_len[i] = r[1] / r[2]
end

-- stereo width. 1 means voice pair is panned hard L/R
voice_width = { 1, 1, 1 }

function set_voice_width(i, v)
voice_width[i] = v
sc.pan(i, v)
sc.pan(i+3, v * -1)
end

local load_files = function()
local pos = 0
local i = 1
for i,k in ipairs(world_keys) do
local file = _path.code .. "nc01-drone/lib/" .. k .. ".wav"
local _, frames, _ = audio.file_info(file)
local dur = worlds[k].dur
sc.buffer_read_stereo(file, worlds[k].start, pos, dur)
world_start[i] = pos
world_end[i] = pos+dur
pos = pos + dur
i = i+1
end
end

-- set a single voice to a given world
-- this updates tonal and positional tendencies
set_voice_world = function(i, z, start_off, end_off)
local zs = world_start[z] + start_off
local ze = world_end[z] + end_off
sc.loop_start(i, zs)
sc.loop_start(i+3, zs)
sc.loop_end(i, ze)
sc.loop_end(i+3, ze)
local hz1 = worlds[world_keys[z]].key_hz
local hz2= hz1
local r = worlds[world_keys[z]].ratios
if (i>1) then
hz1 = hz1 * r[i-1][1] / r[i-1][2]
hz2 = hz2 * r[i+1][1] / r[i+1][2]
end
while (hz1 > 200) do hz1 = hz1 / 2 end
while (hz2 > 300) do hz2 = hz2 / 2 end
sc.post_filter_fc(i,hz1)
sc.post_filter_fc(i+3,hz2)
end

update_voice_loop = function(i)
local pos = voice_pos_scaled[i]
-----
-- fixme: could memoize
local hz = worlds[world_keys[which_world]].key_hz
local loop_dur = 1 / hz * voice_loop_len_ratio[i]
voice_loop_len[i] = loop_dur
----
local le = pos + loop_dur
local ze = world_end[which_world]
if le > ze then le = ze end
fade = loop_dur / 4
sc.loop_start(i, pos)
sc.loop_end(i, le)
sc.fade_time(i, fade)
sc.loop_start(i+3, pos)
sc.loop_end(i+3, le)
sc.fade_time(i+3, fade)
end

-- set position of single voice
-- also updates loop start point
set_voice_pos = function(i, pos)
local zs = world_start[which_world]
local ze = world_end[which_world]
local scaled_pos = zs + pos*(ze-zs)
voice_pos_scaled[i] = scaled_pos
update_voice_loop(i)
sc.position(i, scaled_pos)
sc.position(i+3, scaled_pos)
end

cut_set_world = function(z)
for i=1,3 do
set_voice_world(i, z, i, -2*i)
set_voice_pos(i, voice_pos[i])
end
end

cur_voice_rate_idx = { 1, 2, 3 }
voice_to_advance = 1
cut_advance_voice_rate = function()
local i = voice_to_advance
cur_voice_rate_idx[i] = cur_voice_rate_idx[i] + 1
if cur_voice_rate_idx[i] > 4 then cur_voice_rate_idx[i] = 1 end
local ratio = worlds[world_keys[which_world]].ratios[cur_voice_rate_idx[i]]
sc.rate(i, ratio[1] / ratio[2])
sc.rate(i+3, ratio[1] / ratio[2])
i = i + 1
if i > 3 then i = 1 end
voice_to_advance = i
end

begin_cuts = function()
load_files()

for i=1,6 do
sc.enable(i, 1)
sc.level(i, 0.5)
sc.loop(i,1)
sc.rate(i,1.0)
sc.play(i,1)
sc.rec(i,1)
sc.rec_level(i,0)
sc.pre_level(i,1)
sc.post_filter_dry(i,0)
sc.post_filter_lp(i,0.5)
sc.post_filter_hp(i,0)
sc.post_filter_bp(i,0.25)
sc.post_filter_br(i,0)
sc.post_filter_rq(i,1/12)
end

for i=1,3 do
sc.buffer(i, 1)
sc.buffer(i+3, 2)
sc.pan(i, -1)
sc.pan(i+3, 1)
end

sc.level_cut_cut(1, 4, 1)
sc.level_cut_cut(2, 5, 1)
sc.level_cut_cut(3, 6, 1)
sc.level_cut_cut(4, 1, 1)
sc.level_cut_cut(5, 2, 1)
sc.level_cut_cut(6, 3, 1)

cut_set_world(which_world)

end


@@ -0,0 +1,113 @@

function begin_hands()

----------------
----feed
feed_max = 128
params:add_number("feed", "feed", 0, feed_max)
params:set_action("feed", function(feed)
local x, rec, pre
x = feed/feed_max
pre = (math.cos(x * math.pi) + 1) / 2
rec = (math.cos((x+1) * math.pi) + 1) / 2
for i=1,6 do
softcut.rec_level(i,rec)
softcut.pre_level(i,pre)
end
end)

--------------------
--- bright
bright_max = 128
params:add({type="number", id="bright", name="bright",
min=1, max=bright_max, default=0,
action = function(bright)
local br = 0
local bm_2 = bright_max/2
local lp = (bright_max - bright)/bright_max
local bp = ((bm_2)-math.abs(bright-(bm_2)))/bm_2 + 0.25
if bp > (1-lp) then bp = (1-lp) end
local dry = 1 - (lp+bp)
lp = lp * 0.8
bp = bp * 0.8
br = br * 0.8
rq = math.pow(2, ((1-bright)/bright_max)*4)
if rq > 1 then rq = 1 end
if rq < 0.041666 then rq = 0.041666 end
local w -- width per voice
for i=1,3 do
w = math.cos((i*0.25 + 1)*math.pi)
set_voice_width(i, w)
end
for i=1,6 do
softcut.post_filter_dry(i, dry)
softcut.post_filter_lp(i, lp)
softcut.post_filter_bp(i, bp)
softcut.post_filter_br(i, br)
softcut.post_filter_rq(i, rq)
end

end})

--------------------
---- dense
-- density affects loop length (as a ratio of key hz)
---- with minimum density, all ratios are medium high
----- with max density, two voices loop fast (in audio range)
------ and one loops very slow..

dense_max = 128
params:add({type="number", id="dense", name="dense",
min=1, max=dense_max, default=1,
action = function(dense)
local ridx = { dense % 4, (dense+2) % 4, (dense+5) % 4 }
local r = tab_map(ridx, function(idx)
local pair = worlds[world_keys[which_world]].ratios[idx+1]
return pair[1] / pair[2]
end)

local mul = {
math.pow(2, 9 - (dense / dense_max * 6)),
math.pow(2, 8 - (dense / dense_max * 6)),
math.pow(2, 7 + (dense / dense_max * 4))
}

for i=1,3 do
voice_loop_len_ratio[i] = r[i] * mul[i]
update_voice_loop(i)
end
end})
end

delta_volume = function(z)
params:delta("feed", z)
end

delta_bright = function(z)
params:delta("bright", z);
end

delta_dense = function(z)
params:delta("dense", z);
end

local evolve_count = 1
press_evolve = function(z)
for i=1,evolve_count do
cut_advance_voice_rate()
end
if evolve_count == 1 then
evolve_count = 2
else evolve_count = 1
end
end

local world = 1
press_world = function(z)
if z == 1 then
world = world + 1
if world > 3 then world = 1 end
set_world(world)
end
end

@@ -0,0 +1,84 @@

voice_pos = { 0, 0, 0 }
voice_step = { 1, 1, 1 }

tick_count_list = {}
tick_count = { 0, 0, 0 }

cur_pos_ratio = { 1, 1, 1 }
cur_pos_ratio_flip = { false, false, false }

for k,z in pairs(worlds) do
local count
local r = z['ratios']
for i,p in ipairs(r) do
local q -- next ratio, after wrapping
if i > 3 then
q = r[1]
else
q = r[i+1]
end
count = lcm(p[1]+q[1], p[2]+q[2])
tick_count_list[#tick_count_list + 1] = count
end
end

local calc_voice_step = function(i)
local ratios = worlds[world_keys[which_world]].ratios
local num = ratios[cur_pos_ratio[i]][1]
local denom = ratios[cur_pos_ratio[i]][2]
if cur_pos_ratio_flip[i] then
voice_step[i] = voice_step[i] * num / denom
cur_pos_ratio_flip[i] = false
else
voice_step[i] = voice_step[i] * denom / num
cur_pos_ratio_flip[i] = true
end
end

local wrap_voice_step = function(x)
while x < 0.001953125 do
x = x * 2
end
while x > 0.015625 do
x = x / 2
end
return x
end

local apply_voice_step = function(i)
local pos = voice_pos[i] + voice_step[i]
while pos < 0 do pos = pos + 1 end
while pos > 1 do pos = pos - 1 end
--print(i, voice_step[i], voice_pos[i])
set_voice_pos(i, pos)

voice_pos[i] = pos
end

local handle_tick = function(count)
for i=1,3 do
tick_count[i] = tick_count[i] + 1
--print(i, tick_count[i])
if tick_count[i] == tick_count_list[i] then
tick_count[i] = 0
cur_pos_ratio[i] = cur_pos_ratio[i] + 1
if cur_pos_ratio[i] > 4 then cur_pos_ratio[i] = 1 end
calc_voice_step(i)
voice_step[i] = wrap_voice_step(voice_step[i])
apply_voice_step(i)
end
end
end


local tick_base = 1/32
begin_moves = function()
tick = metro.init(handle_tick, tick_base)
tick:start()
end

set_tick_base = function(x)
tick_base = x
tick.time = tick_base
end
ProTip! Use n and p to navigate between commits in a pull request.