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

Preplace update #718

Open
wants to merge 2 commits into
base: 2.0-beatmode-retired
Choose a base branch
from

Conversation

bgold-cosmos
Copy link
Contributor

Updating and reinstating the preplace family of functions, including prrw protate preplace <~> ~>> etc.

@yaxu
Copy link
Member

yaxu commented Sep 29, 2020

(The Travis fail seems unrelated.)

I'm a bit unsure about how valuePattern is treated as a list rather than a pattern. Wouldn't it be better to just pass a list there, maybe using varargs for nicer syntax? https://www.youtube.com/watch?v=u_5Ldi2YtGM

@bgold-cosmos
Copy link
Contributor Author

Would the varargs usage make it transparent for the user? I'll have to look into that. One of the nice things about the preplace functions is that even though they bake in some list-like behavior, they mostly feel natural and easy to use live. I think people work with Patterns a lot more than lists.

@bgold-cosmos
Copy link
Contributor Author

Coming back to this...

So the convenience of the Pattern notation means it's easy to do something like this with preplace:

t> rotL 0 $ preplace (1,1) "1 0 1" (s "a:0 b:0 c:1")
(0>⅓)|n: 0.0f, s: "a"
(⅔>1)|n: 0.0f, s: "b"
t> rotL 1 $ preplace (1,1) "1 0 1" (s "a:0 b:0 c:1")
(0>⅓)|n: 1.0f, s: "c"
(⅔>1)|n: 0.0f, s: "a"

Your objection is absolutely correct - we're not really using s "a:0 b:0 c:1" as a Pattern here, we really are using it as a list. The tricky part is that it's a list of type [ControlMap], and Tidal doesn't really have a concise syntax for building such a thing (the best I can come up with is [singleton "s" (VS "a" Nothing) `union` singleton "n" (VF 1 Nothing), ... ] which is a bit lengthy). TIdal of course does have plenty of nice syntax for building ControlPatterns. So while it's kind of an abuse to use that only to then coerce it into a list, the only alternatives I see are:

  • have the preplace family just coerce Patterns into lists (i.e. the current functionality)
  • like the above, but force it to be explicit: preplace (1,1) "1 0 1" (toList $ s "a:0 b:0 c:1")
  • build an entire new set of functions so lists of ControlMaps can be built concisely, even though most of these would presumably be duplicating functionality that already exists for Patterns (ugh)
  • not have preplace at all (but it seems like it fills a niche that's otherwise hard to duplicate)
  • ???

Maybe having an explicit toList is the best? There's already deconstruct but it seems to be doing something a little different.

@yaxu yaxu added this to To do in Tidal 1.7.5 Dec 11, 2020
@yaxu
Copy link
Member

yaxu commented Dec 12, 2020

OK I see the problem with making lists of controlmaps now, but don't really understand preplace itself. preplace (1,1) "1 0 1" (s "a:0 b:0 c:1") takes two patterns, both with three elements in per cycle but returns a pattern with two elements per cycle.. how so?

Also what's the significance of the values in "1 0 1" - are they ignored?

Agreed that stepwise combination of patterns would be really amazing to have back in tidal, but would like to make sure that there isn't some tweaking of the underlying model that could make it a lot more flexible.

The first parameter (1,1) has to be there because the patterns don't know their own period right?

@yaxu yaxu moved this from To do to In progress in Tidal 1.7.5 Dec 12, 2020
@bgold-cosmos
Copy link
Contributor Author

It works somewhat like struct, in that the first Pattern (the "1 0 1") is a Pattern Bool. The difference is that the second pattern is treated as a list, and values from that list are put into the True values of the Pattern Bool in order.

And yes, the (1,1) is the number of cycles of the Bool and "list-like" Pattern to use.

This particular example is also similar to something fit does, it's almost

fit 2 ["a", "b", "c"] "0 ~ 1"

But since fit explicitly takes lists, it's difficult to work the ControlMap stuff in there.

@yaxu
Copy link
Member

yaxu commented Dec 18, 2020

Ok so how about we imagine we had mini-notation syntax to produce a pattern of lists, would that help? We kind of have this with :.

place :: Pattern Bool -> Pattern [a] -> Pattern a

fast 2 $ place "t f t" $ s "a:b:c"
|a bc a|b ca b|

fast 2 $ place "t f t" $ s "a:b:c x:y:z"
|a bc a|y zx y|

The above would require the params being able to produce both Pattern Control and Pattern [Control], that might be possible.. It also needs place to know how many items per cycle are in the boolean pattern.. It'd need to calculate all the elements in a cycle for that, but I don't think that'd be too heavy.

@bgold-cosmos
Copy link
Contributor Author

I'm not sure I understand what place is doing, wouldn't it make more sense for the second to evaluate to
|a yc x|b za y|

And what happens if you use lists of differing lengths, would it be

fast 2 $ place "t f t" $ s "a:b:c x:y:z:w"
|a yc w|b ya w|

@yaxu
Copy link
Member

yaxu commented Dec 18, 2020

Yes true, I guess I meant this:

fast 2 $ place "t f t" $ s "<a:b:c x:y:z>"
|a bc a|y zx y|

Do you think this sort of approach would cover what preplace does now?

@yaxu
Copy link
Member

yaxu commented Dec 18, 2020

It's a bit more complicated without the <>, but I'd expect:

place "t f t" $ s "<a:b:c x:y:z:w>"
|a bc a|z wx y|c ab c|z wx y|b ca b|z wx y|

@yaxu
Copy link
Member

yaxu commented Feb 2, 2021

This seems related: #750

@bgold-cosmos
Copy link
Contributor Author

OK, coming back to this...

So if I'm thinking about this correctly, if a mini-notation were to produce a pattern of lists with ":", then that would have to happen in the pattern parser. Right now ":" is kind of special in that it actually gets handled at a later stage, with the grp function. And grp itself would have to be redone, as I think it would have a different type

grp :: [String -> ControlMap] -> Pattern [String] -> ControlPattern

But otherwise I think it's quite possible?

@bgold-cosmos
Copy link
Contributor Author

Meant to comment earlier - I did take a look at modifying the parser but got a bit stuck on the seeming need for a new "atomic" type to handle Patterns of lists and how to implement it without tearing everything apart. Meanwhile, preplace might still be nice to have back again... (the Travis fails seem to be unrelated cabal issues)

@yaxu
Copy link
Member

yaxu commented May 4, 2021

I'm keen to find a way forward that avoids treating patterns as lists though, it seems like there's a nice opportunity to make things more flexible. For example as you point to, the current grp function isn't ideal, as you can't do things like sound "bd:<0 3>".

I'I think it might be surprisingly easy to generalise some bits of tidal so that the same functions works on both [ControlMap] and Pattern ControlMap. E.g. the type of speed :: Pattern Double -> ControlPattern could be generalised to speed :: Functor a => a Double -> a ControlMap without changing any definitions - just the types. It uses pF :: String -> Pattern Double -> ControlPattern which is really pF :: Functor f => k -> f Double -> f (Map k Value).

Looking at |+|, it should already work on lists of controlmaps: (|+|) :: (Applicative a, Num b) => a b -> a b -> a b

The new stateful events from #750 offers up a different approach to isorhythms too.

@bgold-cosmos
Copy link
Contributor Author

OK, that's interesting... listToPat already exists, and patToList is fairly straightforward:

patToList p = value <$> (flip queryArc) (Arc 0 1)

(could be generalized for longer Arcs)

which means you can do something sort of preplace-like with

let a = patToList (s "bd sn cp")
    b = patToList (speed "1 2")
    n = lcm (length a) (length b)
    in
slow (fromIntegral $ n `div` length a) $ listToPat $ take n $ zipWith Map.union (cycle a) (cycle b)

Certainly could be wrapped in something easier to use.

One issue is "rests" and how to represent them in lists, patToList (s "a ~ b") evaluates to the same thing as patToList (s "a b"), but that can probably be fixed with a fancier patToList.... I'll keep working on it

@yaxu
Copy link
Member

yaxu commented May 6, 2021

patToList is straightforward to make but it's also something I'd like to avoid basing Tidal functions on. I think patterns and lists are different worlds, really. You can convert a list to a pattern with contiguous events, but can't convert a pattern to a list without losing information, as the example of rests shows.

Here's a different implementation though:

patToList pat = value <$> concatMap (\i -> queryArc pat (Arc i (i+1)))  [0 ..]

It returns an infinite lazy list so you don't have to don't have to specify a number of cycles. Then you can just take the number of values you want:

take 8 $ patToList ("1 2 3?" :: Pattern Int)
> [1,2,1,2,1,2,3,1]

@yaxu
Copy link
Member

yaxu commented May 6, 2021

Then this works:

d1 $ sound "numbers(3,8)" # nTake "foo" (patToList "1 2 3?")

@bgold-cosmos
Copy link
Contributor Author

OK, I definitely need to dive into the new stateful stuff; I don't quite understand nTake

@yaxu
Copy link
Member

yaxu commented May 6, 2021

The stateful stuff is a WIP really! But promising.. You have to name the state explicitly, which is what "foo" is. Then the 'take' functions read from the given list, consuming a value for each event. Essentially you're creating a pattern of functions that manipulate state, which means that reversing a stateful pattern, e.g.

d1 $ rev $ sound "numbers(3,8)" # nTake "foo" (patToList "1 2 3?")

... will reverse the structure, but not the values.

@bgold-cosmos
Copy link
Contributor Author

Infinite lists seem to clog up pStateList so that the state can't be further updated, might need a lazy map?

@yaxu
Copy link
Member

yaxu commented May 7, 2021

Ah yes, forgot about that bug.. :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Tidal 1.7.5
In progress
Development

Successfully merging this pull request may close these issues.

None yet

2 participants