How does trigger's implementation work? (Is it possible to have patterns react to each other?)


I was wondering if anyone would be willing to walk me through how trigger works inside.

trigger :: Show a => a -> Pattern b -> Pattern b
trigger k pat = pat {query = q}
  where q st = query ((offset st) ~> pat) st
        offset st = fromMaybe (pure 0) $ do pat <- Map.lookup ctrl (controls st)
                                            return $ ((fromMaybe 0 . getR) <$> pat)
        ctrl = "_t_" ++ show k

The reason I’m curious is that it seems to allow a pattern running on d1 to interact with a pattern running on d2 by providing a different integer to the first argument. I would like to be able to have the patterns I’m writing interact with one another more. Fore example, say I write a drum pattern that I’m happy with and its fairly complex, and I want to have the bass hit only when the snare hits. If the bass pattern is on d2 and the drums on d1 I have to work out how to program a pattern of bass hits that sounds only when the snare does. What I would like to be able to do is have a function similar to fix act on the bass pattern whenever d1 has a snare event.

Does that make any sense?



Here is some additional context on what I would like to be able to do. In hydra, I can send a visual pattern to an output (ie o0). That pattern can then reference its own output with src(o0), creating feedback. I would like to be able to create similar structures in tidal. If I have a pattern on d1 that has a note parameter, I would like to be able to access this patterns note parameter from within the pattern or from within another pattern.

To imagine what this might look like:

d1 $ note ("1 2 3 4" + (src 1 note)) #s "gtr"

Unfortunately you can’t have feedback like this in Tidal. However in the latest release there is experimental support for shared variables, which you can use to make rhythmic interactions between parts. E.g. lets set up a getter and setter for something called metre:

let setI = streamSetI tidal
    setF = streamSetF tidal
    setS = streamSetS tidal
    setR = streamSetI tidal
    setB = streamSetB tidal

let metre = cB True "metre"
    setMetre = setB "metre"

Then you can do things like this:

d1 $ struct metre $ sound "bd"

d2 $ struct (inv metre) $ sound "cp" # speed 2

setMetre "t(3,8)"

I think you can achieve interaction close to what you want to this way. Being able to dial-up parameters from other patterns like you describe is a nice idea and could be possible though, worth creating an issue for it! What I think wouldn’t be possible is having two patterns referring to each other, with the way Tidal works that’d be an infinite loop and would cause a crash if it were allowed.

Thanks alex! I think that is a huge piece of what I’m be looking for. very cool!

I’ll open an issue up for the feedback think just in case there is some way to describe it in tidal patterns that doesn’t cause explosions.

Thanks again!

– EDIT –

I went back and played with the setter methods you outlined. I think you can make feedback with those? I tried making a custom choose method where the number doing the choosing is referencing itself.

let setI = streamSetI tidal
    setF = streamSetF tidal
    setS = streamSetS tidal
    setR = streamSetI tidal
    setB = streamSetB tidal
    choice = cF 1 "choice"
    setChoice = setF "choice"
    myChoose = chooseBy choice
    myCycleChoose = segment 1 . chooseBy choice
    myWChoose = wchooseBy choice
do {
  setcps 0.58;
  setChoice (
    slow 4 $
    range 0 1 $
    0.5*sine + 0.5*cos(slow (myChoose [1.5,2,3,0.5]) $
  $ (# up (myChoose [0,2,4,7,11,5,4,9,14]))
  $ fast "<1 1 1 1 2 4 1 1 1 3>"
  $ slow 4
  $ degradeBy 0.3
  $ every 3 (hurry 2)
  $ struct "<t*16 t*16 t*16 <t*12 t*16>>"
  $ s "modal02"
  # gain (range 0.7 1.1 $ rand)
  # room 1.2
  # orbit 3

Ah that’s interesting, each evaluation makes use of the previous version? If you run this a few times you might see cpu go up!

I think when I wrote it I thought it would be constantly re-evaluateing in the background. I see now that it will only feedback when executed. Because tidal patterns are functions mapping times to events, this should make the function more expensive to execute the more it references itself as each self reference adds another layer of functions to be executed (I think). Whats odd is that, when I ran the experiment it doesn’t seem to make the cpu climb!? I’m not really sure what the code I wrote does. I think It would need to update itself at regular intervals in order to get the effect I was hoping for. Maybe an IO pattern?

1 Like