Jump to content

Featured Replies

Posted

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?
 

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:

 

 

  • 2 weeks later...
  • 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...

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.

  • 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.

  • 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

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

  • 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.

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)
  )
  • 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

  • Author

There are many identical dictums, which might mess up the situation. I must now take care of getting unique dictums.

  • 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


Copyright © 2014-2025 Opusmodus™ Ltd. All rights reserved.
Product features, specifications, system requirements and availability are subject to change without notice.
Opusmodus, the Opusmodus logo, and other Opusmodus trademarks are either registered trademarks or trademarks of Opusmodus Ltd.
All other trademarks contained herein are the property of their respective owners.

Powered by Invision Community

Important Information

Terms of Use Privacy Policy