Jump to content

torstenanders

Members
  • Content count

    305
  • Joined

  • Last visited

  • Days Won

    56

Reputation Activity

  1. Like
    torstenanders got a reaction from loopyc in Composing various polyphonic textures concisely   
    I am interested in controlling musical textures, i.e., relations between polyphonic parts. I defined a bunch of functions that use the simple polyphonic music representation used also by my function preview-score that I presented shortly (see https://opusmodus.com/forums/topic/902-polyphonic-preview/#comment-2686). Apologies that this post is a bit longer. 
     
    Here is a particularly flexible function: map-parts transforms a polyphonic score. It is a variant of the standard Lisp function mapcar, where a function is applied to every part in a score, but each instrument/part can be given its own function arguments for the transformation. One argument is the respective part of the score. This argument is marked by an underscore (_) in the argument lists.
     
    In the following example, the function pitch-transpose is applied to a score with two very simple parts consisting of only a single note each. This function has two required arguments, a transposition interval (measured in semitones), and the pitch sequence or OMN to transpose. The transposition interval for the first part is 4 (major third upwards), and the underscore marks the position of the violin part to transpose, etc.
     
    Note that you can always see and hear the resulting score by wrapping preview-score around each example. Hopefully these examples sufficiently demonstrate my need to have some shortcut for preview-score :)
    (map-parts '(:vln ((h e4)) :vlc ((h c3))) #'pitch-transpose '(:vln (4 _) :vlc (12 _))) => (:vln ((h gs4)) :vlc ((h c4)))  
    Here are a few more relatively simple application examples. I am first defining some musical material to reduce the length of the remaining definitions. 
    (setf material '((-3h fs4 pp eb4 <) (q e4 < fs4 <) (3h gs4 mp> a4 > bb4 >) (q a4 pp -) (-5h - g4 pp leg eb4 < leg d4 < leg) (q bb4 < e4 <) (5h g4 mp> leg b4 > leg a4 > leg bb4 > leg d4 > leg) (q gs4 pp -)))  
    Now, the next example creates a strict canon formed with the given material -- but without following any counterpoint rules :)  For different parts the material is metrically shifted and transposed.
    This example also shows that map-parts calls can be nested (naturally). The function metric-shift appends some rest before some musical material, but preserves its rhythmical structure, i.e. metrically shifts the material.   
    (map-parts (map-parts `(:vl1 ,material :vl2 ,material :vla ,material :vlc ,material) #'metric-shift '(:vl1 :skip ;; :skip means to leave part unchanged :vl2 (-q _) :vla (-h _) :vlc (-h. _))) #'pitch-transpose '(:vl1 (6 _) :vl2 (4 _) :vla (2 _) :vlc :skip))  
    The next examples shows some simple homorhythmic texture created by randomised transpositions. Each part shares a similar overall pitch profile. Note also that calls can be more concise with a (lambda) function that nests calls to transformation functions -- instead of nesting map-parts as shown above.
    (map-parts `(:vl1 ,material :vl2 ,material :vla ,material :vlc ,material) #'(lambda (transpose seq) ;; static transposition for moving parts into different registers (pitch-transpose transpose ;; randomised transposition of notes in parts (pitch-transpose-n (rnd 10 :low -2 :high 2) seq))) '(:vl1 (7 _) :vl2 (0 _) :vla (-10 _) :vlc (-20 _)))  
    Finally, here is a homophonic texture created by random pitch variants (retrograde, inversion etc.). The global pitch profiles of parts differ here, in contrast to the previous example.
    (map-parts `(:vl1 ,material :vl2 ,material :vla ,material :vlc ,material) #'pitch-variant `(:vl1 (_ :transpose 7 :seed 10) :vl2 (_ :transpose 0 :seed 20) :vla (_ :transpose -10 :seed 30) :vlc (_ :transpose -20 :seed 40)) :shared-args '(:variant ?))  
    All these examples demonstrate very conventional textures, as such textures are more easy to explain.  
     
    For completeness, below is the definition of MAP-PARTS. There are various dependencies that I tried all to add as well. Please let me know if I missed any definition, and apologies in advance.
     
    Best,
    Torsten
    (defun map-parts (score fn part-args &key (parameter nil) (shared-args nil)) "Create or transform a polyphonic score. The original purpose is for creating/transforming musical textures, i.e., relations between polyphonic parts. Applies function `fn' to parts in `score': this function is a variant of the standard Lisp function `mapcar', but specialised for scores. A score is represented in the format discussed in the documentation of the function `preview-score'. Additional arguments for `fn' can be specified in `part-args', and these argument lists can be different for each part. However, one argument is the part of the score. This argument is marked by an underscore (_) in the argument lists. In the following example, the function `pitch-transpose' is applied to a score with two parts. This function has two required arguments, a transposition interval (measured in semitones), and the pitch sequence or OMN to transpose. The transposition interval for the first part is 4 (major third upwards), and the underscore marks the position of the violin part to transpose, etc. ;;; (map-parts '(:vln ((h e4)) ;;; :vlc ((h c3))) ;;; #'pitch-transpose ;;; '(:vln (4 _) ;;; :vlc (12 _))) Args: - score (headerless score): See {defun preview-score} for format description. - fn: A function that expects and returns an OMN sequence or a sequence of parameter values (e.g., lengths, or pitches) as specified in the argument `parameter'. - part-args (plist): Alternating instrument keywords (same as in `score') followed by arguments list for `fn' for that instrument/part. If arguments is :skip, then that part is returned unchanged. - parameter (omn parameter, e.g., :length or :pitch, default nil means processing full OMN expression): If `fn' expects only single parameter to process, then it can be set here. - shared-args (list): For all instruments/parts, these arguments are appended at end end of its part-specific arguments. They are useful, e.g., for keyword arguments. " ;; catching hard-to-find user error... (let* ((instruments (get-instruments score)) (missing-instruments (remove-if #'(lambda (arg-instr) (member arg-instr instruments)) (get-instruments part-args)))) (assert (not missing-instruments) (part-args) "map-parts: Some instruments in `part-args' don't have a matching instrument in `score'. ~A.~%" missing-instruments)) (let ((parts (make-hash-table :test #'equal))) ;; fill hash table, using leading keywords as keys (loop for part in (tu:plist->pairs score) do (setf (gethash (first part) parts) part)) (tu:pairs->plist (loop for instrument-arg-pair in (tu:plist->pairs part-args) for instrument = (first instrument-arg-pair) for part = (gethash instrument parts) for part-omn = (second part) for fn-args = (second instrument-arg-pair) collect (if (equal fn-args :skip) part ; no processing (cons instrument (let ((result (apply fn (append (substitute (if parameter (omn parameter part-omn) part-omn) '_ fn-args) shared-args)))) (list (if parameter (omn-replace parameter result part-omn) result))))) )))) (defun metric-shift (l lengths) "Appends `l' (a length or omn) before `lengths' (a list of lengths or omn), but maintains the metric structure, i.e., the function shifts `lengths' metrically 'to the right' by `l'. Returns an OMN form if lengths is an OMN form, otherwise a length form. Related: assemble-seq (but that does not shift across bars)" (let* ((time-sigs (get-time-signature lengths)) (result (omn-to-time-signature (cons l (flatten lengths)) time-sigs))) (if (omn-formp lengths) result (omn :length result)))) ; (metric-shift '-h '((q q q q) (q q q q))) ; (metric-shift '(h g4) '((q c4 q d4 q e4 q f4) (q c4 q d4 q e4 q f4))) (defun get-instruments (score) "Returns all instruments of `score', a headerless score (see {defun preview-score} for its format)." (at-even-position score)) (defun at-even-position (in-list) (at-position in-list 2 0)) (defun at-position (in-list factor offset) "Returns a list containing every factor-th elements of in-list starting at offset" (mapcar #'(lambda (i) (nth i in-list)) (arithmeric-series factor offset (ceiling (/ (length in-list) factor))))) (defun arithmeric-series (factor offset length) (let (result) (reverse (dotimes (i length result) (push (+ (* i factor) offset) result))))) (defun plist->pairs (plist) (loop :for (key val) :on plist :by #'cddr :collect (list key val))) (defun pairs->plist (pairs) (one-level-flat pairs)) (defun one-level-flat (list) "flatens one level of the given form. Example: (one-level-flat '(((note) (note)) ((pause) (pause)) ((note)))) -> ( (note) (note) (pause) (pause) (note))" (apply #'append list))  
  2. Like
    torstenanders got a reaction from opmo in sorting algorithms   
    Simply slightly edit the algorithms below by inserting some additional output. Below I simply took one of the algorithms you linked and added a single print statement. If instead you want to collect all the results, then just accumulate them in some variable with a scope surrounding your algorithm function.

    (defun bubble-sort/naive (sequence)
      (let ((end (length sequence)))
        (labels ((compare-and-swap (index modified)
                   ;; print intermediate results
                   (print sequence)
                   (if (= index (1- end))
                       (if modified (compare-and-swap 0 nil) (values))
                       (let ((index+1 (1+ index)))
                         (if (> (elt sequence index) (elt sequence index+1))
                             (let ((x (elt sequence index)))
                               (setf (elt sequence index) (elt sequence index+1)
                                     (elt sequence index+1) x)
                               (compare-and-swap index+1 t))
                             (compare-and-swap index+1 modified))))))
          (unless (< end 2)
            (compare-and-swap 0 nil))
          sequence)))
     
    (bubble-sort/naive '(3 1 9 5 3 6 4 2 3 7))

    Best,
    Torsten
  3. Like
    torstenanders got a reaction from opmo in sorting algorithms   
    Simply slightly edit the algorithms below by inserting some additional output. Below I simply took one of the algorithms you linked and added a single print statement. If instead you want to collect all the results, then just accumulate them in some variable with a scope surrounding your algorithm function.

    (defun bubble-sort/naive (sequence)
      (let ((end (length sequence)))
        (labels ((compare-and-swap (index modified)
                   ;; print intermediate results
                   (print sequence)
                   (if (= index (1- end))
                       (if modified (compare-and-swap 0 nil) (values))
                       (let ((index+1 (1+ index)))
                         (if (> (elt sequence index) (elt sequence index+1))
                             (let ((x (elt sequence index)))
                               (setf (elt sequence index) (elt sequence index+1)
                                     (elt sequence index+1) x)
                               (compare-and-swap index+1 t))
                             (compare-and-swap index+1 modified))))))
          (unless (< end 2)
            (compare-and-swap 0 nil))
          sequence)))
     
    (bubble-sort/naive '(3 1 9 5 3 6 4 2 3 7))

    Best,
    Torsten
  4. Like
    torstenanders got a reaction from Stephane Boussuge in tonality-map: controlling in which octave tones occur   
    I like how the function tonality-map allows specifying some input harmony (called tonality) and raw music, where the "raw" music is then quasi-quantised into the given harmony. 
     
    However, I would like to control in which octaves specific tones are allowed to occur. tonality-map allows specifying an underlying harmony that ranges over multiple octaves, but it seems that internally only octave-less pitch classes are used, and any tone in the harmony can occur in any octave in the result. By contrast, in the spectral tradition of music thinking, you change the underlying spectrum if you scramble in which octaves pitches occur. For example, if you have a spectrum or chord that approximates the overtone series, then that spectrum sounds rather consonant, regardless how far up in the overtone series you allow tones to be included. However, if you then randomly octave-transpose the pitches of this spectrum/chord, then it can become much more dissonant, without changing any pitch classes.
     
    To be more specific here is a dummy example with simple traditional chords where tones are distributed across octaves in a certain way. 
     
    (tonality-map   ;; underlying harmony or spectra  '((c4g4e5b5d6) (g3d4b5f5a4))  ;; input music  '((h c4f5 cs4fs5) (d4g5 cs4gs5) (eb4as5 f4a5) (e4gs5 c4gs5))  ;; harmonic rhythm  :time '(w w w_w)) => ((h c4e5 c4g5) (h a3d5 g3d5) (h e4b5 e4b5) (h e4g5 c4g5))  
    As you can see, the tone G in the first tonality occurs only in octave 4, but in the result, in the second chord of the first bar (still following the first tonality) we have a g5 instead. Now, you might feel that the g5 does not musically do any harm, but in the second tonality, there is an A only in octave 6, while in the function output in the related third chord the A occurs three octaves lower in octave 3, where it greatly increases the dissonance degree of this chord/scale. 
     
    So, is there a way to restrict the octaves of tones in the result to be restricted to the octaves of these tones in the respective tonalities? Alternatively, is there another function that complements tonality-map, where I can force some "raw" music to follow some underlying harmony with a given harmonic rhythm, and where the octaves of the resulting pitches can be restricted?
     
    Thank you! 
     
    Best,
    Torsten
  5. Like
    torstenanders reacted to opmo in gen-loop not working   
    The GEN-LOOP macro is now supporting the INIT-SEED sequential seed numbers 
    Ver. 1.2.23227
  6. Like
    torstenanders reacted to opmo in Posting seed value   
    I think it will help us to work even more freely and this is what we expect from Opusmodus :-) Thank you for the suggestion.
  7. Like
    torstenanders got a reaction from lviklund in Posting seed value   
    Citing the documentation: "INIT-SEED can be used at the start of algorithmic compositions employing high degrees of randomness in order to create consisted results, without having to worry about supplying seed N values to all the other functions which may also use random generation."
     
    The function init-seed does not give you any new value actually (it just returns its argument), but in the background as a side-effect initialises the random generation of all Opusmodus random functions, so that their results are consistent on re-evaluation. 
     
    Just try removing it in your function :)
     
    Besides, programs that reduce any changes to global states and side effects to the absolute minimum (which is most often using no stateful programming at all) are more easy to maintain and expand. Your little function introduces two stateful operations that are not needed here: setf (better use a local let) and init-seed. (This is even more important when writing concurrent code, which can be necessary when doing real-time stuff, e.g., when you have multiple processes running in parallel changing the same global variables without further precaution.)   
     
    Best,
    Torsten
  8. Like
    torstenanders reacted to opmo in Posting seed value   
    I found a way to display an internal seed if seed is nil (random process).
     
    Example without a seed (each time different result):
    (rnd-number 12 1 10) ? rnd-number :seed 678732 => (10 6 10 6 1 9 5 6 10 2 4 2)  
    Now the same function with a manually "lock" using the :seed
    (rnd-number 12 1 10 :seed 678732) ? rnd-number :seed 678732 => (10 6 10 6 1 9 5 6 10 2 4 2)  
    (rnd-pick '((q c4 p d4 ff s a4 stacc) (s e4 app q c5 d5 pp))) ? rnd-pick :seed 598336 => (q c4 p d4 ff s a4 stacc)  

     
    I hope everybody will be happy with the solution  :-)
    To add this functionality to all the functions using random seed will take some time to do.
    Hopefully tomorrow :-)
     
    Best,
    Janusz
  9. Like
    torstenanders got a reaction from AM in Easiest way to add click track / Metronome in OM   
    Wow, that is fancy :)
     
    Here is one that is even slightly more fancy, where the resulting softer velocity values also react to the velocity input, and also the pitch of easy beats is slightly different.   
     
    (defun metronome (sequence &key (pitch 'c4) (velocity 90))
      (let* ((ts (get-time-signature sequence))
             (len (loop for i in ts
                    collect (gen-repeat (car i) (list (/ 1 (second i))))))
             (vel (loop for i in ts
                    collect (append (get-velocity (list velocity) :type :symbol)
                                    (gen-repeat (1- (car i)) 
                                                (get-velocity (list (- velocity 25))
                                                              :type :symbol)))))
             (pitch (loop for i in ts
                      collect (cons pitch
                                    (pitch-transpose 
                                     -2 (gen-repeat (1- (car i)) pitch))))))
        (make-omn :length len
                  :pitch pitch
                  :velocity vel)))
     
    TA
  10. Like
    torstenanders got a reaction from AM in Easiest way to add click track / Metronome in OM   
    Wow, that is fancy :)
     
    Here is one that is even slightly more fancy, where the resulting softer velocity values also react to the velocity input, and also the pitch of easy beats is slightly different.   
     
    (defun metronome (sequence &key (pitch 'c4) (velocity 90))
      (let* ((ts (get-time-signature sequence))
             (len (loop for i in ts
                    collect (gen-repeat (car i) (list (/ 1 (second i))))))
             (vel (loop for i in ts
                    collect (append (get-velocity (list velocity) :type :symbol)
                                    (gen-repeat (1- (car i)) 
                                                (get-velocity (list (- velocity 25))
                                                              :type :symbol)))))
             (pitch (loop for i in ts
                      collect (cons pitch
                                    (pitch-transpose 
                                     -2 (gen-repeat (1- (car i)) pitch))))))
        (make-omn :length len
                  :pitch pitch
                  :velocity vel)))
     
    TA
  11. Thanks
    torstenanders reacted to opmo in Changing MIDI channels with articulations   
    In 1.2.23106 I made few correction to the accents relative duration.
    Only stacc and stacs is a bit shorter now, all other accents will not change the midi duration.
     
    Janusz
  12. Like
    torstenanders reacted to Yuichi Yamamoto in Quantization   
    Hi people,

    Though I've been using Opusmodus for about 2 years now, and in great love with it,
    I haven't really had a chance to share my work on the community.

    But here I have my piano piece vastly written with Opusmodus, and guess what, it's got a cool video, too!

    Yamaha corporation kindly offered me to use their automated piano for filming, and I think an algorithmic music like this goes very well with it!

    Yuichi
  13. Like
    torstenanders reacted to opmo in Quantization   
    Thank you Yuichi for sharing, great piece, I am very happy to see Opusmodus is able to help you realise your ideas.
  14. Like
    torstenanders got a reaction from Stephane Boussuge in Playback of articulations   
    It would be useful for playback, if certain articulations would affect the sounding note durations and velocity values. With some articulations this already seems to be the case (e.g., stacc seems to reduce note durations by 50%), while other articulations are ignored during playback in that regard (e.g., leg is notated, but makes seemingly no difference in playback -- notes played legato are not overlapping, as would be expected, nor are non-legato notes very slightly separated). Other articulations are seemingly even wrongly set (e.g., marc is an accent, but it instead shortens the note).
     
    Here is a little example demonstrating the problem, which results in the attached MIDI file.
    EDIT: The upload failed, but you can easily listening to this or export it to MIDI yourself.
     
    '((e c4 leg d4 e4 leg f4) (q g4 marc -q -h))
     
    Can users somewhere set/correct these playback settings?
     
    BTW: Ideally settings would allow for code (functions) evaluationed on the fly. That would allow, e.g., to add custom humanisation, e.g., to slightly vary the length of staccato articuations, or the velocity values set by non-legato notes.... 
     
    Thanks!
     
    Best,
    Torsten
  15. Like
    torstenanders reacted to Stephane Boussuge in Playback of articulations   
    i agree totally :-)
     
    Actually, i always finish my scores in a DAW for such reason but with this new possibilities into Opusmodus, it would be no more necessary and i love the idea to stay in Opusmodus from start to end of the music process.
     
    SB.
  16. Like
    torstenanders got a reaction from Stephane Boussuge in Playback of articulations   
    It would be useful for playback, if certain articulations would affect the sounding note durations and velocity values. With some articulations this already seems to be the case (e.g., stacc seems to reduce note durations by 50%), while other articulations are ignored during playback in that regard (e.g., leg is notated, but makes seemingly no difference in playback -- notes played legato are not overlapping, as would be expected, nor are non-legato notes very slightly separated). Other articulations are seemingly even wrongly set (e.g., marc is an accent, but it instead shortens the note).
     
    Here is a little example demonstrating the problem, which results in the attached MIDI file.
    EDIT: The upload failed, but you can easily listening to this or export it to MIDI yourself.
     
    '((e c4 leg d4 e4 leg f4) (q g4 marc -q -h))
     
    Can users somewhere set/correct these playback settings?
     
    BTW: Ideally settings would allow for code (functions) evaluationed on the fly. That would allow, e.g., to add custom humanisation, e.g., to slightly vary the length of staccato articuations, or the velocity values set by non-legato notes.... 
     
    Thanks!
     
    Best,
    Torsten
  17. Like
    torstenanders got a reaction from JulioHerrlein in Changing MIDI channels with articulations   
    It would also be useful, e.g., for combining sample instruments by different brands etc. where certain articulations are available in one plugin, and others in another.
     
    Best,
    Torsten
  18. Like
    torstenanders reacted to opmo in Changing MIDI channels with articulations   
    We will add this, I love it too :-)
  19. Thanks
    torstenanders got a reaction from JulioHerrlein in Changing MIDI channels with articulations   
    How is it possible to change the MIDI output channel of an instrument? For example, when writing for organ, you have two staffs for the right and left hand, but hands can change manuals, and this is best represented by different MIDI channels. Is there a way to define articulations that result in changing the MIDI channel?
     
    Thanks!
     
    Best,
    Torsten
  20. Thanks
    torstenanders got a reaction from JulioHerrlein in Modelling Tonality (1) Diatonic Transposition (some intuitions)   
    You already know how to translate a MIDI pitch number into a representation consisting of two pieces of information, a pitch class integer and an octave integer. The conversion can be applied in both directions.
     
    Now, you can apply a similar conversion to a pitch class integers, converting them into a scale degree (integer) and an accidental (another integer), depending on a scale (a set of pitch classes). Again, this conversion can be done in both ways (there are multiple solutions if you allow for enharmonic equivalence).
     
    Once you have such a representation, you can then do diatonic transpositions (depending on whatever scale) within the scale degree domain, and finally translate your  results back into pitch classes, or MIDI pitch numbers.
     
    I have a draft of a paper discussing this formally. A preliminary version of this paper has been published at SMC, http://uobrep.openrepository.com/uobrep/handle/10547/622264 Don't get scared away by the constraint programming aspect. Such conversions can also be implemented as plain deterministic functions.
     
    This can be seen as a kind of mapping, if you want, if you also see the relation between MIDI pitch numbers and pitch classes as a mapping :)
     
    Best,
    Torsten
     
  21. Like
  22. Thanks
    torstenanders got a reaction from JulioHerrlein in Modulo 12 Function ?   
    You can roll such a function easily yourself. The modulus function is part of Common Lisp.
     
    ;; modulus 12 of 60
    (mod 60 12)
    ; => 0
     
    (defun mod12 (xs)
      (loop for x in xs
        collect (mod x 12)))
     
    (mod12 '(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 24 48))
    ; => (0 1 2 3 4 5 6 7 8 9 10 11 0 1 2 3 4 5 6 7 0 0)
     
    To empower yourself, once you know how to program a simple function, you simply need to google for the modulus function
     
      http://lmgtfy.com/?q=common lisp modulo
     
    :)
     
    Best,
    Torsten
  23. Thanks
    torstenanders got a reaction from JulioHerrlein in Modelling Tonality (1) Diatonic Transposition (some intuitions)   
    You already know how to translate a MIDI pitch number into a representation consisting of two pieces of information, a pitch class integer and an octave integer. The conversion can be applied in both directions.
     
    Now, you can apply a similar conversion to a pitch class integers, converting them into a scale degree (integer) and an accidental (another integer), depending on a scale (a set of pitch classes). Again, this conversion can be done in both ways (there are multiple solutions if you allow for enharmonic equivalence).
     
    Once you have such a representation, you can then do diatonic transpositions (depending on whatever scale) within the scale degree domain, and finally translate your  results back into pitch classes, or MIDI pitch numbers.
     
    I have a draft of a paper discussing this formally. A preliminary version of this paper has been published at SMC, http://uobrep.openrepository.com/uobrep/handle/10547/622264 Don't get scared away by the constraint programming aspect. Such conversions can also be implemented as plain deterministic functions.
     
    This can be seen as a kind of mapping, if you want, if you also see the relation between MIDI pitch numbers and pitch classes as a mapping :)
     
    Best,
    Torsten
     
  24. Like
    torstenanders reacted to opmo in euclidean to interval   
    PATTERN-MAP
     
    (pattern-map '((1 0) 2) (gen-binary-euclidean 1 14 8 8)) => (2 1 2 2 2 1 2 2)  
  25. Like
    torstenanders reacted to Stephane Boussuge in Requiem for Nigel   
    I consider Nigel was my most important music comp. teacher even if we had mainly only mails exchange and few meetings and i will be always thankful to him for his always very constructives advices.
     
    S.
×