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

block based evaluation #34

Open
felixroos opened this issue Apr 10, 2022 · 6 comments
Open

block based evaluation #34

felixroos opened this issue Apr 10, 2022 · 6 comments
Labels
compatibility Mainline Tidal Compatibility feature completely new feature

Comments

@felixroos
Copy link
Collaborator

felixroos commented Apr 10, 2022

Think about if we should implement block based evaluation, like in mainline tidal.
Currently, the whole code is reevaluated when hitting ctrl+enter.
To make that work, some way of naming patterns needs to be found. One idea is using the rarely used javascript label statements:

d1: s("bd sn")

d1: s("bd ~")

If we run the fist line, and then the second, only the second should play.

To make that work, the runtime needs to store patterns in some map that stores { [name]: pattern }.
When a line is evaluated, the pattern map is updated.
After each evaluation all patterns in the map are stacked together and queried.

One question is when non pattern code should be evaluated? Example:

const p = "x ~ x*2"

d1: "c3 e3 g3".struct(p)

Here, d1 references p, so we need to have that evaluated too.
It would make usage much more complicated if the user is expected to run the first line before the second otherwise it crashes.

@felixroos felixroos added enhancement improves an existing feature compatibility Mainline Tidal Compatibility labels Apr 24, 2022
@felixroos
Copy link
Collaborator Author

@felixroos
Copy link
Collaborator Author

before it gets forgotten, from chat:

yaxu:
With the usual tidal editor plugin approach, blocks are pasted into a repl. it makes it more of a conversation but it's difficult to keep aware of which code is being run
with the strudel approach it's just the final line that brings things together. If there's mininotation in play it's pretty obvious which bits are running but it's a bit awkward having to scroll to this line at the bottom being in charge of everything
The feedforward approach is more of a structural approach, where blocks automatically get tagged with numbers and can be switched on and off with a shortcut. You also get a VU meter per block. You can only write patterns though, not e.g. definitions of a new function to use
With tidal editor plugins you have to do the work of assigning numbers to patterns and it's really easy to accidentally replace a running pattern that you didn't want to. Unexpected breakdowns can be nice, but..
Another approach would be tiling multiple editors and having one editor per tile. Siren did something like this but mixed with a old school tracker paradigm https://toplap.org/2017/04/12/siren/
It could be interesting to have a semi-structured approach like if you did ctrl-enter on s("bd sd") it would be transformed into s("bd sd").send(3) , where '3' is a unique number. Then you could toggle it with ctrl-3 and it'd toggle between that and s("bd sd").mute(3).
this could be called a 'performance mode'
the send(3) would do the work of adding the pattern to be scheduled

froos:
I thought about evaluating all code by default, but if you have a named block, the evaluated block wins and the rest is ignored. this would make sure variable declarations etc are evaluated

drums: s("bd sd")

bass: note("g1 c2(3,8)")

drums: s("bd sd*2")

if you press play the default behavior could be to take the first block
now if you evaluate the last block, the evaluator will search for blocks with the same name and cut them out
this is valid js btw

yaxu:
one thing is what happens if there is a syntax error somewhere.. in feedforward iirc everything else still gets evaluated and updates. In tidal it doesn't matter as you only eval one block at a time
if everything has to be valid for anything to be edited that would be a sticking point when live coding

froos:
true.. another idea i had was to use the evaluated block as a starting point and search for variable references within the ast of that block and evaluate everything that matches.. this is the spreadsheet approach but it involves basically writing a subset of a js runtime
that means only the block and potential references must be valid
or we just throw an error if you haven't evaluated the variable declaration yet..
but a thing i really would like to keep is that you can just press play and the music starts. This is really important for beginners

@yaxu yaxu mentioned this issue Mar 5, 2023
@felixroos felixroos added feature completely new feature and removed enhancement improves an existing feature labels Mar 29, 2023
@felixroos
Copy link
Collaborator Author

this little algorithm could be used for block detection:

https://codesandbox.io/s/block-detection-p8zozv?file=/src/index.js

@felixroos
Copy link
Collaborator Author

more fleshed out, using codemirror + adjusted flash logic + added gutter lines to show individual blocks: https://codesandbox.io/s/block-based-evaluation-codemirror-7fu5vo?file=/src/codemirror.js

@felixroos
Copy link
Collaborator Author

maybe just use:

s("bd").p(1)
s("bd").p(2)

analogous to tidal. there could also be the d functions:

s("bd").d1
s("bd").d2

with global evaluation, patterns with the same number would always win when they are last. example:

note("a").p(1)
note("b").p(1)

here, note("b") would win. BUT: why not use the cursor position at eval time to edit the code accordingly, allowing to reflect the active pattern state in the code. assuming the above code is evaluated with the cursor in the first line, the code could change to:

note("a").p(1)
note("b").q(1) // <-- this line is changed automatically

the .q could mean that that pattern is quiet, still indicating it belongs to group 1. If now the second line is evaluated, the code changes to:

note("a").q(1)
note("b").p(1)

There could be additional shortcuts / ui elements to switch patterns on and off, the crucial part is just that the state that contains the info which patterns are on and off is saved to the code itself.

A piece of code can then also be evaluated globally without a cursor info and it would still reflect the correct playback state.

This approach might still be problematic when the code as a whole contains syntax errors (with collaborative editing the same document in mind) but maybe it's at least a step in the direction of better live codability + compatibility with tidal.
It can also be backward compatible, as the old behavior (use last expression as the pattern to use) can kick in when no p functions are used.

@felixroos
Copy link
Collaborator Author

above idea is now implemented in #805 (without code transforms)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compatibility Mainline Tidal Compatibility feature completely new feature
Projects
None yet
Development

No branches or pull requests

1 participant