Writing custom (patternable) functions which switch based on their input

Hey folks!

I've writing some little utility functions in my BootTidal.hs, which provide a default set of parameters to superfm for each of the 32 classic FM algorithms:

    algo 1 =
      amp1 1 # amp2 0 # amp3 1 # amp4 0 # amp5 0 # amp6 0
      # mod12 0.5 # mod34 0.5 # mod45 0.5 # mod56 0.5 # mod66 0.5
      # feedback 1
    algo 2 =
      amp1 1 # amp2 0 # amp3 1 # amp4 0 # amp5 0 # amp6 0
      # mod12 0.5 # mod22 0.5 # mod34 0.5 # mod45 0.5 # mod56 0.5
      # feedback 1
    -- etc etc etc with `algo _ = id` (or something equivalent of the right type) at the end

The idea being that i can then do eg:

d1 $ n "c4 ef4 g4 c4" # s "superfm" # algo 1

as a set of default settings which i can further tweak by appending.

I was wondering how i might go about rewriting this in a way that was patternable, so that i could do things like algo "<[|1|32|5] 2 7 8>" and have it switch the superfm parameters within the definition of algo above as rhythmic events? I assume i couldn't use the pattern-match syntax above but maybe there's some other way of doing comparisons and if/then logic on a Pattern Integer value?

Is there any way of doing this, or have i fundamentally misunderstood how Tidal works? :smiley:

Thank you very much!

Tim

Oh wait, if Pattern is a Monad, can I just rename my 'non-patterned' definition above to something like amp' which would have type Int -> ControlPattern then simply define amp pat = pat >>= amp'? which would have type Pattern Int -> ControlPattern? It can't be that simple surely! :exploding_head:

I think that would work, but wouldn't be exactly what you want. The default bind will mix the structure of both patterns (by ending up with events that are fragments of the intersection of the timespans).

You want an innerBind here, which isn't explicitly defined in the current release of tidal (but is coming in the next one!). There is an innerJoin, though.

Anyway there's a helper function tParam that will do the lifting for you. So all you need to do is algo = tParam algo' to 'patternify' that first argument.

There's also a function inhabit for solving your problem a different way, you give it a lookup table from keys (strings) to patterns of values, and it returns a function from a pattern of keys to a pattern of values.

Oh amazing, thanks Alex! I'll try tParam first then maybe refactor to use inhabit as it sounds like it already solves the 'structural' part of this. (One thing that consistently amazes me about tidal is that there's pretty much always already some combinator that does exactly the difficult part of whatever i'm trying to do at exactly that point, so thank you very much!!)

Did you ever get this figured, mistertim? I'm new to this, but was watching the toplap_barcelona tutorial on superfm and was wondering if I could do the same thing--set up default algorithms based on the DX7...

hah, I'm still working on it! it's quite verbose, here's an extract from my BootTidal.hs (I haven't yet written out all 32 algorithms because I'm lazy)

    -- My extra superfm utilities
    algo1 =
      amp1 1 # amp2 0 # amp3 1 # amp4 0 # amp5 0 # amp6 0
        # mod11 0 # mod12 0.5 # mod13 0 # mod14 0 # mod15 0 # mod16 0
        # mod21 0 # mod22 0 # mod23 0 # mod24 0 # mod25 0 # mod26 0
        # mod31 0 # mod32 0 # mod33 0 # mod34 0.5 # mod35 0 # mod36 0
        # mod41 0 # mod42 0 # mod43 0 # mod44 0 # mod45 0.5 # mod46 0
        # mod51 0 # mod52 0 # mod53 0 # mod54 0 # mod55 0 # mod56 0.5
        # mod61 0 # mod62 0 # mod63 0 # mod64 0 # mod65 0 # mod66 0.5
        # feedback 1
    algo2 =
      amp1 1 # amp2 0 # amp3 1 # amp4 0 # amp5 0 # amp6 0
        # mod11 0 # mod12 0.5 # mod13 0 # mod14 0 # mod15 0 # mod16 0
        # mod21 0 # mod22 0.5 # mod23 0 # mod24 0 # mod25 0 # mod26 0
        # mod31 0 # mod32 0 # mod33 0 # mod34 0.5 # mod35 0 # mod36 0
        # mod41 0 # mod42 0 # mod43 0 # mod44 0 # mod45 0.5 # mod46 0
        # mod51 0 # mod52 0 # mod53 0 # mod54 0 # mod55 0 # mod56 0.5
        # mod61 0 # mod62 0 # mod63 0 # mod64 0 # mod65 0 # mod66 0
        # feedback 1
    algo3 =
      amp1 1 # amp2 0 # amp3 0 # amp4 1 # amp5 0 # amp6 0
        # mod11 0 # mod12 0.5 # mod13 0 # mod14 0 # mod15 0 # mod16 0
        # mod21 0 # mod22 0 # mod23 0.5 # mod24 0 # mod25 0 # mod26 0
        # mod31 0 # mod32 0 # mod33 0 # mod34 0 # mod35 0 # mod36 0
        # mod41 0 # mod42 0 # mod43 0 # mod44 0 # mod45 0.5 # mod46 0
        # mod51 0 # mod52 0 # mod53 0 # mod54 0 # mod55 0 # mod56 0.5
        # mod61 0 # mod62 0 # mod63 0 # mod64 0 # mod65 0 # mod66 0.5
        # feedback 1

    algo = inhabit [("1", algo1), ("2", algo2), ("3", algo3)]


...that compiles and does something however i'm unconvinced it's quite right yet - the audible changes between the algos are quite subtle (when i don't modify them further) - i'm unclear because this is because the first three are just very similar (at least with the default paramaters i've given them) or because I'm doing something wrong. I'm going to investigate further this week!

The other thing i can't quite get my head around is how this would work mixed with other parameters. For instance if i did something like

d1 $ n "c3 ef3 g3 bf3 c4" # s "superfm # algo "[1|2|3]" # mod12 "<0.3 0.4 0.6>"

would the patterning of mod12 override the implicit patterning of mod12 performed by my patterning of algo? This is all stuff i need to work out in practice i think. I'll let you know how it progresses!

3 Likes