Posted January 27Jan 27 I've been having a long session with Opusmodus ChatGPT. My aim has been to make a process driven score with a group of functions, from which would randomly be selected one to produce patterns for each section. I have two versions of gen-euclidean-omn, one getting argument values from outside lists and one inside let-form, and one version of polygon-rhythm. Things have been developing quite complicated, even ChatGPT haven't found a solution. In the resulting omn '-markings have been expressed with quote-word, which doesn't produce any score. The last hint from ChatGPT was to clean the result with replace-quote function, which doesn't work either. In score example you see the results: (defun generate-fragment (number-of-sections number-of-patterns) "Generate a musical fragment consisting of NUMBER-OF-SECTIONS sections, each containing NUMBER-OF-PATTERNS patterns." (loop for section-number from 1 to number-of-sections collect (generate-section number-of-patterns))) (defun generate-section (number-of-patterns) "Generate a section of music consisting of NUMBER-OF-PATTERNS patterns using a randomly selected rhythm-generating function." (let ((functions '(gen-euclidean-omn-1 gen-euclidean-omn-2 polygon-rhythm)) (pitches '(a4 b4 gs3 bb5))) ; Example pitches (let* ((chosen-function (nth (random (length functions)) functions)) (patterns (loop for i from 0 below number-of-patterns collect (case chosen-function (gen-euclidean-omn-1 ;; Version 1: gen-euclidean-omn using external lists `(gen-euclidean-omn ,(+ 1 (random 4)) ; count ,(nth (mod i (length eucl-subdivisions)) eucl-subdivisions) ; level ,(nth (mod i (length eucl-pulses)) eucl-pulses) ; low ,(+ 1 (nth (mod i (length eucl-pulses)) eucl-pulses)) ; high ',pitches ; Properly quoted pitch list 'e ; Properly quoted rhythmic length nil ; Velocity :seed ,(nth (mod i (length eucl-seeds)) eucl-seeds))) ; seed (gen-euclidean-omn-2 ;; Version 2: gen-euclidean-omn using internal let-form (let ((count (+ 1 (random 4))) (level (+ 4 (random 12))) (low (random 2)) (high (+ 1 (random 4))) (seed (random 100))) `(gen-euclidean-omn ,count ,level ,low ,high ',pitches 'e nil :seed ,seed))) (polygon-rhythm ;; Polygon rhythm must loop explicitly `(polygon-rhythm ,(+ 3 (random 5)) ; n-gon 16 ; points ,(random 16) ; start :pitch ',pitches ; Properly quoted pitch list :seed ,(random 100))))))) patterns))) (setf eucl-subdivisions '(8 8 12 4)) (setf eucl-pulses '(2 4)) (setf eucl-seeds '(10 20 30 40 11 21 31 41 12 22 32 42)) (setf eucl-variants '(p r i ri)) ;; Generate a fragment with 3 sections, each having 4 patterns (generate-fragment 3 4) ;;RESULT: (((gen-euclidean-omn 4 8 2 3 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 10) (gen-euclidean-omn 2 8 4 5 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 20) (gen-euclidean-omn 1 12 2 3 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 30) (gen-euclidean-omn 3 4 4 5 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 40)) ((polygon-rhythm 5 16 14 :pitch (quote (a4 b4 gs3 bb5)) :seed 42) (polygon-rhythm 3 16 2 :pitch (quote (a4 b4 gs3 bb5)) :seed 60) (polygon-rhythm 5 16 4 :pitch (quote (a4 b4 gs3 bb5)) :seed 85) (polygon-rhythm 5 16 9 :pitch (quote (a4 b4 gs3 bb5)) :seed 64)) ((gen-euclidean-omn 4 8 2 3 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 10) (gen-euclidean-omn 1 8 4 5 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 20) (gen-euclidean-omn 3 12 2 3 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 30) (gen-euclidean-omn 3 4 4 5 (quote (a4 b4 gs3 bb5)) (quote e) nil :seed 40))) (defun replace-quote (input-string) "Replace `(quote ...)` with `'` in a string." (let ((result (with-output-to-string (out (loop for char across input-string for next = (char input-string (1+ (position char input-string :start t))) do (cond ((and (char= char #\Q) (char= next #\U)) (write-string "'" out) (incf (position char input-string))) (t (write-char char out)))))))) result)) ;;RESULT: Error: In a call to "Loop..Across": (((polygon-rhythm 7 16 0 :pitch # :seed 10) (polygon-rhythm 5 16 11 :pitch # :seed 15) (polygon-rhythm 3 16 9 :pitch # :seed 34) (polygon-rhythm 6 16 2 :pitch # :seed 69)) ((polygon-rhythm 5 16 0 :pitch # :seed 79) (polygon-rhythm 4 16 10 :pitch # :seed 87) (polygon-rhythm 5 16 5 :pitch # :seed 48) (polygon-rhythm 3 16 14 :pitch # :seed 64)) ((polygon-rhythm 7 16 1 :pitch # :seed 52) (polygon-rhythm 4 16 1 :pitch # :seed 31) (polygon-rhythm 7 16 0 :pitch # :seed 2) (polygon-rhythm 6 16 0 :pitch # :seed 19))) (of type cons) is not of type vector. Any ideas? Could this be solved more simply/elegantly?
January 28Jan 28 Here’s a short example—hope it helps!(setf n-section 4) (setf n-patterns-by-section '(4 6 4 3)) (setf pmat-by-section '((d4 e4 f4 a4 c5)(c4 d4 eb4 f4 fs4 gs4 a4)(c4 e4 g4)(bb3 d4 f4 ab4))) (setf process (loop repeat n-section for pt in n-patterns-by-section for pm in pmat-by-section :collect (gen-loop pt (make-omn :pitch (rnd-sample 16 pm) :length (rnd-pick (list (euclidean-rhythm 16 1 12 's :type 2) (polygon-rhythm (rndn 1 3 16) 16 0 :legato t) )) )))) (setf fl (assemble-seq process)) (ps 'gm :fl (list fl)) and for a much more extensive example:
February 1Feb 1 Author Thanks for the replay! I have tried to combine repeating structure with dictums. So far I have this working score, which also gives listing of the dictums. Henryalgorithmic process driven multi-dictum.opmo Collected Dictums.txt
February 1Feb 1 Great example ! Thank you. I like the dictums to file and all the details given into the listener when compiling. Bravo !
February 10Feb 10 Author I was thinking that I could build a system, where algorithmically, with counterpoint function, score evaluation would write dictums and patterns into files (that has succeeded) and another score could read from files dictums and patterns which then could be used as parameters for counterpoint. This has not succeeded. Apparently counterpoint does not accept ready-made dictums read from files as parameters. The reason why I want to work this way, is that after the first score is evaluated, you could open dictums- and pattern-files in OM, edit them as you wish, and evaluate with these edited dictums and patterns. So , you would get a combination of algorithmic and manual workflow. Might be a good way to study counterpoint function.ChatGPT did not know which parameters counterpoint accepts, so I sent him (her? it?) counterpoint documents. Let's see what answers may come out. We've had a discussion going on for days. So far no luck.Of course, there is make-dictum -function. Could the aforesaid aim be possible by destructuring the dictums read from file and letting make-dictum rebuild the dictums? Could be complicated...
February 10Feb 10 36 minutes ago, HenryT said:This has not succeeded. Apparently counterpoint does not accept ready-made dictums read from files as parameters.Did you try to put them in variable ?For my part, I have done few months ago the same kind of research as you but in a different way, not by writing the dictum to a file but by building an algorithm who write (format) all the code in the listener (counterpoint and dictums) so you can copy and paste in a file and edit it manually. But I've stopped to develop it because it was a bit too complicated and I switched to some other projects / research. Unfortunately, I was not able to find back that file, it is probably somewhere in my scratch and test files but don't know where ... I will try to find it when I will have a bit more free time.Your approach is very interesting and I would be very interested to explore it further.
February 11Feb 11 Author Yes, dictums are put into dictums-from-file and patterns into patterns-from-file, and feeded, one by one, into counterpoint function. There might be something wrong with counterpoint function, since I'm getting an error: Error: Unexpected keyword ((- - 9 -) :span 5/16 :methods (t-2 - rol t2) :harmony nil :tonality nil :polyphony (6 7)) which is not one of (:voices :sequence :span :extend :methods :harmony :tonality :polyphony :leading :iterate :sustain :tempo). Sometimes :voices pops up and stops evaluation. I'm wondering why, since voices is not keyed argument.
February 11Feb 11 Could you send me the files ? I can have a look to it to try to find form where come the error if you wish.
February 11Feb 11 Author First you evaluate Algorithmic process-driv multi-dict write-to-file.opmo and then you update the names of resulted dictum- and patterns -files in Algorithmic process-driv multi-dict read-from-file.opmo and run that. There are old files in output folder, they can be deleted, to clear the situation.Algo_and_Manual.zip
February 11Feb 11 I had a look, and I think you could use a simpler approach, like setf in the patterns and dictum files.Attached is an example file generated with your algorithm, but with setf manually added. There’s also a file with counterpoint that uses these files.Of course, you can modify your original algorithm to insert setf automatically—no need for manual input. I just did it manually as an example of a possible, simpler solution.SimpleDictumCallExample.opmo SimplePatternsExample.opmo SimpleDictumExample.opmo
February 11Feb 11 Author Thanks a lot! This is really an economic and elegant solution. I was not aware of the possibility to put dictums and patterns into variables and evaluate them in one editor and then call them in another editor. I thought that the declared variables are available only in the same editor where they were declared. But this seems not to be the case...Is there a way to make counterpoint work deterministically?There seems to be a stubborn randomisation going on in counterpoint function. Can it be put off? I inited the same seed that was used in original score, but I didn't get the same results. Of course this makes manual editing less promising.
February 12Feb 12 If you encapsulate the counterpoint inside a progn with an init-seed function, you will achieve fully deterministic counterpoint.You can even change local seeds within the counterpoint because the OM seed system is very well designed!(setf patterns (assemble-seq (gen-loop 4 (make-omn :pitch (rnd-sample 16 (make-scale 'c4 9 :alt '(2 1)) :seed 8) :length (rnd-sample 4 '((q)(e e)(s s s s)(3q = =)) :seed 8) )))) (progn (init-seed 42) ;; Methods list for random choice inide counterpoint (setf m-list '(ld22 ld24 t4 t2 t-2 t-4 ra ri i)) (counterpoint patterns '( ((2 1 3 -) :methods (rnd-sample 4 m-list :seed 5)) ((rndn 4 1 4 :seed 12)) ((3 2 1 4) :methods (rnd-sample 4 m-list :seed 9)) ) :index 'v ) (ps 'gm :sq '(v1 v2 v3 v4)) (init-seed nil) )
February 12Feb 12 Author Thanks for this! Now the first xml produced with SimpleDictumCall is different compared to the xml, which was produced with the Algorithmic process-driv multi-dict write-to-file.opmo. But after that the following versions from SDC are similar, and manual editions show up in xml. So far looks good.But if you want to get from SDC an xml, which is identical with the xml produced with the Algorithmic process-driv multi-dict write-to-file.opmo, how would you do it? Assemble-seq is used in both, should it also be wrapped inside progn? And how do you add rea-marks to violin 1 (only the first shows up).SimpleDictumCallExample.opmo
February 12Feb 12 Author There are many identical dictums, which might mess up the situation. I must now take care of getting unique dictums.
February 12Feb 12 I believe there was a misunderstanding regarding my code. In any case, here’s a corrected version of what you were trying to achieve—hope it helps!If you’re interested, I offer private lessons in Opusmodus. Let me know if you’d like more details!SimpleDictumCallExample2Henry.opmo
February 12Feb 12 Author Thank you very much! This works fine. I'm learning from your video courses, especially 5 Opusmodus lessons. They are excellent!
Create an account or sign in to comment