<?xml version="1.0"?>
<rss version="2.0"><channel><title>All Activity</title><link>https://opusmodus.com/forums/discover/</link><description>Opusmodus Community - All Activity</description><language>en</language><item><title>quintuplet +septolet</title><link><![CDATA[https://opusmodus.com/forums/topic/4215-quintuplet-septolet/?do=findComment&comment=14589]]></link><description>I just tried it :rewrite-lengths 't but it doesn't work!  (def-score score-name  (:title "twelve-tone" :key-signature 'atonal :rewrite-lengths 't :time-signature '((2 2) 4) :tempo 80) (instrument :omn test :channel 1 :sound 'gm :program 'acoustic-grand-piano)))</description><pubDate>Thu, 15 Oct 2026 13:12:11 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14588]]></link><description>Great ! I'm very happy to inspire you. Doing GUI for Opusmodus algorithms is really cool !</description><pubDate>Thu, 15 Oct 2026 11:54:08 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14587]]></link><description>Here's also a GUI to make the many settings easier to handle. Read the manual first, then it works. Be sure to set the paths in load.opmo and nt-gui.   Thanks, @Stephane Boussuge &#x2013; it was your example that showed me this was possible (with Claude)!  NT-GUI-Manual-EN.pdfNT-GUI-Anleitung-DE.pdfnt-gui-en.opmo</description><pubDate>Thu, 15 Oct 2026 09:06:02 +0000</pubDate></item><item><title>quintuplet +septolet</title><link><![CDATA[https://opusmodus.com/forums/topic/4215-quintuplet-septolet/?do=findComment&comment=14586]]></link><description>Hi, Can someone help me?  I'm having another problem with the display of quintuplets.  (setf comping4 (make-omn :pitch ass :length '(5h = = = = 5h = = = = 5h = = = = 5h = = = = 5h = = = = 5h = = = = 5h = = = = 5h = = = -5h 5h = = = = 5h = = = = 5h = = = = 5h = = = = 5h 5h 5h 5h 5h 5h 5h -5h -5h -5h) :span :length))   the problem disappears with def-score!  (def-score score-name (:title "twelve-tone" :key-signature 'atonal :time-signature '((2 2) 4) :tempo 72) (instrument :omn comping4 :channel 1 :sound 'gm :program 'acoustic-grand-piano)))   The problem recurs if I combine several lists  (setf test (omn-to-time-signature (assemble-seq comping comping2 comping3 comping4 comping5 comping6 comping7) '(4 4)))  (def-score score-name (:title "twelve-tone" :key-signature 'atonal :time-signature '((2 2) 4) :tempo 72) (instrument :omn test :channel 1 :sound 'gm :program 'acoustic-grand-piano)))  exactly the same thing with septuplets</description><enclosure url="https://opusmodus.com/forums/uploads/monthly_2026_10/image.png.f4ee6b3d59821930465a77e53d2409c4.png" length="29614" type="image/png"/><pubDate>Thu, 15 Oct 2026 09:02:11 +0000</pubDate></item><item><title>Creating graphical composition tools in Opusmodus with Codex</title><link>https://opusmodus.com/forums/blogs/entry/10-creating-graphical-composition-tools-in-opusmodus-with-codex/</link><description><![CDATA[OPUSMODUS · COMPOSITION AND CODE Creating graphical composition tools in Opusmodus with CodexStéphane Boussuge · October 2026 From a musical idea to a working interface with native functions and internal General MIDI. An Opusmodus script can become a small composition workshop: a window in which we choose musical parameters, generate a passage, inspect its notation and listen to it. AI can help us write that workshop while we keep control of the musical idea and the rules behind it. In this article, I use Codex to develop an English-language interface for a chamber ensemble: flute, clarinet, violin, cello, vibraphone and piano. The example generates three sections from reservoirs of rhythmic and melodic motifs. Each section has its own scale, root, tendency, instrumental density and optional random rests. The musical calculations use native Opusmodus functions. The interface uses Common Lisp and LispWorks CAPI, the GUI toolkit available in the Opusmodus runtime. All helper definitions are included in the example; no personal extension library is needed. Playback uses the internal General MIDI sounds. Codex helps write and revise the program during development. Once the code is in Opusmodus, the workshop runs locally without an AI connection. In Connecting an AI to Opusmodus through Swank, I explored an iterative dialogue between musical ideas, Lisp expressions and evaluated results. Here, that approach leads to a reusable graphical tool. Swank can help Codex test the code during development; readers can run the finished workshop directly in Opusmodus without setting up that connection. Start with a musical specificationThe first useful step is to describe the composition process. Before asking for buttons, I specify what those buttons should control. For this study, every motif lasts exactly one bar of 4/4. A reservoir contains motifs ranging from two half notes to sixteen sixteenth notes. The pitches come from a scale built with make-scale. A tendency path selects motifs from this reservoir, ordered from sparse to busy. A separate density path determines how many instrumental parts play in each bar. These are two different musical dimensions. A passage can contain many instruments playing slowly, or only two instruments playing rapid figures. Keeping the two controls separate makes the interface useful for exploring texture. The three sections initially contain eight bars each. The complete form therefore lasts twenty-four bars, with six synchronised parts. The piano is represented by a single melodic staff to keep this example compact. Here is the kind of prompt I would give Codex:  This prompt gives Codex concrete musical requirements and a result we can test. A working score or a small OMN example is also valuable context: it shows the notation, ensemble and transformations we actually use. Give Codex the relevant documentationOpusmodus is a specialised environment. Ask Codex to check the signatures of the functions it uses against the documentation for your installed version. If it can read your local files, point it to the relevant documentation; otherwise paste the entries into the conversation. For this example, the useful entries are make-scale, rnd-pick, rnd-sample, rnd-unique, rnd-rest, rndn, gen-loop, sort-count-asc, gen-tendency, vector-round, vector-map, gen-binary-row, matrix-transpose, binary-to-section, gen-pause, make-omn, omn, pitch-transpose, ambitus, def-score, compile-score, display-musicxml and display-midi. The entries for rnd-seed and init-seedexplain how a custom generator should cooperate with Opusmodus's random sequence. For the interface, identify CAPI explicitly. It provides windows, text fields, option menus, tabs, layouts and button callbacks. These are LispWorks components, rather than Opusmodus musical functions. They do not require a separate GUI application. LispWorks describes CAPI and provides a small interface example here. Keep persistent project instructions short and specific. For example, an AGENTS.md file can say that musical processing must use native Opusmodus functions, that the interface must be in English and that playback must use internal GM. Codex reads project instructions from AGENTS.md; the official Codex prompting guide describes how those instructions are supplied to the agent. Build the musical engine before the interfaceThe central function in this example is om-ai-chamber-generate. It accepts a parameter list and returns the generated parts, score definition and seed. It can be tested directly in the Listener: (om-ai-chamber-generate
 (om-ai-chamber-defaults)
 :seed 261)
This call defines the score om-ai-chamber-score. It generates music without opening the workshop. We can then inspect or audition that score: (display-musicxml 'om-ai-chamber-score)

(display-midi
 (compile-score 'om-ai-chamber-score)
 :ignore-ports t
 :auto-play nil)
Testing the engine first lets us check durations, pitch ranges and reproducibility before adding window behaviour. The GUI then has a clear job: read the controls, call the engine and retain the latest successful result. The musical chain uses named materials throughout. rnd-sample selects one-bar rhythms, and sort-count-asc orders them from sparse to busy. gen-loop, a native Opusmodus macro, builds the pitch reservoir. make-omn combines lengths and pitches. Each instrument has an explicit gen-tendency path, read through vector-map, followed by its own register transformations. For distribution, vector-round turns the density path into requested part counts. rnd-unique selects that number of distinct instrument IDs in each bar. gen-binary-row and matrix-transpose turn these choices into six named instrument masks. binary-to-section and gen-pause silence the unselected bars without changing their duration. The scale choices are Major, Dorian, Whole tone, Octatonic and Chromatic. They are defined as interval patterns passed to make-scale, so the example does not depend on a separate scale catalogue. The root applies to the scale before the parts are shifted by octaves. The four curve presets are Rise, Arch, Fall and Wave. They contain four control values between zero and one. A motif tendency moves through the reservoir according to rhythmic activity. It does not describe melodic contour. Its variance introduces fluctuations around that path. The active-parts curve controls the number of parts selected in each bar before random rests; their identities are chosen separately. Write the musical process in the STF Atelier styleFor this revision, I asked Codex to use my STF-Atelier skill. Its musical reference is the working method of Empreintes: identifiable source materials, explicit transformations, named distributions and separate instrumental parts. That method guides the organisation of the code; this example keeps its own rhythm vocabulary and chamber-study design. The composition block contains short setf expressions using native Opusmodus operations. It contains no loop, mapcar, lambda or new musical helper definitions. For example, the flute path and register remain visible: (setf ac-path-fl
      (gen-tendency ac-bars ac-tendency-curve
                    :variance ac-variance :seed (seed)))
(setf ac-line-fl (vector-map ac-reservoir ac-path-fl))
(setf ac-register-fl (pitch-transpose 12 ac-line-fl))
(setf ac-register-fl (ambitus '(c4 c7) ac-register-fl))
The GUI still needs Common Lisp functions for callbacks, input validation, local scope and seed cleanup. Those technical wrappers contain the explicit musical expressions; they do not replace them with a hidden generic composition engine. The accompanying AI-Chamber-Composition.opmo presents the same musical process as directly evaluable Sections 1, 2 and 3, without GUI definitions or custom helper functions. Readers do not need to install STF-Atelier: the skill guided the writing, and the delivered score uses native Opusmodus functions. Add random rests independently to each sectionEach section has three additional controls: Random rests enables or disables the transformation. Rest span % supplies the first argument of rnd-rest: the target proportion of musical duration converted to rests. Placement probability % supplies its separate :prob parameter, which weights the random distribution of the choices. For example, a rest span of 30% and a placement probability of 50% produce this native operation: (setf ac-rest-fl
      (rnd-rest 0.3 ac-register-fl :prob 0.5 :seed (seed)))
The complete program reserves six rest seeds and uses them explicitly, one for each part. The percentage describes a target duration proportion, rather than an independent 30% chance applied to every note. The achieved rest duration is constrained by the available rhythmic values. The placement probability does not mean that 50% of the resulting notes must be silent. Random rests are applied to each line before the orchestral bar mutes. Existing inactive bars therefore do not consume the added-rest target. The distribution masks are then applied with gen-pause. Every bar retains its original span. The active-parts curve describes the parts selected before adding these rests. A selected part may become completely silent, so the number of actually sounding parts can fall below the requested minimum. A rest span of 100% silences the whole selected section. With the option disabled, or the rest span set to zero, no additional rests are inserted. The default option is disabled, with a prepared rest span of 20% and placement probability of 50%. Changing only these controls preserves the reservoir, motif paths and orchestral masks. The native rewrite changes the order of random operations from the first version of the workshop, so seed 261 reproduces this revised version rather than its earlier music. The native rnd-seed contract remains the same. Assign the General MIDI instruments explicitlyThe score uses a named program for every instrument: (fl  :omn flute-part      :channel 1 :sound 'gm :program 'flute)
(cl  :omn clarinet-part   :channel 2 :sound 'gm :program 'clarinet)
(vn  :omn violin-part     :channel 3 :sound 'gm :program 'violin)
(vc  :omn cello-part      :channel 4 :sound 'gm :program 'cello)
(vib :omn vibraphone-part :channel 5 :sound 'gm :program 'vibraphone)
(pno :omn piano-part      :channel 6 :sound 'gm :program 'acoustic-grand-piano)
This excerpt illustrates the part syntax; the complete program below supplies the actual OMN data. Separate channels preserve the six timbres. The score contains no external port assignments. There are two settings to distinguish: :sound 'gm and :program specify the instrument, while :ignore-ports t in display-midi forces playback through the internal General MIDI sounds. The workshop always opens its GM player with that option. It also uses :auto-play nil, so opening the player does not immediately start the music. Press Play in the MIDI Player to listen. Make random results reproducibleA seed lets us return to a particular version of the study. With identical parameters and seed, the generated music is identical. This makes a useful comparison possible: hold the seed fixed, change one musical parameter and listen to the difference. The generator follows the native rnd-seed protocol. It saves *init-seed*, calls rnd-seed, obtains seeds for nested random operations through (seed), and uses init-state in an unwind-protect cleanup. Each top-level generation occupies one position in the init-seed sequence, independently of how many random operations occur inside it. Cleanup also runs if a calculation fails after initialisation. Three section seeds are allocated before deciding whether to generate one section or all three. Consequently, Generate Section reproduces that section's material within Generate Full Form, provided the parameters and master seed are unchanged. The window starts with seed 261. To request a new version, clear the seed field. The next generation passes :seed nil, following the current native seed state. Once generation succeeds, the field displays the resolved seed so that we can reproduce the result. Load the program and open the windowDefining the program and opening its interface are separate actions. Evaluating a defun tells Lisp how a function works; it does not call that function. If you evaluate only the definitions, no workshop window appears. This is expected. There are two ways to use the supplied example: Open AI-Chamber-Workshop.opmo in Opusmodus and evaluate the entire file. Its final expression calls (om-ai-chamber-gui), so the window opens. If you use AI-Chamber-GUI.lisp, evaluate the file to load the definitions, then enter the following expression in the Listener or in an Opusmodus document and evaluate it: (om-ai-chamber-gui)
This is the command that opens the graphical interface. Evaluate the complete parenthesised expression, not just the symbol om-ai-chamber-gui. For a first test: Open the window using the command above. Keep the default parameters and seed 261. Click Generate Full Form. The status line should report twenty-four bars. Click Show Score to see the notation. Click Open GM Player, then press Play in the MIDI Player. This is the workshop's internal GM audition path. The section tabs retain independent settings. Generate Section uses the currently selected tab; Generate Full Form concatenates Sections 1, 2 and 3. Both commands produce six parts with equal duration. Closing the workshop closes its window. Evaluate (om-ai-chamber-gui) again to open a fresh workshop. Calling it while another workshop is open creates a second window; each window keeps its own parameters and latest result. The random-rest option and both percentages are also retained independently by the three tabs. Export the version you generatedClick Export .opmo to save the latest successful score. The export contains a def-score with the calculated OMN data and named GM programs, followed by commands to show the score and open the internal GM player. That exported file runs without the workshop's helper functions. It does not rebuild the reservoir or make new random choices. Editing a parameter in the window therefore affects the export only after we generate a new result. This preserves a version we have already inspected and heard. The window also retains its previous successful result when later input is invalid or generation fails. Show Score, Open GM Player and Export .opmo use that retained result. Refine the tool through specific requestsOnce the example works, we can ask Codex for small changes tied to musical intentions:    For each revision, ask for a concrete check. Adding a bass staff should preserve alignment. Changing the density algorithm should preserve its bounds. Altering the interface should preserve section settings when we switch tabs. In this example, the checks cover one-bar durations, six aligned parts, the requested active-part counts, fixed-seed reproducibility, the native init-seed sequence, cleanup after an internal error, isolated-section consistency, all five scales, disabled/zero/100% rest spans, placement probability bounds, section independence, autonomous score export and MIDI/MusicXML compilation. The implementation was tested in Opusmodus 4.0.32650 on macOS with LispWorks 8.1.2; other runtime versions may require adjustments to CAPI availability or function signatures. The musical decisions remain ours: the ensemble, the reservoir, the shape of each section and the versions we choose to keep. Codex helps translate those decisions into a tool that we can read, test and develop further. Complete working examplePaste the following complete code into a new Opusmodus document and evaluate it. The last expression opens the interface. If you evaluate only the definitions, evaluate (om-ai-chamber-gui) separately afterward. ;;;; AI Chamber Workshop -- a standalone Opusmodus / LispWorks CAPI example.
;;;; Stephane Boussuge, October 2026.
;;;; Evaluate this ENTIRE .opmo file: its final expression opens the window.
;;;; If you evaluated only the definitions, evaluate (om-ai-chamber-gui) now.
(in-package :opusmodus)

(defparameter *om-ai-chamber-scales*
  '(("Dorian" 2 1 2 2 2 1 2)
    ("Major" 2 2 1 2 2 2 1)
    ("Whole tone" 2)
    ("Octatonic" 1 2)
    ("Chromatic" 1)))

(defparameter *om-ai-chamber-curves*
  '(("Rise" . (0.1 0.3 0.65 0.95))
    ("Arch" . (0.15 0.8 0.8 0.15))
    ("Fall" . (0.95 0.65 0.3 0.1))
    ("Wave" . (0.2 0.85 0.25 0.75))))

(defparameter *om-ai-chamber-rhythms*
  '((1/2 1/2)
    (1/4 1/4 1/4 1/4)
    (1/4 1/8 1/8 1/4 1/8 1/8)
    (1/8 1/8 1/8 1/8 1/8 1/8 1/8 1/8)
    (1/8 1/16 1/16 1/8 1/16 1/16 1/8 1/16 1/16 1/8 1/16 1/16)
    (1/16 1/16 1/16 1/16 1/16 1/16 1/16 1/16
     1/16 1/16 1/16 1/16 1/16 1/16 1/16 1/16)))

(defvar *om-ai-chamber-lock* (mp:make-lock :name "AI Chamber generation"))

(defun om-ai-chamber-defaults ()
  "Return fresh parameters for three sections. Every cell is one 4/4 bar."
  (list :tempo 96 :sections
    (loop for tendency in '("Rise" "Arch" "Fall")
          for density in '("Rise" "Wave" "Fall")
          collect (list :bars 8 :motifs 12 :variance 0.25
                        :scale "Dorian" :root 'd4 :tendency tendency
                        :density density :min-parts 2 :max-parts 6
                        :rests nil :rest-amount 0.2 :rest-prob 0.5))))

(defun om-ai-chamber-check-integer (value low high label)
  (unless (and (integerp value) (&lt;= low value high))
    (error "~A must be an integer from ~D to ~D." label low high))
  value)

(defun om-ai-chamber-check (config seed section)
  (unless (or (null seed) (integerp seed))
    (error "Seed must be an integer or NIL."))
  (when section (om-ai-chamber-check-integer section 1 3 "Section"))
  (om-ai-chamber-check-integer (getf config :tempo) 40 200 "Tempo")
  (unless (= (length (getf config :sections)) 3)
    (error "The workshop needs exactly three sections."))
  (dolist (s (getf config :sections))
    (om-ai-chamber-check-integer (getf s :bars) 4 16 "Bars")
    (om-ai-chamber-check-integer (getf s :motifs) 2 24 "Motifs")
    (om-ai-chamber-check-integer (getf s :min-parts) 1 6 "Minimum parts")
    (om-ai-chamber-check-integer (getf s :max-parts) (getf s :min-parts) 6 "Maximum parts")
    (unless (and (realp (getf s :variance)) (&lt;= 0 (getf s :variance) 1))
      (error "Variance must be between 0 and 1."))
    (unless (member (getf s :rests) '(t nil))
      (error "Random rests must be T or NIL."))
    (dolist (key '(:rest-amount :rest-prob))
      (unless (and (realp (getf s key)) (&lt;= 0 (getf s key) 1))
        (error "~A must be between 0 and 1." key)))
    (unless (assoc (getf s :scale) *om-ai-chamber-scales* :test #'equal)
      (error "Unknown scale."))
    (unless (member (getf s :root) '(c4 cs4 d4 eb4 e4 f4 fs4 g4 ab4 a4 bb4 b4))
      (error "Unknown root."))
    (dolist (key '(:tendency :density))
      (unless (assoc (getf s key) *om-ai-chamber-curves* :test #'equal)
        (error "Unknown curve for ~A." key))))
  config)

(defun om-ai-chamber-material (parameters &amp;key seed)
  "Technical seed/scope wrapper around the explicit STF-Atelier expressions."
  (let* ((state *init-seed*)
         (ac-bars (getf parameters :bars))
         (ac-motifs (getf parameters :motifs))
         (ac-root (getf parameters :root))
         (ac-scale-steps (cdr (assoc (getf parameters :scale) *om-ai-chamber-scales* :test #'equal)))
         (ac-tendency-curve (cdr (assoc (getf parameters :tendency) *om-ai-chamber-curves* :test #'equal)))
         (ac-density-curve (cdr (assoc (getf parameters :density) *om-ai-chamber-curves* :test #'equal)))
         (ac-variance (getf parameters :variance))
         (ac-min-parts (getf parameters :min-parts))
         (ac-max-parts (getf parameters :max-parts))
         (ac-rest-rate (if (getf parameters :rests) (getf parameters :rest-amount) 0.0))
         (ac-rest-probability (getf parameters :rest-prob)))
    (unwind-protect
        (progn
          (rnd-seed seed)
          (let (ac-scale ac-rhythm-reservoir ac-ordered-rhythms ac-pitch-reservoir ac-reservoir
                ac-density-path ac-counts ac-active-ids ac-masks ac-rest-seeds
                ac-path-fl ac-line-fl ac-register-fl ac-before-rest-fl ac-rest-fl
                ac-part-fl ac-path-cl ac-line-cl ac-register-cl ac-before-rest-cl
                ac-rest-cl ac-part-cl ac-path-vn ac-line-vn ac-register-vn
                ac-before-rest-vn ac-rest-vn ac-part-vn ac-path-vc ac-line-vc
                ac-register-vc ac-before-rest-vc ac-rest-vc ac-part-vc ac-path-vib
                ac-line-vib ac-register-vib ac-before-rest-vib ac-rest-vib ac-part-vib
                ac-path-pno ac-line-pno ac-register-pno ac-before-rest-pno ac-rest-pno
                ac-part-pno)
            ;;; SOURCE -- the existing one-bar rhythm vocabulary remains unchanged.
            (setf ac-scale (make-scale ac-root 15 :alt ac-scale-steps))
            (setf ac-rhythm-reservoir
                  (rnd-sample ac-motifs *om-ai-chamber-rhythms* :seed (seed)))
            (setf ac-ordered-rhythms (sort-count-asc ac-rhythm-reservoir))
            (setf ac-pitch-reservoir
                  (gen-loop ac-motifs (rnd-sample 16 ac-scale :seed (seed)) :seed (seed)))
            (setf ac-reservoir
                  (make-omn :length ac-ordered-rhythms :pitch ac-pitch-reservoir
                            :velocity '(mp) :span :length))

            ;;; DISTRIBUTION -- one coordinated choice of instrument IDs in each bar.
            (setf ac-density-path
                  (gen-tendency ac-bars ac-density-curve :variance 0.0 :seed (seed)))
            (setf ac-counts (vector-round ac-min-parts ac-max-parts ac-density-path))
            (setf ac-active-ids
                  (rnd-unique ac-counts (gen-repeat ac-bars '((0 1 2 3 4 5))) :seed (seed)))
            (setf ac-masks (gen-binary-row 6 ac-active-ids))
            (setf ac-masks (matrix-transpose ac-masks))

            ;;; MOTIF PATHS -- independent readings of the same named reservoir.
            (setf ac-path-fl
                  (gen-tendency ac-bars ac-tendency-curve :variance ac-variance :seed (seed)))
            (setf ac-line-fl (vector-map ac-reservoir ac-path-fl))
            (setf ac-path-cl
                  (gen-tendency ac-bars ac-tendency-curve :variance ac-variance :seed (seed)))
            (setf ac-line-cl (vector-map ac-reservoir ac-path-cl))
            (setf ac-path-vn
                  (gen-tendency ac-bars ac-tendency-curve :variance ac-variance :seed (seed)))
            (setf ac-line-vn (vector-map ac-reservoir ac-path-vn))
            (setf ac-path-vc
                  (gen-tendency ac-bars ac-tendency-curve :variance ac-variance :seed (seed)))
            (setf ac-line-vc (vector-map ac-reservoir ac-path-vc))
            (setf ac-path-vib
                  (gen-tendency ac-bars ac-tendency-curve :variance ac-variance :seed (seed)))
            (setf ac-line-vib (vector-map ac-reservoir ac-path-vib))
            (setf ac-path-pno
                  (gen-tendency ac-bars ac-tendency-curve :variance ac-variance :seed (seed)))
            (setf ac-line-pno (vector-map ac-reservoir ac-path-pno))

            ;;; REGISTERS -- octave shifts retain the chosen scale.
            (setf ac-register-fl (pitch-transpose 12 ac-line-fl))
            (setf ac-register-fl (ambitus '(c4 c7) ac-register-fl))
            (setf ac-register-cl (pitch-transpose 0 ac-line-cl))
            (setf ac-register-cl (ambitus '(d3 c7) ac-register-cl))
            (setf ac-register-vn (pitch-transpose 0 ac-line-vn))
            (setf ac-register-vn (ambitus '(g3 e7) ac-register-vn))
            (setf ac-register-vc (pitch-transpose -24 ac-line-vc))
            (setf ac-register-vc (ambitus '(c2 e5) ac-register-vc))
            (setf ac-register-vib (pitch-transpose 0 ac-line-vib))
            (setf ac-register-vib (ambitus '(f3 f6) ac-register-vib))
            (setf ac-register-pno (pitch-transpose -12 ac-line-pno))
            (setf ac-register-pno (ambitus '(c3 c6) ac-register-pno))

            ;;; RANDOM RESTS -- introduce breathing space before the orchestral mutes.
            ;;; ac-rest-rate is a target duration proportion, not a note-by-note chance.
            ;;; Allocate every seed even when disabled, so changing rests preserves paths.
            (setf ac-rest-seeds (rndn 6 1 999999 :seed (seed)))
            (setf ac-rest-fl
                  (rnd-rest ac-rest-rate ac-register-fl
                            :prob ac-rest-probability :seed (nth 0 ac-rest-seeds)))
            (setf ac-rest-cl
                  (rnd-rest ac-rest-rate ac-register-cl
                            :prob ac-rest-probability :seed (nth 1 ac-rest-seeds)))
            (setf ac-rest-vn
                  (rnd-rest ac-rest-rate ac-register-vn
                            :prob ac-rest-probability :seed (nth 2 ac-rest-seeds)))
            (setf ac-rest-vc
                  (rnd-rest ac-rest-rate ac-register-vc
                            :prob ac-rest-probability :seed (nth 3 ac-rest-seeds)))
            (setf ac-rest-vib
                  (rnd-rest ac-rest-rate ac-register-vib
                            :prob ac-rest-probability :seed (nth 4 ac-rest-seeds)))
            (setf ac-rest-pno
                  (rnd-rest ac-rest-rate ac-register-pno
                            :prob ac-rest-probability :seed (nth 5 ac-rest-seeds)))

            ;;; PARTS -- a zero mask silences the whole bar without changing its span.
            ;;; The density count describes selected parts BEFORE random rests.
            (setf ac-before-rest-fl
                  (gen-pause ac-register-fl :exclude (binary-to-section (nth 0 ac-masks))))
            (setf ac-part-fl
                  (gen-pause ac-rest-fl :exclude (binary-to-section (nth 0 ac-masks))))
            (setf ac-before-rest-cl
                  (gen-pause ac-register-cl :exclude (binary-to-section (nth 1 ac-masks))))
            (setf ac-part-cl
                  (gen-pause ac-rest-cl :exclude (binary-to-section (nth 1 ac-masks))))
            (setf ac-before-rest-vn
                  (gen-pause ac-register-vn :exclude (binary-to-section (nth 2 ac-masks))))
            (setf ac-part-vn
                  (gen-pause ac-rest-vn :exclude (binary-to-section (nth 2 ac-masks))))
            (setf ac-before-rest-vc
                  (gen-pause ac-register-vc :exclude (binary-to-section (nth 3 ac-masks))))
            (setf ac-part-vc
                  (gen-pause ac-rest-vc :exclude (binary-to-section (nth 3 ac-masks))))
            (setf ac-before-rest-vib
                  (gen-pause ac-register-vib :exclude (binary-to-section (nth 4 ac-masks))))
            (setf ac-part-vib
                  (gen-pause ac-rest-vib :exclude (binary-to-section (nth 4 ac-masks))))
            (setf ac-before-rest-pno
                  (gen-pause ac-register-pno :exclude (binary-to-section (nth 5 ac-masks))))
            (setf ac-part-pno
                  (gen-pause ac-rest-pno :exclude (binary-to-section (nth 5 ac-masks))))
            (values
              (list ac-part-fl ac-part-cl ac-part-vn ac-part-vc ac-part-vib ac-part-pno)
              ac-counts
              (list :reservoir ac-reservoir :rhythms ac-ordered-rhythms
                    :masks ac-masks :rest-seeds ac-rest-seeds
                    :paths (list ac-path-fl ac-path-cl ac-path-vn ac-path-vc ac-path-vib ac-path-pno)
                    :parts-before-rests
                    (list ac-before-rest-fl ac-before-rest-cl ac-before-rest-vn
                          ac-before-rest-vc ac-before-rest-vib ac-before-rest-pno)))))
      (init-state state))))

(defun om-ai-chamber-score-form (parts tempo title)
  ;; Embed the music, so viewing and exporting never regenerate random material.
  `(def-score om-ai-chamber-score
       (:title ,title :composer "AI Chamber Workshop"
        :key-signature 'chromatic :time-signature '(4 4) :tempo ,tempo
        :layout (bracket-group (flute-layout 'fl) (clarinet-layout 'cl)
                  (violin-layout 'vn) (violoncello-layout 'vc)
                  (treble-layout 'vib :name "Vibraphone" :abbr "Vib.")
                  (treble-layout 'pno :name "Piano" :abbr "Pno.")))
     (fl  :omn ',(first parts)  :channel 1 :sound 'gm :program 'flute)
     (cl  :omn ',(second parts) :channel 2 :sound 'gm :program 'clarinet)
     (vn  :omn ',(third parts)  :channel 3 :sound 'gm :program 'violin)
     (vc  :omn ',(fourth parts) :channel 4 :sound 'gm :program 'cello)
     (vib :omn ',(fifth parts)  :channel 5 :sound 'gm :program 'vibraphone)
     (pno :omn ',(sixth parts)  :channel 6 :sound 'gm :program 'acoustic-grand-piano)))

(defun om-ai-chamber-generate (config &amp;key seed section)
  "Return a result plist. SECTION is NIL (whole form) or 1, 2, 3."
  (om-ai-chamber-check config seed section)
  (mp:with-lock (*om-ai-chamber-lock*)
    (let ((state *init-seed*))
      (unwind-protect
          (let* ((chosen (rnd-seed seed))
                 (section-seeds (rndn 3 1 999999 :seed (seed)))
                 (config (copy-tree config))
                 ;; Allocate every section seed before choosing an audition.
                 (s1 (when (or (null section) (= section 1))
                       (multiple-value-list
                         (om-ai-chamber-material (first (getf config :sections))
                                                 :seed (first section-seeds)))))
                 (s2 (when (or (null section) (= section 2))
                       (multiple-value-list
                         (om-ai-chamber-material (second (getf config :sections))
                                                 :seed (second section-seeds)))))
                 (s3 (when (or (null section) (= section 3))
                       (multiple-value-list
                         (om-ai-chamber-material (third (getf config :sections))
                                                 :seed (third section-seeds)))))
                 ;; FORM -- the same explicit section order for each part.
                 (ac-form-fl (append (first (first s1))
                                     (first (first s2))
                                     (first (first s3))))
                 (ac-form-cl (append (second (first s1))
                                     (second (first s2))
                                     (second (first s3))))
                 (ac-form-vn (append (third (first s1))
                                     (third (first s2))
                                     (third (first s3))))
                 (ac-form-vc (append (fourth (first s1))
                                     (fourth (first s2))
                                     (fourth (first s3))))
                 (ac-form-vib (append (fifth (first s1))
                                     (fifth (first s2))
                                     (fifth (first s3))))
                 (ac-form-pno (append (sixth (first s1))
                                     (sixth (first s2))
                                     (sixth (first s3))))
                 (parts (list ac-form-fl ac-form-cl ac-form-vn ac-form-vc ac-form-vib ac-form-pno))
                 (form (om-ai-chamber-score-form parts (getf config :tempo)
                         (if section (format nil "AI Chamber -- Section ~D" section)
                           "AI Chamber -- Three Sections"))))
            (eval (copy-tree form))
            (list :name 'om-ai-chamber-score :form form :parts parts
                  :config config :seed chosen :section section
                  :section-seeds section-seeds
                  :densities (remove nil (list (second s1) (second s2) (second s3)))
                  :materials (remove nil (list (third s1) (third s2) (third s3)))
                  :bars (length (first parts))))
        (init-state state)))))

(defun om-ai-chamber-export (result pathname)
  "Save the last generated score as an autonomous .opmo file."
  (unless (getf result :form) (error "Generate music before exporting."))
  (with-open-file (out pathname :direction :output :if-exists :supersede
                               :if-does-not-exist :create :external-format :utf-8)
    (format out ";;; AI Chamber Workshop -- seed ~A~%" (getf result :seed))
    (write-line "(in-package :opusmodus)" out)
    (let ((*package* (find-package :opusmodus)) (*print-pretty* t)
          (*print-case* :downcase) (*print-length* nil) (*print-level* nil))
      (pprint (getf result :form) out)
      (pprint '(display-musicxml 'om-ai-chamber-score) out)
      (pprint '(display-midi (compile-score 'om-ai-chamber-score)
                            :ignore-ports t :auto-play nil) out)))
  pathname)

;;; The interface uses only Common Lisp and the CAPI supplied with Opusmodus.
(defclass om-ai-chamber-interface (capi:interface)
  ((controls :initform (make-hash-table :test #'equal) :reader om-ai-chamber-controls)
   (result :initform nil :accessor om-ai-chamber-result))
  (:default-initargs :title "AI Chamber Workshop"))

(defun om-ai-chamber-control (ui key)
  (gethash key (om-ai-chamber-controls ui)))

(defun om-ai-chamber-read-integer (text label &amp;optional blankp)
  (let ((clean (string-trim '(#\Space #\Tab #\Newline) text)))
    (if (and blankp (or (equal clean "") (string-equal clean "nil"))) nil
      (or (ignore-errors (parse-integer clean :junk-allowed nil))
          (error "~A must contain a whole number." label)))))

(defun om-ai-chamber-snapshot (ui)
  (labels ((number (key label) (om-ai-chamber-read-integer
                               (capi:text-input-pane-text (om-ai-chamber-control ui key)) label))
           (choice (key) (capi:choice-selected-item (om-ai-chamber-control ui key))))
    (list :tempo (number :tempo "Tempo") :sections
      (loop for i from 1 to 3 collect
        (list :bars (number (list i :bars) "Bars")
              :motifs (number (list i :motifs) "Motifs")
              :variance (/ (number (list i :variance) "Variance") 100.0)
              :min-parts (number (list i :min-parts) "Minimum parts")
              :max-parts (number (list i :max-parts) "Maximum parts")
              :rests (not (null (capi:button-selected (om-ai-chamber-control ui (list i :rests)))))
              :rest-amount (/ (number (list i :rest-amount) "Rest span") 100.0)
              :rest-prob (/ (number (list i :rest-prob) "Placement probability") 100.0)
              :scale (choice (list i :scale)) :root (choice (list i :root))
              :tendency (choice (list i :tendency)) :density (choice (list i :density)))))))

(defun om-ai-chamber-ui-generate (ui sectionp)
  (handler-case
      (let* ((config (om-ai-chamber-snapshot ui))
             (seed (om-ai-chamber-read-integer
                     (capi:text-input-pane-text (om-ai-chamber-control ui :seed)) "Seed" t))
             (section (when sectionp
                        (capi:choice-selected-item (om-ai-chamber-control ui :tabs))))
             (result (om-ai-chamber-generate config :seed seed :section section)))
        ;; Replace the stored snapshot only after successful generation.
        (setf (om-ai-chamber-result ui) result
              (capi:text-input-pane-text (om-ai-chamber-control ui :seed))
              (princ-to-string (getf result :seed))
              (capi:display-pane-text (om-ai-chamber-control ui :status))
              (format nil "Ready: ~D bars | Seed: ~A | Use Show Score or Open GM Player."
                      (getf result :bars) (getf result :seed))))
    (error (e) (capi:display-message "~A" e))))

(defun om-ai-chamber-ui-output (ui action)
  (handler-case
      (let ((result (om-ai-chamber-result ui)))
        (unless result (error "Click Generate Section or Generate Full Form first."))
        (case action
          (:export
           (let ((path (capi:prompt-for-file "Save generated score as .opmo"
                         :owner ui :operation :save
                         :pathname (merge-pathnames "AI-Chamber.opmo" (user-homedir-pathname)))))
             (when path (om-ai-chamber-export result path))))
          (otherwise
           (eval (copy-tree (getf result :form)))
           (if (eq action :score) (display-musicxml (getf result :name))
             (display-midi (compile-score (getf result :name))
                           :ignore-ports t :auto-play nil)))))
    (error (e) (capi:display-message "~A" e))))

(defun om-ai-chamber-gui ()
  "Open a new English-language CAPI workshop. Evaluate this after loading."
  (let ((ui (make-instance 'om-ai-chamber-interface)))
    (labels ((register (key pane) (setf (gethash key (om-ai-chamber-controls ui)) pane))
             (row (&amp;rest panes) (make-instance 'capi:row-layout :description panes :gap 12))
             (column (&amp;rest panes) (make-instance 'capi:column-layout :description panes :gap 12))
             (field (key title value)
               (register key (make-instance 'capi:text-input-pane :title title :title-position :top
                                           :text (princ-to-string value) :visible-min-width 85)))
             (choice (key title items selected)
               (register key (make-instance 'capi:option-pane :title title :title-position :top
                                           :items items :selected-item (find selected items :test #'equal))))
             (button (title function)
               (make-instance 'capi:push-button :text title :callback-type :none :callback function)))
      (let* ((sections
               (loop for s in (getf (om-ai-chamber-defaults) :sections) for i from 1 collect
                 (column
                   (row (field (list i :bars) "Bars (4-16)" (getf s :bars))
                        (field (list i :motifs) "Motifs (2-24)" (getf s :motifs))
                        (field (list i :variance) "Variance %" 25)
                        (field (list i :min-parts) "Min. parts (1-6)" 2)
                        (field (list i :max-parts) "Max. parts (1-6)" 6))
                   (row (choice (list i :scale) "Scale" (mapcar #'car *om-ai-chamber-scales*) "Dorian")
                        (choice (list i :root) "Root" '(c4 cs4 d4 eb4 e4 f4 fs4 g4 ab4 a4 bb4 b4) 'd4)
                        (choice (list i :tendency) "Motif tendency" (mapcar #'car *om-ai-chamber-curves*) (getf s :tendency))
                        (choice (list i :density) "Active parts curve" (mapcar #'car *om-ai-chamber-curves*) (getf s :density)))
                   (row (register (list i :rests)
                          (make-instance 'capi:check-button :text "Random rests" :selected nil))
                        (field (list i :rest-amount) "Rest span % (0-100)" 20)
                        (field (list i :rest-prob) "Placement probability % (0-100)" 50))
                   (make-instance 'capi:title-pane
                     :text "Density selects parts before random rests; rests can thin the sounding texture."))))
             (tabs (register :tabs
                     (make-instance 'capi:tab-layout :items '(1 2 3) :selected-item 1
                       :print-function (lambda (i) (format nil "Section ~D" i))
                       :visible-child-function (lambda (i) (nth (1- i) sections)))))
             (layout
               (column
                 (make-instance 'capi:title-pane :text "AI CHAMBER WORKSHOP")
                 (make-instance 'capi:title-pane
                   :text "Flute / Clarinet / Violin / Cello / Vibraphone / Piano -- Internal General MIDI")
                 (row (field :tempo "Tempo (40-200)" 96) (field :seed "Seed (blank = new)" 261))
                 tabs
                 (row (button "Generate Section" (lambda () (om-ai-chamber-ui-generate ui t)))
                      (button "Generate Full Form" (lambda () (om-ai-chamber-ui-generate ui nil))))
                 (row (button "Show Score" (lambda () (om-ai-chamber-ui-output ui :score)))
                      (button "Open GM Player" (lambda () (om-ai-chamber-ui-output ui :midi)))
                      (button "Export .opmo" (lambda () (om-ai-chamber-ui-output ui :export))))
                 (register :status (make-instance 'capi:display-pane
                   :text "Ready. Select a section, edit its parameters, then generate.")))))
        (setf (capi:pane-layout ui) layout)
        (capi:display ui)
        ui))))

;;; LAUNCH THE INTERFACE: evaluate this expression to open the window.
(om-ai-chamber-gui)
AI-Chamber-Workshop.opmo 
AI-Chamber-Composition.opmo 
AI-Chamber-GUI.lisp 
README.md]]></description><enclosure url="https://opusmodus.com/forums/uploads/monthly_2026_10/opusmodus-ai-chamber-feature.png.564f1926da4cf586042532dafb85d390.png" length="2065146" type="image/png"/><pubDate>Wed, 14 Oct 2026 19:57:42 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14585]]></link><description>wow !</description><pubDate>Tue, 13 Oct 2026 22:12:09 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14584]]></link><description>Thanks, you&#x2019;re the pro! &#x1F60A;</description><pubDate>Tue, 13 Oct 2026 21:30:46 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14583]]></link><description>Thank you, exacly like that :-)</description><pubDate>Tue, 13 Oct 2026 21:21:52 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14582]]></link><description>Corrections done &#x2013; here&#x2019;s everything as a ZIP file.   Good point, thanks! I&#x2019;ve added load-all.opmo: it loads all files in dependency order (and checks first that they&#x2019;re all there). Set *nt-ordner* to your folder once, then evaluate the file. (nt-laden :lauf nil) loads only the setup without running.  Machine Musicianship.zip</description><pubDate>Tue, 13 Oct 2026 21:10:19 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14581]]></link><description>Thank you for sharing. Good practice here would be to create a one load file to load them all - in a dependency sequence.  The agents files returning an error: Error: Undefined operator pick-norepeat in form (pick-norepeat 30 (gen-integer 30 80)).  you could use: (rnd-unique 30 (gen-integer 30 80)) or (rnd-sample 30 (gen-integer 30 80) :norep t)  You could create a workspace folder called &#x201C;Machine Musicianship&#x201D;, include a workspace file along with all the relevant files, and then compress the entire folder into a ZIP archive. This would make sharing and loading everything much easier! :-)</description><pubDate>Tue, 13 Oct 2026 20:52:45 +0000</pubDate></item><item><title>ST/4: Stochastic Fields, Temporal Morphology, and the Articulation of Musical Form</title><link><![CDATA[https://opusmodus.com/forums/topic/4213-st4-stochastic-fields-temporal-morphology-and-the-articulation-of-musical-form/?do=findComment&comment=14580]]></link><description/><pubDate>Tue, 13 Oct 2026 20:01:56 +0000</pubDate></item><item><title>Machine Musicianship - A generative agent system</title><link><![CDATA[https://opusmodus.com/forums/topic/4214-machine-musicianship-a-generative-agent-system/?do=findComment&comment=14579]]></link><description>Machine Musicianship - A generative agent system  I&#x2019;d like to share a project I&#x2019;ve been working on: a &#x201C;non-trivial machine&#x201D; for Opusmodus, after Heinz von Foerster. A trivial machine always answers the same input in the same way. A non-trivial machine has an internal state that decides how it reacts, and every input changes that state. The result is fully deterministic, yet hardly predictable.  What it does  Three agents (A, B, C). Each one combines a pitch agent and a rhythm agent. The pitch agent listens to the others, recognises interval patterns and builds up an internal value z. A selection rule then picks reactions on pitch, articulation and velocity, and the agent keeps developing its own material at every cell start. The rhythm agent plays an 8-bit counter (1 = note, 0 = rest), has a pulse and wanders through a small space. It reacts when it meets other agents or locks into the same pulse. In-between lanes. When neighbouring agents cross, share rhythmic ones or mirror each other, the lane between them produces events. These become two extra voices, and each lane is a small agent itself: it can rattle, play solo chords or fall silent. Form and presets. Global form states sit on top, together with presets: a few controls from 0 to 1, which can also change over time. Pitch fields. After the run, the voices can be mapped onto Xenakis-style sieves, large chords or scales. Pattern recognition always runs in the chromatic domain.  Important: always evaluate the files in the correct order (see the manual, chapter 2.1):  nt-kern.opmo einstellungen.opmo ab-functions.opmo tonfelder.opmo agent-a.opmo, agent-b.opmo, agent-c.opmo zwischen-ab.opmo, zwischen-bc.opmo form.opmo preset.opmo lauf.opmo partitur.opmo  After a change, evaluate the changed file as a whole, then lauf.opmo and partitur.opmo again.  I&#x2019;ve added load-all.opmo: it loads all files in dependency order (and checks first that they&#x2019;re all there). Set *nt-ordner* to your folder once, then evaluate the file. (nt-laden :lauf nil) loads only the setup without running.  Everything is plain Common Lisp in a set of .opmo files: an engine you don&#x2019;t touch, plus clearly structured files for the settings, the agents, the lanes, the form, the presets and the run. You plug in your own Opusmodus functions as reactions. The package includes an manual covering concept, settings, reference, troubleshooting and formulas. A note on language: function and parameter names are German, since that&#x2019;s how the project grew. The comments and the manual are in English, and the manual has a glossary.  Credits I developed this together with Claude (Anthropic&#x2019;s AI assistant). The concept and musical decisions are mine; Claude did most of the coding and documentation, and we refined it step by step in dialogue. Free to use, open source. Use it, change it, take it apart, build your own pieces with it. If you make something with it, I&#x2019;d love to hear it.  Feedback, questions and bug reports are very welcome. Andr&#xE9;    Machine Musicianship.zip  Test_Run with 256 Fenster/Windows  test_run_256.mp3</description><enclosure url="https://opusmodus.com/forums/uploads/monthly_2026_10/Bildschirmfoto2026-10-13um21_36_35.png.0010bb419097654b5f0ded4d91ba8e27.png" length="3420882" type="image/png"/><pubDate>Tue, 13 Oct 2026 19:40:22 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=28]]></link><description>The Opusmodus Source Code and further documentation is now at Substack.</description><pubDate>Tue, 13 Oct 2026 16:37:03 +0000</pubDate></item><item><title>ST/4: Stochastic Fields, Temporal Morphology, and the Articulation of Musical Form</title><link><![CDATA[https://opusmodus.com/forums/topic/4213-st4-stochastic-fields-temporal-morphology-and-the-articulation-of-musical-form/?do=findComment&comment=14578]]></link><description>An Opusmodus Generative Composition Project &#x2014; Version 2.4One of the enduring questions in my work with algorithmic composition concerns the relationship between statistical processes and musical coherence. Randomness, even when carefully constrained, does not necessarily produce music of compelling interest. The more consequential question, at least for me, is how stochastic behavior might be organized into recognizable musical situations, and how those situations might develop, interact, and transform over time.   My continuing exploration of this question has led to the development of ST/4 &#x2014; Stochastic Field Composition, a generative model for string quartet created in Opusmodus. The project takes inspiration from the pioneering stochastic compositions of Iannis Xenakis, particularly ST/4 and ST/10, without attempting to reconstruct their compositional procedures or imitate their musical surfaces. Instead, I am interested in extending some of the underlying ideas into a contemporary programmable environment, where statistical distributions, instrumental behavior, and larger formal relationships can become objects of compositional design.   At the center of the program is the concept of the stochastic field: a musical region defined by a particular constellation of probabilities, constraints, and behavioral tendencies. Each field possesses its own characteristics of density, event duration, registral disposition, dynamics, and instrumental activity. These fields can be juxtaposed to produce contrasting musical moments, or they can gradually morph into one another, allowing their statistical properties to evolve over time.   Version 2.4 introduces several important developments in this approach. A system of stochastic rubato permits event-arrival rates to accelerate and decelerate within individual fields, producing a sense of temporal breathing without relying upon conventional changes of tempo. Composer-defined rhythmic subdivisions, including tuplets, extend the available temporal vocabulary, while independently articulated instrumental ambitus trajectories allow each member of the quartet to move through, expand, or contract its available pitch register according to its own evolving contour.   Of particular interest to me is the possibility of creating musical direction without prescribing individual notes or gestures in advance. An instrument might gradually ascend while narrowing its active register, while another expands downward into a more widely distributed pitch field. Simultaneously, the density of events might increase, the durations become shorter, and the collective texture move toward a more agitated state. These processes are individually programmable, yet their interaction gives rise to musical circumstances that are not entirely predetermined.   This is where the distinction between control and emergence becomes especially important. The composer establishes the conditions within which the music can unfold, but the particular realization remains contingent upon the generative process. My interest is not in surrendering compositional responsibility to chance, but in discovering how carefully designed systems can produce relationships, continuities, and unexpected details that invite further listening.   The implementation makes use of Opusmodus and its Constraints System, alongside procedures for stochastic event generation, temporal interpolation, and the construction of instrumental parts. The result is intended to be both a practical compositional instrument and an environment for research into the organization of musical time, texture, and form.   Accompanying this post is an illustrated user guide for ST/4 version 2.4, documenting the program's architecture, composer controls, field definitions, temporal and registral transformations, and procedures for generating and evaluating musical results. I have included the guide not simply as software documentation, but as a way of making the compositional thinking behind the program more accessible to those interested in algorithmic music, stochastic processes, and the creative possibilities of Opusmodus.   The project remains a work in progress for my own composition work, as I expect such investigations always will. Each new capability suggests further questions about the relationship between formal intention and perceptual result. Ultimately, the measure of success is not the sophistication of the algorithm, but whether the music that emerges from it possesses a convincing sense of identity, movement, and consequence.   For me, that remains the essential task: to discover coherence through listening, and to use the algorithm not as a substitute for musical imagination, but as an extension of it. Accompanying material available at Substack: ST/4 &#x2014; Stochastic Field Composition v2.4: Independent Instrumental Ambitus Morphology &#x2014; Illustrated User Guide.  The guide provides an overview of the program, explanations of its principal parameters, diagrams of its temporal and registral processes, and suggestions for experimentation with the generative model.   Paid subscribers can download the Opusmodus program from Substack.  Now available on Substack.</description><pubDate>Tue, 13 Oct 2026 16:24:15 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=27]]></link><description/><enclosure url="https://opusmodus.com/forums/uploads/monthly_2026_10/image.png.61613f2869dc3e4b8f5d8da5b2476223.png" length="48878" type="image/png"/><pubDate>Tue, 13 Oct 2026 15:32:03 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=26]]></link><description>The field library for the current working version ;;; Each field: name, per-instrument arrival rate per 1/16, ;;; sounding durations in 1/16 units, register center/width ;;; (normalized within each instrument's pitch field), ;;; dynamics, and per-instrument activity multipliers. ;;; An activity multiplier of 0 silences that instrument. (defparameter st4-field-library   '((:suspended      :rate 0.10 :durations (4 6 8 12 16)      :center 0.50 :width 0.46      :dynamics (pp p mp)      :roles (1.0 0.65 0.85 0.50))     (:agitated      :rate 0.75 :durations (1 1 2 2 3 4)      :center 0.65 :width 0.18      :dynamics (mp mf f ff)      :roles (1.15 1.0 0.95 0.75))     (:fragmented      :rate 0.26 :durations (1 2 3 5 8)      :center 0.50 :width 0.38      :dynamics (p mp mf f)      :roles (1.0 0.45 0.85 0.65))     (:saturated      :rate 0.85 :durations (2 3 4 6 8)      :center 0.50 :width 0.48      :dynamics (mp mf f ff)      :roles (1.0 1.0 1.0 1.0))     (:residual      :rate 0.08 :durations (1 1 2 3 4)      :center 0.80 :width 0.17      :dynamics (pp p)      :roles (0.70 0.35 0.45 0.20))))  ;;; The composer determines the form explicitly.
;;; Each entry is (field-name number-of-4/4-measures).
;;; Unequal proportions and returns are intentional.
(defparameter *st4-field-form*
  '((:suspended 4)
    (:fragmented 5)
    (:agitated 5)
    (:suspended 3)
    (:saturated 6)
    (:fragmented 5)
    (:residual 4)));;; The composer determines the form explicitly.
;;; Each entry is (field-name number-of-4/4-measures).
;;; Unequal proportions and returns are intentional.
(defparameter *st4-field-form*
  '((:suspended 7)
    (:fragmented 1)
    (:agitated 1)
    (:suspended 3)
    (:saturated 2)
    (:fragmented 1)
    (:residual 8)))Same seed, and all other parameters the same.  Only change is the form definition above.</description><pubDate>Tue, 13 Oct 2026 14:35:06 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=25]]></link><description>Where the algorithm is headed, generally speaking:  Compositional Form Sequence, duration, proportion and recurrence of musical moments  to   Stochastic Field Definitions Density, duration, tessitura, register, dynamics, silence and instrumental relationships  to   Field Relationships and Transitions Juxtaposition, overlap, gradual morphing, interruption, recurrence  to   Stochastic Event Generation Independent instrumental realizations governed by the active field  to   Constraints and Musical Evaluation Local field identity, ensemble relationships and larger formal coherence</description><pubDate>Tue, 13 Oct 2026 13:54:41 +0000</pubDate></item><item><title>Opusmodus 4.0.32624 (Update) - Nested Tuplets</title><link><![CDATA[https://opusmodus.com/forums/topic/4212-opusmodus-4032624-update-nested-tuplets/?do=findComment&comment=14577]]></link><description>Addition: How-to/Stochastic - 4.0.32645</description><pubDate>Tue, 13 Oct 2026 07:53:20 +0000</pubDate></item><item><title>Opusmodus 4.0.32624 (Update) - Nested Tuplets</title><link><![CDATA[https://opusmodus.com/forums/topic/4212-opusmodus-4032624-update-nested-tuplets/?do=findComment&comment=14576]]></link><description>Fix to disassemble-omn: 4.0.32629</description><pubDate>Tue, 13 Oct 2026 04:22:22 +0000</pubDate></item><item><title>Opusmodus 4.0.32624 (Update) - Nested Tuplets</title><link><![CDATA[https://opusmodus.com/forums/topic/4212-opusmodus-4032624-update-nested-tuplets/?do=findComment&comment=14575]]></link><description>I need to extend the disassemble-omn functionality to cover the new nestet tuplets symbols. Will make a new build tomorrow.</description><pubDate>Mon, 12 Oct 2026 21:16:38 +0000</pubDate></item><item><title>Opusmodus 4.0.32624 (Update) - Nested Tuplets</title><link><![CDATA[https://opusmodus.com/forums/topic/4212-opusmodus-4032624-update-nested-tuplets/?do=findComment&comment=14574]]></link><description>Superb!  And NOT EASY!  This is so welcomed!!  Bravo!</description><pubDate>Mon, 12 Oct 2026 19:56:59 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=24]]></link><description>Oh yes, I see.  Thank you for your thoughts. I certainly understand your point, and I agree that a thriving forum community depends upon the generosity of its members and their willingness to exchange ideas, experiences, and knowledge. I have always valued that kind of collegial exchange, particularly in the world of computer music, where so much of our collective progress has depended upon people sharing their discoveries.   At the same time, I am trying to develop something rather more substantial through my Substack publication. After more than four decades as a professional composer, researcher, and educator, including over thirty years teaching composition and music technology at the university level, I have accumulated a considerable body of knowledge, experience, and intellectual property. I am now devoting a significant amount of my time to documenting that experience, developing original compositional software and generative models, and writing about the ideas and techniques that inform my creative practice.   This is not simply a matter of sharing a few examples or fragments of code. Much of what I am publishing represents many hours, sometimes days or weeks, of research, experimentation, programming, testing, musical evaluation, and documentation. I regard this work as an extension of my professional creative practice, and I believe that both the intellectual effort and the time invested in it have value.   My Substack is intended to provide a sustainable way of continuing this work, with freely accessible writing alongside more substantial materials available to supporting subscribers. The modest subscription fees are a means of supporting the ongoing research and development, rather than an attempt to discourage the exchange of ideas.   I remain very happy to participate in the forum, share insights, discuss techniques, and contribute to the community. Indeed, I hope that what I am doing will ultimately enrich that community as well. But I also believe that composers, researchers, and educators should feel comfortable assigning value to their intellectual and creative labor, just as we recognize the value of the software and other tools we use, such as Opusmodus.   I hope you can appreciate the distinction. I see these two forms of engagement as complementary rather than competing, and I would very much like to continue contributing to both.</description><pubDate>Mon, 12 Oct 2026 19:50:34 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=23]]></link><description>I think it&#x2019;s much better for the forum community and engagement if you post it here directly rather than referring people to your website. In my opinion it&#x2019;s more direct and helps the forum community thrive &#x263A;&#xFE0F;</description><pubDate>Mon, 12 Oct 2026 19:38:39 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=22]]></link><description>Yes, the audio example was created using Opusmodus and the current program version is v1.8.1 - basically it creates a continuous stochastic morphology and uses some of the new constraints functions that have recently been added to Opusmodus.  I used the VSTi Opus, a sampled string quartet, and recorded the example with Nuendo.  I do plan to share this program when I am finished working on it.   The Opusmodus program and reference materials will uploaded to my Substack.</description><pubDate>Mon, 12 Oct 2026 19:37:27 +0000</pubDate></item><item><title>Stochastic Thinking: Xenakis, ST/4, and the New Opusmodus Constraints System</title><link><![CDATA[https://opusmodus.com/forums/blogs/entry/9-stochastic-thinking-xenakis-st4-and-the-new-opusmodus-constraints-system/?do=findComment&comment=20]]></link><description>I know the Xenakis things. I thought you had programmed your track/example in Opusmodus, so the code would be easy to share.</description><pubDate>Mon, 12 Oct 2026 19:28:31 +0000</pubDate></item></channel></rss>
