I realize this may be a silly question, but in my experiments with tidal and with the videos I've watched, I haven't seen references to implementing local functions and/or implementing local variables that are then used in patterns.
The reason why I ask is because it would be nice if you could have a melody stored in a variable, used in some permutations across different patterns, and then change the variable and then have that change reflected in the playing patterns. Additionally, if you could change that variable within a function and still have that change reflected in the playing patterns, that would be very nice.
Is this possible in tidal ?
I saw one possible technique from this @kindohm's amazing tidal tutorial: he defines expressions with the
let keyword inside a
In your case, I create an expression for a sequence of notes and then have both the original and the reversed played by the lead synth, and the stretched reversed version played by the bass:
let melodicSeq = scale "minor" "0 -1 3 2 7 5 4 -3"
$ off 0.125 (rev)
$ slow 2
$ note melodicSeq
# s "juno:5"
# gain 0.8
$ slow 4
$ note melodicSeq
# s "bass1"
# legato 0.7
Is this what you're looking into?
Here's a way:
setF "melody" "c a f e"
d1 $ n (cF_ "melody") # s "superpiano"
The interface for this might be refined in a future version of Tidal.
Is there a place where to look up for the documentation?
I tried looking into hackage but I found nothing about it.
I hope Alex won't mind me saying, it is kind of incomplete and a bit out of date in places. But, it's a wiki! So improvements want you to make them
Yes I've been wondering if I should shift focus back to the userbase rather than trying to do this book.. But I'd really love to make progress on the latter, which should then feed back into the userbase. Edits to the userbase are always appreciated though!
Or maybe the two efforts should come together. I've been looking admiringly at this great system: https://www.pubpub.org/
I'm going to explore moving my tidal book efforts there, and then maybe the userbase could be merged with that somehow
Yeah, I had a look at pubpub after I saw you refer to it, very interesting. Meanwhile, I'm finding a bit more confidence in myself to chip away at the userbase wiki occasionally when I find something that needs doing.
thanks @tedthetrumpet, I think this is sort of low-level though and I saw that reading from hackage you can sometimes get slightly more information on the innards. But I found no information in either place. I will wait then
Feel free to also ask if you'd like more info about anything.
Once I get around to documenting cF and friends then I will probably change it in the process to make it more user friendly.
Thanks @yaxu - that makes sense - I'll fiddle with that sort of syntax and see what I can get out. What about my second question - having functions that can modify variables and theoretically, be tied to the clock somehow - is that something that exists ?
@mattia.paterna - thanks for the link to Kindohm's tutorial - that's definitely an interesting way of managing this state. Could definitely work well.
Do you have a more concrete example of what you'd like to achieve?
I'm trying to daisy chain a couple of concepts here, so my full thought process goes like this:
- Have variables that are accessible by d1,d2, etc. and can change on re-evaluation.
- Have functions that are tied to the clock and can either read variables and spit out a new variable or modify existing variables that are currently being read by the d1, d2, etc.
The first proof of concept I want to do with something like this is to create a cellular automata / game of life idea that will have rhythms and melodies constantly changing based on very simple interaction rules.
If that works, then what I want to be able to do is
- Have midi input read by a function that in turn modifies a value, which is then read by d1,d2, etc. - essentially allowing for feedback loops that are modifiable in the real world and will interact in unpredictable, but stable ways.
That's kind of been my blocker, and I'm trying to figure out how or if that's possible.
Fwiw, I saw that in the upcoming learning sessions that there was some discussion of functions, I've bought the sessions and will be working my way through them. Hopefully they'll help answer some of the more basic stuff I've been asking.
Tidal's computational model of patterns has advantages and disadvantages. One disadvantage is that there is no internal state. A Tidal pattern is a pure function from time (and other external state) to events, and has no way of storing state within a pattern. This buys you flexibility, for example being able to reverse time. Furthermore you can usually work around this, for example by making patterns that wrap other patterns, manipulating their inputs and outputs.. But you can't really implement cellular automata as a tidal pattern, unfortunately.
@yaxu That makes sense and is consistent with how I was modeling the patterns in my head (e.g. as a pure function without state), but was hoping I was wrong I'll watch the teaching tidal series and hopefully glean some information about how to achieve something similar. Thanks!
would it be possible at some point have a thorough discussion about it?
Super interested, as I am approaching functional programming and I would really like to understand Tidal's core concepts.
(I just don't want to flood this discussion with things not related to the video.)
My understanding is that the patterns are pure functions within Haskell, which means that they are essentially immutable once they are evaluated ; this is why in the Kindohm example. the variable definition is part of the block, so that when the block gets re-evaluated, the variable gets re-evaluated and placed back into the patterns.
I haven't yet tested it, but I believe in Yaxu's example, if we define the variable and evaluate it into the pattern, if we change the variable and evaluate, its change won't be immediately reflected in any pattern that uses it - those patterns will each need to be re-evaluated. Is that a correct understanding ?
No in my example, you are setting some mutable state, which gets passed into the pattern by the scheduler via the same mechanism as MIDI/OSC input, so you do hear the results straight away. This is the "external state" I alluded to earlier.
Ah cool! That's great! Is there any external access to the global clock ? That'd open up a ton of possibilities.
You can make a callback that's called at a fixed rate with the current clock state, by default every 1/20th of a second:
let callback state = putStrLn $ show state
clocked defaultConfig callback
This is what the main tidal scheduler uses. I'm reworking this in the current dev version of Tidal and the
clocked function needs an extra parameter:
let callback state = putStrLn $ show state
clocked defaultConfig (sTempoMV tidal) callback
Does that help?