Jump to content
Sign in to follow this  
torstenanders

Towards algorithmic orchestration (and customising sound playback with multiple sound libraries)

Recommended Posts

Dear Alain Jamot, 

 

here is a function that may help you. This function is useful for customising sound playback with multiple sound libraries or for algorithmic orchestration. 

 

The function expects an OMN expression and returns a list of multiple OMN sequences (multiple parts). It basically sorts notes from the OMN sequence into different parts, depending on the articulations of individual notes. All notes with certain articulations go in one resulting parts, and notes with other articulations in another part.

 

Here is an example. It sorts all notes with pizz or arco articulations into one part, and notes with trem articulations into another part. Each time, notes are substituted by rests in other parts, so that timing relations of notes in different parts are preserved.  

(separate-parts '((h c4 pizz q arco) (h trem q h pizz) (h arco+stacc -q fermata))
                '((pizz arco)
                  (trem)))
=> ; part 1: pizz and arco
   ((h c4 mf pizz q arco) (-h q c4 mf h pizz) (h c4 mf arco+stacc -q fermata))
   ; part 2: trem
   ((-h -q) (h c4 mf trem -q -h) (-h -q fermata)))

 

You can then assign your first part to on MIDI channel in your def-score call, and the next part to another MIDI channel, e.g., like so.

(setf omn-expr '((h c4 pizz q arco) (h trem q h pizz) (h arco+stacc -q fermata)))
(setf parts (separate-parts omn-expr
                            '((pizz arco)
                              (trem))))

(def-score two-violins
           (:title "Title"
            :composer "Composer"
            :copyright "Copyright © "
            :key-signature 'chromatic
            :time-signature '((1 1 1 1) 4)
            :tempo 100
            :layout (bracket-group
                     (violin1-layout 'violin1)
                     (violin2-layout 'violin2)))
  
  (violin1
   :omn (nth 0 parts)
   :channel 1
   :sound 'gm
   :program 'violin
   :volume 100
   :pan 54
   :controllers (91 '(48))
   )
  
  (violin2
   :omn (nth 1 parts)
   :channel 2
   :sound 'gm
   :program 'violin
   :volume 100
   :pan 74
   :controllers (91 '(60))
   )
  )

 

The function definition of separate-parts is below.

 

Best,

Torsten

 

Janusz: This is another example of a function showing how processing polyphonic music with double-nested OMN expressions can be useful. Once we have a standard notation for polyphonic OMN expressions with multiple voices/parts in Opusmodus 2, as discussed earlier, then this function can easily be adapted to output that format. 

(labels ((make-corresponding-rest (event)
         (let ((len (omn-encode (first event))))
           (cons 
            ;; rests should remain rests
            (if (> len 0)
              (* len -1)
              len)
            (omn :rest-articulation event))))
       (push-event-and-rests (event matching-position result-omns articulation-sets-length)
         (push event (nth matching-position result-omns))
         (loop for i in (remove matching-position (gen-integer 0 (1- articulation-sets-length)))
           do (push (make-corresponding-rest event) (nth i result-omns)))))
  (defun separate-parts (sequence articulation-sets)
    "The function `separate-parts' is useful for customising your sound playback with multiple sound libraries or for algorithmic orchestration. 
    The function breaks an OMN sequence (a single part) into a list of multiple OMN sequences (multiple parts). It basically sorts notes from the OMN sequence into different parts, depending on the articulations of individual notes. All notes with certain articulations go in one resulting parts, and notes with other articulations in another part. In all other resulting parts, notes are substituted by rests, so that timing relations of notes in different parts are preserved. 
    This function can be useful, when you have multiple sound libraries that support different articulations of the same instrument. You can then perform notes with certain articulations on one software instrument (on its own MIDI channel etc.), and notes with other articulations on another instrument. 
    Alternatively, you can use the function for algorithmic orchestration, where you assign custom articulations (typically declared with add-text-attributes first) such as instrument labels with your custom algorithm, and then use this function in a second step to separate your instruments.

    Remember that the result of this function is a list of multiple OMN sequences (multiple parts). You have to split it into its individual parts for use in OMN. 

    Args:
    - sequence: OMN sequence, can be nested
    - articulation-sets: list of list of articulations. All notes with articulations contained in the first articulation-set end up in the first resulting part, notes with articulations in the second set end up in the second part and so forth. 
    
    The decision which part a note belongs to is always made based on the first articulation that matches an articulation-set. If a note contains no articulation, or an articulation contained in no set, then it is matched to the first articulation-set. If an articulation is contained in multiple articulation-sets, then the earlier match in articulation-sets is used.

    Examples: 
    
(separate-parts '(h c4 pizz q arco)
                '((pizz)
                  (arco)))
=> ((h c4 mf pizz -q)   ; part 1 with pizz articulations
    (-h q c4 mf arco))  ; part 2 with arco

(separate-parts '((h c4 pizz q arco) (h trem q h pizz) (h arco+stacc -q fermata))
                '((pizz arco)
                  (trem)))
=> (((h c4 mf pizz q arco) (-h q c4 mf h pizz) (h c4 mf arco+stacc -q fermata)) ; part 1: pizz and arco
    ((-h -q) (h c4 mf trem -q -h) (-h -q fermata)))                             ; part 2: trem
"
    (if (listp (first sequence))
      ;; sequence is nested
      (matrix-transpose 
       (mapcar #'(lambda (seq) (separate-parts seq articulation-sets))
               sequence))
      ;; sequence is flat list
      (let* ((articulation-sets-length (length articulation-sets))
             (result-omns (make-list articulation-sets-length :initial-element nil)))
        (loop for event in  (single-events sequence) 
          do (let ((event-articulation (fourth event)))
               (if event-articulation
                 (let ((matching-position
                        (position-if #'(lambda (articulation-set)
                                         (some #'(lambda (art) 
                                                   (member art (disassemble-articulations event-articulation)))
                                               articulation-set))
                                     articulation-sets)))
                   (if matching-position
                     (push-event-and-rests event matching-position result-omns articulation-sets-length)
                     ;; if no match, then add event to first omn result                    
                     (push-event-and-rests event 0 result-omns articulation-sets-length)))
                 ;; if no articulation, then add event to first omn result 
                 (push-event-and-rests event 0 result-omns articulation-sets-length))))
        (mapcar #'(lambda (result) (flatten-omn (reverse result))) result-omns)))))
        
;; for  your convenience, I include the following definition already shared earlier
(defun disassemble-articulations (art)
  "Splits a combined OMN articulations into a list of its individual attributes.

  Example:
  (disassemble-articulations 'leg+ponte)
  => (leg ponte)"  
  (mapcar #'intern (split-string (symbol-name art) :separator "+")))

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

  • Similar Topics

    • By torstenanders
      What is the Lisp function for stopping playback (bound to the shortcut cmd-esc)? 
       
      ... I could never add a custom keyboard shortcut to the Opusmodus text editor Hemlock (and I tried), but I just managed adding keyboard shortcuts and a menu for playing Opusmodus snippets and my polyphonic score format to Emacs (thanks to the fact that scores now can be displayed in a separate window). Only need some key for stopping playback as well.
       
      (The main thing I will then miss when using Emacs as the Opusmodus IDE is the close integration of the document, but well, you cannot have everything 🙂  On the plus side, I have a more stable editor and in particular a very good debugger.)
       
      Thanks! 
       
      Torsten
    • By torstenanders
      Dear all,
       
      After updating to a recent Opusmodus version 1.3, I ran into some errors when loading code that had been loaded and compiled before, e.g., quicklisp, quicklisp libraries, swank (the interface that allows using Emacs Slime with Opusmodus), and my own Lisp libraries. The errors I saw were rather cryptic, like the following:
       
      Error: The value "CL" is not of the expected type list.
      While executing: (:internal ccl::operation-on-all-specs ccl::%define-package), in process Listener-1(7).
       
      After exchanges with Janusz (thanks a lot for your help!) I learnt that the underlying Common Lisp version of Opusmodus had been upgraded to CCL version 1.12, and the format of compiled files (fasl files or dx64fsl files) of this version had changed.
       
      So, in case you run into similar problems, these can be fixed simply by deleting all previously compiled files (which Lisp stores if they do not change to speed up the load process), so that the Lisp compiler has to compile them again. In the folder  ~/.cache/common-lisp just delete any folders starting with ccl-, so that all compiled files in this directory are compiled again.
       
      Best,
      Torsten
×
×
  • Create New...