💾 Archived View for thrig.me › blog › 2023 › 12 › 11 › musical-lsystem.gmi captured on 2024-08-18 at 18:11:14. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-12-28)
-=-=-=-=-=-=-
More information about L-systems can be found elsewhere. A good source with regard to music is the following,
"Applications of Generative String-Substitution Systems in Computer Music"
as it has pratical details on how to convert random bits of L-system generated strings into cromulent musical events. Some dabbling around with the rules and axiom
F F+G G F-G F
which yields the output that a LOGO turtle might usually draw
1 F 2 F+G 3 F+G+F-G 4 F+G+F-G+F+G-F-G 5 F+G+F-G+F+G-F-G+F+G+F-G-F+G-F-G 6 F+G+F-G+F+G-F-G+F+G+F-G-F+G-F-G+F+G+F-G+F+G-F-G-F+G+F-G-F+G-F-G ...
but instead can be turned into music.
The method here is to use two sets of pitches that the observant may note as being a tonic and a dominant,
$ atonal-util ly2pitch e g b 52 55 59 $ atonal-util ly2pitch b fis d 59 54 50
and then "F" turns into a rest (of some duration), "G" turns into a note (of some pitch), "+" shifts the note selection, and "-" shifts the rest duration selection. There's an additional complication to shift the pitch set selected from after a certain number of notes have gone by.
All told, this works out pretty well?
Many different mappings from a L-System to music (or turtle motions in LOGO, or whatever) are possible. Statistics may help: if an L-System has a lot more "+" than "-" in it, and those symbols are used to change the pitch or velocity, the notes may run off the scale, or the music may become too loud. This may be desirable in a shorter fragment, to get an upards arc.
Where the symbols appear is also important and may contradict a statistical summary; if there is a "++++++" run at the end of the string, then that pitch leap or volume change may not appear in the output. Looking at the statistics is however a good step to get the general lay of the land, so to speak. The frequency of the symbols would be a good place to start.
(defun plusone (n) (1+ (if (numberp n) n 0))) (defun charstats (string &aux (seen (make-hash-table)) seq (total 0)) (declare (inline plusone)) (loop for ch across string do (incf total) (setf (gethash ch seen) (plusone (gethash ch seen)))) (maphash (lambda (k v) (push (cons k v) seq)) seen) (sort seq #'< :key #'cdr) (dolist (c seq) (format t "~&~a ~d ~$~&" (car c) (cdr c) (coerce (/ (cdr c) total) 'long-float)))) (charstats "F+G+F-G+F+G-F-G+F+G+F-G-F+G-F-G+F+G+F-G+F+G-F-G-F+G+F-G-F+G-F-G")
This particular string is pretty boring, though.
- 15 0.24 G 16 0.25 + 16 0.25 F 16 0.25