Hi, i've tried to play with counterpoints. It's not quite easy, as they implies relation between note of different pattern and i do not know how to do this. By the way, i've done this literaly (as of literate). Here the output, which may be useful for others learning tidal. https://blap.space/
I've been working on this topic too. sbv can express the other rules such as avoiding parallel fifths. It looks like all of the original (Fux's) rules were coded for a different solver by http://hdl.handle.net/2078.1/thesis:48985 . Sometimes all possible solutions can be enumerated, but most are very similar. So this suggests a gap in tidal:
rand :: Pattern Double -- no memory
irand :: Int -> Pattern Int -- no memory
perlin :: Pattern Double -- 1 cycle memory
rcounterpoint' :: Fux -> [Int] -> Pattern [Int] -- 1 cycle memory
-- ^ rcounterpoint will take a cantus firmus
-- and produce counterpoints which interpolate
-- towards a relatively distinct counterpoint chosen
-- at the beginning of each cycle
thanks for pointing to this thesis, I started reading it.
with each of the Haskell constraint libraries that I know of (ersatz, hasmtlib), calling the solver would have some IO
type (because it invokes an external process), so it cannot be declared as a pure function on patterns in tidal-cycles.
perhaps "finding one/the best pattern that satisfies some set of constraints" is really different from "constructing/transforming a pattern" (during performance). but who knows.
is there some theory of "counterpoint for rhythms" (describing what is allowed/reasonable for base, snare, cymbal)? I have this book the breakbeat bible — Mike Adamo which lists "elements" that "sound good" (proven by examples from history) but makes not attempt at explaining why they do. Such an explanation, if formaliized (as constraints) could be used to derive these "elements", and new ones.
List or logict might be enough, which would replicate the hand calculation of starting at each end which usually can meet in the middle. unsafePerformIO might be morally permissible here, but maybe it makes more sense to enumerate the counterpoints once before calling d1 :: _ -> IO ()
Excluding parallel fifths/octaves doesn't seem to add any "holes" when I plot it this way:
My example at GitHub - aavogt/counterpoint: first species counterpoint using sbv for tidalcycles has one possible encoding for rhythm, but there is likely one that does the following better:
- continuity: similar melodies yield similar rhythms
- aesthetic-preserving: good melodies yield good rhythms
- density: rhythms have enough notes to make sense, but still be an accompaniment
If I improvise I don't have much variety in rhythm, so I don't think I'm likely to be able to find the rules for directly making good breakbeats.