monome / crow

Crow speaks and listens and remembers bits of text. A scriptable USB-CV-II machine
GNU General Public License v3.0
166 stars 34 forks source link

hotswap library #456

Closed trentgill closed 1 year ago

trentgill commented 3 years ago

simple library oriented toward live-coding, and specifically addressing the desire to re-run a line of code numerous times to change data piecemeal.

take the following example:

s = sequins
s1 = s{1,2,3}
print(s1) --> 1
print(s1) --> 2
s1 = s{4,5,6}
print(s1) --> 4

see how when we re-assigned s1, the sequins is reset to it's first stage. this makes sense as we are creating a new sequins and replacing the old one with the new. sometimes we want to keep the index into the sequins, and just modify the data sightly (perhaps 1 note was off, and we're already using the sequins in a metro somewhere):

s = sequins
-- current solution
s1 = s{1,2,3}
s1:settable{4,5,6} -- requires different syntax

-- or use the hotswap library
hs = hotswap
hs.s1 = s{1,2,3}
hs.s1 = s{4,5,6}

behaviour of the 2 above solutions is identical. the difference is that the 2nd version doesn't require changing the syntax at all. hotswap is a special table that does some extra checks & balances whenever setting a value in the table. specifically it's for handling objects with internal state. at present that means sequins and timeline.

take this example of an arpeggiator, where we gradually morph the timeline in various ways, each without missing a beat or resetting any of the scales:

hs = hotswap
tl = timeline
s = sequins
hs.mytl = tl.loop{ s{3,3,2}, {ii.wsyn.play_note, s{0,4,7,11}, 2.0 }
hs.mytl = tl.loop{ s{3,3,2}, {ii.wsyn.play_note, s{0,4,7,11}, 1.5 } -- reduce volume
hs.mytl = tl.loop{ s{3,3,2}, {ii.jf.play_note, s{0,4,7,11}, 1.5 } -- switch to just friends instead of w/
hs.mytl = tl.loop{ s{3,3,2}, {ii.jf.play_note, s{0,3,7,10}, 1.5 } -- switch to minor 7th
hs.mytl = tl.loop{ s{3,2,3}, {ii.jf.play_note, s{0,3,7,10}, 1.5 } -- reorganize rhythm
hs.mytl = tl.loop{ s{3,2,3}, {ii.jf.play_note, s{0,3,7,s{10,14}}, 1.5 } -- add nested sequins to scale
hs.mytl = tl.loop{ s{3,2,3}, {ii.jf.play_note, s{0,3,7,s{10,15}}, 1.5 } -- change upper harmony

while the result is very wordy when written out as a block, this is designed for use in a text editor where you can re-evaluate a line of code at a time (LINK for howto). thus all the changes made above would be iterative modification of the line of text, and then re-running that line.

implementation

the way the library works is a very basic shim which just captures assignments & forwards everything through the internal hotswap and settable methods of timeline & sequins respectively. as a result, any bugs that are found are likely not the fault of hotswap, but the implementation of those methods.

the new sequins-transformer PR includes a number of fixes & extensions for sequins.settable. the timeline.hotswap function is only loosely tested currently, and while it works on a basic level, i haven't explored complex nesting of different features (beyond a sequins in an action-table). there is likely some bugs & edge-cases not handled.

extending

this library is intended to be extended as other stateful data structures ("classes"?) are identified that could benefit from live-piecemeal-modification. there may be a bunch of these on norns that i don't know about. on crow, ASL could potentially use this feature, but it's likely a very big undertaking as it'd have to interact with the C-layer.

trentgill commented 1 year ago

merging this as it's a very small and simple library. any bugs found are very likely the fault of sequins:settable or timeline:hotswap.