tidalcycles / strudel

Web-based environment for live coding algorithmic patterns, incorporating a faithful port of TidalCycles to JavaScript
https://strudel.cc/
GNU Affero General Public License v3.0
677 stars 119 forks source link

Real legato #1142

Open daslyfe opened 4 months ago

daslyfe commented 4 months ago

Legato now changes the note duration relative to the gap between notes.

Screenshot 2024-07-13 at 1 05 44 PM

s("sawtooth").euclid(11,16).legato("<1 .5>")._pianoroll()

second array parameter is "lookahead" which is the number of cycles in the future to query for the next event

s("supersaw").euclid(7,16).legato("1:<1 .15>")._pianoroll()`

geikha commented 4 months ago

I've been thinking about this and I'm worried about the name collision with the original Tidal legato.

How about articulate ?

https://en.wikipedia.org/wiki/Articulation_(music) https://www.image-line.com/fl-studio-learning/fl-studio-online-manual/html/pianoroll_articulate.htm

felixroos commented 4 months ago

I've been thinking about this and I'm worried about the name collision with the original Tidal legato.

How about articulate ?

https://en.wikipedia.org/wiki/Articulation_(music) https://www.image-line.com/fl-studio-learning/fl-studio-online-manual/html/pianoroll_articulate.htm

thinking the same. articulate seems a bit abstract to me.. how about fill?

daslyfe commented 4 months ago

I've been thinking about this and I'm worried about the name collision with the original Tidal legato. How about articulate ? https://en.wikipedia.org/wiki/Articulation_(music) https://www.image-line.com/fl-studio-learning/fl-studio-online-manual/html/pianoroll_articulate.htm

thinking the same. articulate seems a bit abstract to me.. how about fill?

I like fill I think that makes sense and is easy to type :) technically it is legato though and a fill means something different musically. Maybe that’s okay

felixroos commented 4 months ago

I've been thinking about this and I'm worried about the name collision with the original Tidal legato. How about articulate ? https://en.wikipedia.org/wiki/Articulation_(music) https://www.image-line.com/fl-studio-learning/fl-studio-online-manual/html/pianoroll_articulate.htm

thinking the same. articulate seems a bit abstract to me.. how about fill?

I like fill I think that makes sense and is easy to type :) technically it is legato though and a fill means something different musically. Maybe that’s okay

another alternative could be flood

geikha commented 4 months ago

technically it is legato though and a fill means something different musically.

well yeah but in this context it wouldn't mean "a fill" more of like "fill-in the silences"

geikha commented 4 months ago

btw this has a problem, i've run into this in tidal and yeah it replicates in strudel too. the malformed haps list is troublesome when combined with other complex hap-changing functions such as rev:

$: s("supersaw")
  // .euclid(11,16)
  .struct("1 0 0 1 1").fast(2)
  // .beat("0:1:3:4:6:5", 8).fast(2)
  .legato("1").rev()
  ._pianoroll()
daslyfe commented 4 months ago

btw this has a problem, i've run into this in tidal and yeah it replicates in strudel too. the malformed haps list is troublesome when combined with other complex hap-changing functions such as rev:

$: s("supersaw")
  // .euclid(11,16)
  .struct("1 0 0 1 1").fast(2)
  // .beat("0:1:3:4:6:5", 8).fast(2)
  .legato("1").rev()
  ._pianoroll()

hmm yeah im not sure what the solve is for this edge case, it still works if you put rev before legatio at least.. ```$: s("supersaw")

.struct("1 0 0 1 1").fast(2).rev()

.legato("1") ._pianoroll()```

daslyfe commented 4 months ago

I've been thinking about this and I'm worried about the name collision with the original Tidal legato. How about articulate ? https://en.wikipedia.org/wiki/Articulation_(music) https://www.image-line.com/fl-studio-learning/fl-studio-online-manual/html/pianoroll_articulate.htm

thinking the same. articulate seems a bit abstract to me.. how about fill?

looks like fill is a reserved param for the pianoroll, does it need to be?

geikha commented 4 months ago

@daslyfe

hmm yeah im not sure what the solve is for this edge case

here's my solution from the haskell side: a function which, given any event (hap), it encloses it into the original query arc (span), if it shouldn't be there it'll return nothing. in the js side returning null will probably be good enough

encloseHap (Span spanBegin spanEnd) hp@(Hap _ (Just (Span wholeBegin wholeEnd)) _ _)
  | wholeEnd <= spanBegin = Nothing
  | wholeBegin >= spanEnd = Nothing
  | wholeBegin >= spanBegin && wholeEnd <= spanEnd = Just hp  -- fully within
  | wholeBegin >= spanBegin && wholeEnd >  spanEnd = Just hp { part = Span wholeBegin spanEnd }  -- starts within, ends outside
  | wholeBegin < spanBegin  && wholeEnd >  spanEnd = Just hp { part = Span spanBegin spanEnd }   -- starts outside, ends outside
  | wholeBegin < spanBegin  && wholeEnd <= spanEnd = Just hp { part = Span spanBegin wholeEnd }  -- starts outside, ends within
  | otherwise = Nothing

edit: changed function to be more understandable from a strudel perspective

~EDIT: wait it broke again and i don't know why~

geikha commented 4 months ago

ok here's the issue with the lookahead scenario: there are times where some haps will be lost only because their original times weren't on the query. what i did for quantization for tidal doesn't run into this scenario, since what i do there is, for every query span, query the whole cycle (floor and ceiling of the current query span, basically), and then enclose it. if you make sure you always query from the cycle start, you're good to go to then enclose it and have it well-formed

you could also just lookbehind by the same amount you lookahead heh

geikha commented 4 months ago

Other name idea gapless https://github.com/tidalcycles/Tidal/issues/761