Last active
March 21, 2021 01:34
-
-
Save trentgill/fbc5b32c85b0ca4a4a56df93f98992d1 to your computer and use it in GitHub Desktop.
demonstrating usage of the forthcoming 'sequins' lua library for ordering values
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- sequins. | |
-- a demonstration | |
engine.name = 'PolyPerc' | |
local MusicUtil = require "musicutil" | |
-- TODO operate by reference so the scale can change without recreating the sequin | |
dim = {3,6,9,15,18,21,-6} | |
bass = {-6,-12,-15} | |
lyd = {12,14,16,18,19,9,11,24} | |
function init() | |
local s = sequins --alias for brevity | |
-- create our sequin which interleaves the playback of two tables of notes (from above) | |
local mysequin = | |
s.new( { s.new( bass, 'rand' ) | |
, s.count( 3, s.new( dim, 'next' ) ) | |
} | |
, 'next' ) | |
-- iniates a timebase which will pull a note from the sequin on each step | |
myarp = run_arp( play_note | |
, mysequin | |
, 1/4 | |
) | |
end | |
function play_note(v) | |
v = v + 48 -- shift up to good range | |
local freq = MusicUtil.note_num_to_freq(v) | |
engine.hz(freq) | |
end | |
function run_arp(fn,seq,sync) | |
return clock.run( | |
function() | |
while true do | |
clock.sync(sync) | |
fn( seq() ) | |
end | |
end) | |
end | |
--- TODO this is a new file called sequins.lua | |
sequins = {} | |
-- arg1: value table eg{0,2,4,7,9} | |
-- arg2: behaviour enum:{'next','prev',rand','drunk'} -- n as next# | |
-- retv: function that generates the next note | |
function sequins.new( vals, behaviour ) | |
local ix = 1 | |
local function generate() | |
local newix = 0 | |
if behaviour == 'next' then | |
newix = ix + 1 | |
elseif behaviour == 'prev' then | |
newix = ix - 1 | |
elseif behaviour == 'rand' then | |
newix = math.random(#vals) | |
elseif behaviour == 'drunk' then | |
newix = ix + math.random(-1,1) | |
end | |
-- clamp to vals table length | |
while newix < 1 do newix = newix + #vals end | |
while newix > #vals do newix = newix - #vals end | |
local val = vals[newix] | |
-- currently we explictly step the index forward in some cases but not others | |
-- ideally this would be cleaned up & have a single return point | |
if type(val) == 'function' then | |
local v, action = val() -- allows nested tables | |
if action == 'skip' then | |
ix = newix -- nb: doesn't matter if it's before/after recursion | |
return generate( vals, behaviour ) | |
elseif action == 'again' then | |
-- ix stays the same! | |
else -- was a nested table, but just returned a value (no action) | |
ix = newix | |
end | |
return v | |
else -- assume a number | |
ix = newix | |
return val | |
end | |
end | |
return generate -- named function so we can use recursion inside | |
end | |
-- returns a value on everyth call. otherwise instructs the caller to skip it. | |
function sequins.every( everyth, sequin ) | |
local e = everyth | |
return | |
function() | |
e = e%everyth +1 | |
if e == 1 then | |
return sequin() | |
else | |
return 0, 'skip' | |
end | |
end | |
end | |
-- returns count values in a row, stopping the calling sequin from moving forward until count is exhausted | |
function sequins.count( count, sequin ) | |
local c = count | |
return | |
function() | |
c = c%count +1 | |
if c == 1 then | |
print'match' | |
return sequin() | |
else | |
return sequin(), 'again' | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment