I began working with a very simple pattern structure in TidalCycles. Using a string literal as a melody, I defined a helper function to keep a fixed notes-per-cycle rate:
let
notesPerCycle :: Rational -> String -> Pattern Time
notesPerCycle eventFrequency melody = pure $ fromRational $ realToFrac (length (words melody)) / eventFrequency
melody1 = "7 4 0 2 ~ 0 4 9 5 4 2 4 5 0 4 2 0 6 8 0" -- delete or add notes as desired
satellitePattern = slow (notesPerCycle 4 melody1) $ note melody1 # s "supersaw"
# sustain 0.3
# speed 0.3
# cut 1
# gain 0.8
in do
hush
d1 $ satellitePattern
d2 $ s "kick*4" # gain 0.8
This works perfectly. The melody plays at exactly four notes per cycle, regardless of how many notes the string contains. However, I then wondered whether this melody could instead be generated programmatically, rather than hardcoding magic numbers into a string. Conceptually, something like this:
let
notesPerCycle :: Rational -> String -> Pattern Time
notesPerCycle eventFrequency melody = pure $ fromRational $ realToFrac (length (words melody)) / eventFrequency
melody1 :: String -- imagine this is a function that outputs a melody as String
melody1 = "7 4 0 2 ~ 0 4 9 5 4 2 4 5 0 4 2 0 6 8 0" -- delete or add notes as desired
satellitePattern = slow (notesPerCycle 4 melody1) $ note melody1 # s "supersaw"
# sustain 0.3
# speed 0.3
# cut 1
# gain 0.8
in do
hush
d1 $ satellitePattern
d2 $ s "kick*4" # gain 0.8
But this version fails with:
2025-10-10 13:30:20.216 [error] t>
[GHC-83865]
* Couldn't match type: [Char]
with: Pattern Note
Expected: Pattern Note
Actual: String
* In the first argument of `note', namely `melody1'
In the first argument of `(#)', namely `note melody1'
In the first argument of `(#)', namely
`note melody1 # s "supersaw"'
From a user’s perspective, a string literal and a string returned by a function should be interchangeable, yet Tidal treats note "7 4 0" differently from note melody1 when melody1 is user-defined. Clearly this is intentional, but it's not immediately intuitive.
My Actual Question
What is the idiomatic way in TidalCycles to:
Generate melodies programmatically (e.g. as a Pattern Note, not a static string)?
Play them at a fixed rate. For example, exactly 4 notes per cycle, regardless of whether the melody contains 8, 16, or 32 steps?
In other words, how can we dynamically adjust slow (or some equivalent) based on the actual density or length of a generated melody pattern, without resorting to manual strings or magic numbers?