Tidal icon indicating copy to clipboard operation
Tidal copied to clipboard

Pattern String doesn't work with special chars like "-" or "#"

Open thgrund opened this issue 1 year ago • 4 comments

I was wondering why there is this restriction to not use i.e. "-" or "#" for a Pattern String:

"-12" :: Pattern String

Is this intended or a bug? Or should be possible to use "-" but not "#"? Why are these restrictions for a Pattern String?

And is there a way to customize this? To create a pattern directly with pure and VS or to use cS with those characters is not a problem at all:

filterJust $ pure $ getS (VS "-asd")
-- (0>1)|"-asd"

I am trying to achieve something like this:

"[-1#sd 3#it]" :: Pattern String
-- (0>1/2) | "-1#sd"
-- (1/2>1) | "3#it"

I use something similar to parse a string and use custom identifiers to apply custom functions on an other pattern. Making these custom identifiers usable within a pattern would be really helpful.

thgrund avatar Mar 02 '23 18:03 thgrund

On some level this is intended, since the original use of a Pattern String is to indicate the name of a folder to search inside for samples, and restricting the allowable characters makes sure things work across different OSes and filesystems.

Also, isn't there already ur for using custom identifiers to apply functions on a pattern? I'm not sure if that's exactly the same as what you're doing but it might be something to look into.

bgold-cosmos avatar Mar 02 '23 20:03 bgold-cosmos

Hey @bgold-cosmos and thanks for reaching out! I guess this is the question. Is it intended to use a Pattern String for something else than sample name? Or maybe there is another approach for what I try to achieve. But unfortunately ur is not the solution. But yes I mean maybe I need a new type for my custom stuff instead of extending the Pattern String.

But for a little bit more context. I am creating a custom interface to create chords and melodies based on roman numerals. And this works pretty well! Then I wanted to apply some concepts of western music theory like secondary dominants and tritone substitution. And this works pretty well too!

But I reached a point where it becomes harder to scale and maintain and I became a little bit unflexible when using this concept. Because I used a String, parsed it and created new Patterns with this. But this prevented me to use functions on the roman numerals and as I said it made it harder to maintain (Originally I thought this is just a POC but I am really happy with the results).

This is a working example:

s1 = sheet {key= "e5", mode="major", numerals = "<1 8#dt 2 5 1 0 -1 -2>"}
d1 $ s "superpiano" <| prog s1 "[1,3,5,8]"

But the numerals field is just a String that will be parsed again. This means this is not working

s1 = sheet {key= "e5", mode="major", numerals = rev $ "<1 8#dt 2 5 1 0 -1 -2>"}
d1 $ s "superpiano" <| prog s1 "[1,3,5,8]"

This ended up in this little system here btw: https://github.com/thgrund/mrreason-setup/blob/main/README.md

thgrund avatar Mar 03 '23 09:03 thgrund

Well, I think one way to do it might be to explicitly separate out your parsing step from the rest and do that when building the sheet. So, changing the "numerals" part of Sheet to be Pattern Int, and then writing something like

s1 = sheet {key= "e5", mode="major", numerals = rev $ progParse "<1 8#dt 2 5 1 0 -1 -2>"}
d1 $ s "superpiano" <| prog s1 "[1,3,5,8]"

This won't quite work as written because progParse also puts together a bunch of other Patterns in other parts of the record, but I think you could rewrite sheet in a way that allows you to use the record notation for some fields, but still run the parser on your custom string to fill other fields with Patterns.

bgold-cosmos avatar Mar 03 '23 19:03 bgold-cosmos

You made me rethink some of my design decisions. And thank you very much for helping with this.

First of all I think I have two kind of modifications that should happen with the identifiers: mode change and offset.
But using the progParse inside of numerals will only allow me to do the offset calculation. Otherwise the mode field wouldn't makes any sense. I could introduce a new field that only holds the functions that should be applied on the numerals (or the entire result). Then I can apply the functions with the complete record informations during the parsing.

And then in parallel I could simplify the Sheet record to make it more scalable. I could keep the field numerals as a String and create two patterns out of it during the parsing: a Pattern Int for the numerals and a Pattern String for the identifiers. Then with some pattern matching it should be easy to chain and apply new functionality and make it more scalable. So I could trick a little bit when I do not change the Pattern String (because the only thing I really miss is to start with a - char). Basically I think I want to have a type where I mix Pattern Int with Pattern String. Nevertheless the Sheet data type would maybe look like this:

data Sheet = Sheet
  { key :: Pattern Note
  , mode :: Pattern String
  , numerals :: String --I could directly apply the functions if this is a pattern but I could indeed parse it and create two new patterns
  , functions :: Pattern * -> Pattern *
  }

s1 = MyData {
  key = "c",
  mode = "major",
  numerals = "<0 1 -2 3#s>",
  functions =  (rev . fast 2)
}

thgrund avatar Mar 03 '23 22:03 thgrund