Last active
February 16, 2022 00:16
-
-
Save trentgill/84ec5b68816eb03508566addb5a41dd4 to your computer and use it in GitHub Desktop.
crow v3.1 livecoding & sequencing propositions
This file contains 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
--- new elements for sequencing fun! | |
---------------------------------------------------------------------- | |
---------------------------------------------------------------------- | |
s = sequins | |
-- table call with a string is treated as a table of chars | |
cs = s"abcd" -- equivalent to s{'a','b','c','d'} | |
-- deep copy | |
s1 = s{1,2,3} | |
s2 = s1:copy() | |
-- arithmetic metamethods on sequins | |
s3 = ~s2 | |
s4 = s2 + 1 | |
s5 = s2 - 2 | |
s6 = s2 * 3 | |
s7 = s2 / 12 | |
s8 = s2 % 5 | |
-- permutation focused | |
-- :transform(operation) | |
-- switch on or off / change to a different transform | |
-- want to keep the original sequins as the core reference | |
s10 = s{1,2,3} + s{4,5} | |
-- 5,6,7,6,7,8 -- sequential | |
-- 5,7,7, -- interleaved | |
-- synthesis focused | |
-- patterned transformation (combining multiple sequins) | |
-- :flatten | |
-- print a sequins with it's transformations | |
-- :settable to handle nested sequins | |
s9 = s{1, s{2,3}} | |
s9:settable{1, s{4,5}} -- currently s{4,5} is replaced, not updated | |
---------------------------------------------------------------------- | |
---------------------------------------------------------------------- | |
tl = timeline | |
------------------------------------- | |
--- timeline.loop | |
-- table of beat-duration (clock.sync units), action (functions) pairs | |
tl{duration, action} | |
-- table can be n-pairs long | |
t1{ duration, action | |
, duration, action | |
} -- etc. | |
mykick = tl{1, function() output[1]() end} | |
mykick.stop() | |
-- either or both of duration & action can be sequins | |
tl{ s{2,2,1}, s{kick,hats} } | |
tl{ s"x x x", s{kick,hats} } -- could support this string natively | |
rs = s"x x x" | |
rs = s"x xx" | |
rs = s"x x x " | |
tl{ rs, s{kick,hats} } -- could support this string natively | |
-- action can be a table which will call arg[1] with args[2..n] | |
tl(1, {ii.wsyn.play_note, 0, 2}) | |
-- any action table element (fn and args) may be a sequins | |
tl(1, {ii.wsyn.play_note, s{0,2,4,7,9}:step(2), 2}) | |
sq1 = s{0,2,4,7,9}:step(2) | |
tl(1, {ii.wsyn.play_note, sq1, 2}) | |
-- predicate methods may stop looping | |
-- predicates are fns or sequins | |
tl{}:unless(p) -- loop unless p (or p()) is true | |
tl{}:times(n) -- shortcut for countdown closure in :unless | |
tl{}:once() -- sugar for :unless(true) | |
-- timelines will, by default, wait until clock.sync(1) to start | |
-- use a bigger val for bars, or `0` for no-sync | |
-- [implementation note: might need to precede .loop] | |
tl{}:sync(n) | |
-- quantization | |
-- global vs local | |
tl.quantization(4) -- global | |
------------------------------------- | |
--- timeline.score | |
-- relative-beats instead of duration | |
-- same ability re: n-pairs, sequins, action-tables | |
tl.score{ 0, kick | |
, 1, snare | |
, 3, snare | |
} | |
-- scores can be looped with the special timeline.reset function | |
tl.score{ 0, kick | |
, 64, snare | |
, 256, tl.reset -- reset counter and start again | |
} | |
-- if we have access to sequins arithmetic... | |
stimings = s{0,64,256} | |
tl.score{ stimings, s{kick, snare, s{tl.reset}:every(16)} | |
} | |
stimings = stimings * 2 -- slow down the whole composition | |
-- timeline.reset can take a predicate function for conditional looping | |
tl.score{ 0, kick | |
, 2, snare | |
, 4, {tl.reset, function() return math.random() > 0.5 end} | |
} | |
---------- | |
-- alt .score repetition | |
-- feels like reset as a line item is awkward. can we method? | |
tl.score{ 0, {kick, snare} | |
, 0, snare | |
, 2, snare | |
}:reset(4) | |
-- timeline.reset can take a predicate function for conditional looping | |
tl.score{ 0, kick | |
, 2, snare | |
}:reset(4, {tl.reset, function() return math.random() > 0.5 end}) | |
-- optionally change the start-sync to other than clock.sync(1) | |
tl.score{}:sync(n) | |
-- QUESTION | |
-- what feels most natural to call multiple events at the same time? | |
------------------------------------- | |
--- timeline.stamp | |
-- absolute time version of score (seconds, not beats) | |
tl.stamp{ seconds, action | |
, ... | |
} | |
-- optionally change the start-sync to other than clock.sync(1) | |
tl.stamp{}:sync(n) | |
-- naming?! | |
---------------------------------------------------------------------- | |
---------------------------------------------------------------------- | |
fox = foxdot | |
-- naming? | |
-- collection of (character -> function) mappings | |
-- step-sequence city! | |
-- register events by assigning fox keys to functions | |
fox['+'] = kick -- can just be a function name | |
fox['-'] = snare | |
fox.X = function() print'X' end -- or an anonymous fn | |
fox['9'] = function() for i=1,4 do output[i]() end end -- key must be a char! | |
-- ^ this is the character '9' not the number 9 | |
-- integeration with timeline | |
tl{1, {fox, s"+ -- "}} | |
-- eg: swinging step sequencer | |
d1 = tl{s{1,2}, {fox, s"+ -- "}} | |
---------------------------------------------------------------------- | |
---------------------------------------------------------------------- | |
hs = hotswap | |
-- problem: | |
s1 = s{1,2,3} | |
s1() --> 1 | |
s1 = s{4,5,6} -- will reset to first element | |
s1() --> 4, but we want 5 | |
-- current workaround: | |
s1 = s{1,2,3} | |
s1:settable{4,5,6} -- but uses different syntax | |
-- proposed solution: | |
hs.s1 = s{1,2,3} -- put the sequins in the hotswap table | |
s1() --> 1 | |
hs.s1 = s{4,5,6} | |
s1() --> 5, now we maintain the index & just swap the data | |
-- same for timelines (maintain reference beat) | |
-- support anonymous sequins inside timelines | |
-- eg foxdot | |
hs.f1 = s"+ -- " | |
hs.f1 = tl{1, {fox, s.f1}} | |
hs.f1 = s"+ +- " -- modify the sequins on the fly | |
hs.f1 = s"X -- --" | |
--------------- | |
//////////// recap | |
send the executable script to the gang. | |
sequins arithmetic operators | |
- have a :transform() method to apply changes | |
- want to maintain focus on the original sequins | |
- the transform can be flattened with :flatten() or :copy('flatten') | |
- transforms are themselves objects which can be copied / swapped | |
livecode helpers | |
- when :methods are called, print a record of the effect | |
- better introspection to visualize what your code is doing | |
-> answers: is my code doing what i want it to? | |
-> helps develop trust in the tool more rapidly | |
- switchable (globally?) so it's not too noisy | |
permutations | |
auto-completion in REPL (ie ii.jf.<tab> displays all jf functions) | |
tl.score | |
- great at addressing a weakpoint of norns too | |
- currently it's hard to do this stuff without manually managing indexing | |
launch-quantization (aka :sync) | |
- another weakpoint of norns / crow | |
- key to be able to say "at this point in the future" do a thing | |
- could be global or diff syntax form for delayed start | |
record events as they happen so they can replayed | |
- assume that the thing i'm doing with code is a "performance" | |
- this is an instrument | |
- typing in a repl is equivalent to playing midi keyboard | |
- so why can't i record them both! | |
just flip a record flag and commit every event to a timeline!! | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment