I am trying to implement a tranceGate function in TidalCycles that applies a classic trance-style gating effect directly as a ControlPattern, to be used like this:
d1 $ n "0" # s "supersaw" # tranceGate 0.05 "1[11]0"
The function should interpret a string of symbols per cycle, where each symbol represents a step in the gate:
1means: gradually release the current sound’s gain down to 0 over a linear duration specified byr(in seconds), then immediately return its gain to the level it had before the gate affected it.0means: rest for that step, leaving the volume completely untouched — whatever sound is playing continues naturally.
The number and grouping of symbols define subdivision within the cycle. Bracketed groups like "[11]" should be treated as compressed steps within a single slice of the cycle (e.g., "1[11]0" creates a different rhythmic gate density than "1110"). The release time r should remain constant regardless of symbol density, just like a standard envelope release in melodic patterns.
My goal is not random chopping but precise, deterministic gating based on the step sequence. The resulting function should produce rapid rhythmic stutters typical of trance gates, not full-cycle silences or long sustained fades.
I have attempted several implementations, including bracket parsing and envelope construction, but so far the output results in entire-cycle fades or silence instead of step-level gating. Here is my most recent attempt:
let
tranceGate :: Double -> String -> Pattern Double
tranceGate r s = cat $ map symbolToPattern (concatMap expandGroup (parseBrackets s))
where
symbolToPattern '1' = fast 1 $ cat [pure 1, slow (pure $ toRational r) (pure 0), pure 1]
symbolToPattern '0' = pure 1
symbolToPattern _ = pure 1
parseBrackets [] = []
parseBrackets ('[':xs) =
let (grp, rest) = span (/= ']') xs
in grp : parseBrackets (drop 1 rest)
parseBrackets (x:xs) = [x] : parseBrackets xs
expandGroup grp = if length grp > 1
then map id grp
else grp
in do
panic
d1 $ n "0" # s "supersaw" # gain (tranceGate 0.05 "1[11]0")
d2 $ s "kick:8*4"
I have been stuck on this for days. Every variation either produces full-cycle sounds and rests or removes the sound entirely. How can I correctly implement tranceGate so that it:
-
Interprets symbols per cycle with bracket grouping,
-
Applies step-level gating with linear release (
rseconds), -
Restores volume instantly after each gated step,
-
Works as a proper
ControlPatternchained with#
Any guidance or working approach would be greatly appreciated.