Jump to content

torstenanders

Members
  • Posts

    497
  • Joined

  • Last visited

Everything posted by torstenanders

  1. Dear Janusz, To be clear: I do not expect that there will be a keyboard shortcut for every user added to Opusmodus. Instead, if you just share an example of code how to add a short key (and ideally a menu entry) to the Hemlock editor, that would be great. Thanks again! Best, Torsten
  2. 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))
  3. Sorry, I am using multiple libraries of own functions in the background. Easy to miss one :) EDIT: Sorry, the function one-level-flat was still missing and has been added now. I also removed the package prefix above. (defun plist->pairs (plist) (loop :for (key val) :on plist :by #'cddr :collect (list key val))) ; (plist->pairs '(:length 1/16 :pitch 60 :velocity 30)) ; -> ((:LENGTH 1/16) (:PITCH 60) (:VELOCITY 30)) (defun pairs->plist (pairs) (one-level-flat pairs)) ; (pairs->plist '((:LENGTH 1/16) (:PITCH 60) (:VELOCITY 30))) ; -> (:LENGTH 1/16 :PITCH 60 :VELOCITY 30) (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))
  4. I really like how Opusmodus allows to preview monophonic snippets (and other material, like interval sequences). No need to explicitly call some function or connect to some editor, as in other composition systems -- just use a keyboard shortcut to see and hear an intermediate result. However, what I miss is notating/auditioning intermediate results of polyphonic music with a single shortcut. So, I defined the function preview-score, based on def-score. The function expects a polyphonic score in the slightly simplified format we discussed earlier. Here is a minimal example. (preview-score '(:vln ((q b4 a4 h g4)) :vlc ((h g3 b3)))) If I want to control further notation or playback parameters, preview-score provides arguments for that, but that is basically the same as using def-score directly. Instead, such parameters can be defined only once with global variables, and then used for multiple calls to preview-score. Here is some example setting. (setf *default-preview-score-instruments* '(:vln (:program 'violin :sound 'gm) :vlc (:program 'cello :sound 'gm))) (defparameter *default-preview-score-header* '(:title "Opus magnum" :tempo 80)) Janusz: How can I define a keyboard shortcut that calls the function preview-score with the score returned by a selected code region (or the Lisp expression just before the cursor)? Thanks! For completeness, the definition of preview-score is below. Best, Torsten ;;; just some dummy settings for now (defparameter *default-preview-score-instruments* '(:vln (:program 'violin :sound 'gm) :vlc (:program 'cello :sound 'gm)) "Settings for each instrument used by `preview-score'. The format is a plist where keys are the instrument labels, and values a list with the actual settings. For format of these settings are the same as instrument settings for `def-score' with keywords like :sound, :channel etc. -- except for they key :omn.") ;;; just some dummy settings for now (defparameter *default-preview-score-header* '(:title "Opus magnum" :tempo 80) "Global score settings used by `preview-score'. The format is a plist where keys are the instrument labels, and values a list with the actual settings. The format is the same as the header settings for `def-score' with keywords like :title, :key-signature etc.") (defun preview-score (score &key (name 'test-score) (instruments *default-preview-score-instruments*) (header *default-preview-score-header*)) "Notates and plays a score in a format slightly simpler than expected by def-score, i.e., without its header. Args: - score (plist): a headerless score. See below for its format. - name (symbol): The score name. - instruments (plist): Keys are instrument labels, and values a list with the actual settings. These settings have the same format as instrument settings for `def-score' with keywords like :sound, :channel etc. -- except for they key :omn. - header (plist): The format is the same as the header settings for `def-score' with keywords like :title, :composer, :key-signature etc. Score format: ;;; (<part1-name-keyword> <part1-OMN> ;;; <part2-name-keyword> <part2-OMN> ;;; ...) Example: ;;; (preview-score ;;; '(:vln ((q g4) (q. c5 e d5 q e5 f5) (h. e5)) ;;; :vlc ((q g3) (q c4 b3 a3 g3) (h. c3))) ;;; :instruments '(:vln (:program 'violin :sound 'gm) ;;; :vlc (:program 'cello :sound 'gm)) ;;; :header '(:title \"Opus magnum\" ;;; :tempo 80)) " ;; Using eval is problematic (https://stackoverflow.com/questions/2571401/why-exactly-is-eval-evil/), ;; but hard to avoid for a dynamically created def-score expression that requires splicing with ,@. ;; Possible alternative would be to define preview-score as macro, but then arguments are not evaluated. (eval `(def-score ,name ;; quote all header args, because symbol values must be quoted... ,(mapcar #'(lambda (x) `',x) (append header ;; add default vals of required header args at end -- they are overwritten by args given (list :key-signature 'atonal ;; By default, use implicit time signature of 1st part :time-signature (om:get-time-signature (second score)) :tempo 70))) ,@(mapcar #'(lambda (part) (let ((part-symbol (first part)) (part-omn (second part))) (list* part-symbol :omn `(quote ,part-omn) (getf instruments part-symbol)))) (plist->pairs score))) ) (audition-musicxml-last-score) *last-score*) #| ; mini test (preview-score '(:vln ((q g4) (q. c5 e d5 q e5 f5) (h. e5)) :vlc ((q g3) (q c4 b3 a3 g3) (h. c3))) :instruments '(:vln (:program 'violin :sound 'gm) :vlc (:program 'cello :sound 'gm)) :header '(:title "Opus magnum" :tempo 80)) |#
  5. > you would like to have some code which produces/composes a "nearly complete piece" for you Actually, this is not what most of the techniques listed by Wim or me are. Instead, computer-aided composition techniques offer the composer to control the result on a higher level, but by offering various dimensions of control and by often generating intermediate results that are then further processed either by hand or with other algorithms, and not just a single bottom to start the computation of a new piece. The opposite can be called automatic composition, and much of David Cope's work is indeed a good example of that. Details on his publications etc. can be found at his website, http://artsites.ucsc.edu/faculty/cope/. Best, Torsten
  6. Dear Julio, Once you installed a demo version, check out the documentation. Under View > Utilities > Show Documents, the 2nd listed file (1st Element - Length) is what you are looking for. For a translation from another programming language, the most strait forward way to represent rhythms might be by simply using fractions throughout, though computing fractions (not floats!) in Puredata could be a bit tricky. Best, Torsten
  7. > more high-level algorithms I cannot answer your question concerning built-in algorithms, but if you are looking for further ideas what could be added, here is some related literature. A good general overview of algorithmic composition techniques, from a technical point of view. Nierhaus, G. (2009) Algorithmic Composition: Paradigms of Automated Music Generation. Wien, New York: Springer. Discussions of compositional applications in OpenMusic (likewise implemented in Common Lisp, though the main interface is a visual language) by various composers. Agon, C. et al. (eds.) (2006) The OM Composer’s Book. 1. Delatour France. Bresson, J. et al. (eds.) (2008) The OM Composer’s Book. 2. Editions Delatour France / Ircam. Hirs, R. & Gilmore, B. (eds.) (2009) Contemporary Compositional Techniques and OpenMusic. Collection Musique/Sciences. IRCAM/Delatour. Bresson, J. et al. (eds.) (2016) The OM Composer’s Book . 3. Paris; Sampzon: Editions Delatour France. Most of you likely already know the book by Nigel Morgan, containing discussions of compositional applications in Opusmodus itself. Morgan, N. & Legard, P. (2015) Parametric Composition: Computer-Assisted Strategies for Human Performance. West Yorkshire, UK: Tonality Systems Press. The perhaps most important algorithmic composition technique of Common Music (also implemented in common Lisp) and SuperCollider are patterns / item streams, which can be nested. http://commonmusic.sourceforge.net/cm/res/doc/cm.html#patterns Taube, H. (2004) Notes from the Metalevel. London and New York: Taylor & Francis. Online: http://www.moz.ac.at/sem/lehre/lib/cm/Notes from the Metalevel/00/contents.html The libraries of the venerable PatchWork and its successors PWGL and OpenMusic (all Common Lisp) provide ideas for various approaches, some already mentioned above. Below are links to relevant link collections. OpenMusic libraries: http://repmus.ircam.fr/openmusic/libraries PWGL libraries: http://www2.siba.fi/pwgl/downloads.html Another successful technique, implemented in multiple PWGL and OpenMusic libraries and beyond is constraint programming. I did a lot of research in this area and therefore quote some own publication here. Anders, T. & Miranda, E. R. (2011) Constraint Programming Systems for Modeling Music Theories and Composition. ACM Computing Surveys. 43 (4), 30:1–30:38. Online: https://pdfs.semanticscholar.org/7d0e/783e5bb1c35a871a45e72fddaf7bf3db5d28.pdf. Of course, there is much more literature on algorithmic composition (computer-aided composition) out there, but the above literature gives a good starting point to study more general composition techniques and their applications. Best, Torsten
  8. Does Opusmodus already support the notation of nested tuplets? Nested tuplets are easily expressed directly with fractions. For example, for the nested rhythm found at http://klangnewmusic.weebly.com/direct-sound/lets-talk-rhythm-part-2-nested-tuplets I can write the following. '(2/15 2/15 2/15 2/25 2/25 2/25 2/25 2/25 1/20 3/20) The playback of this rhythm works fine, but the notation is unnecessarily complex. Not even the simple time signature 2/4 is detected. Also, I could not find the length symbols for the required durations. For example, what is the symbol for the fraction 2/15? Thanks! Best, Torsten
  9. > Does this mean GEN-CONTROLLER always returns at most the number of input values? Is that intended behaviour, and if so why? > It is important to make a DEF-SOUND-SET file for your system first. I understand the usefulness for libraries with key-switches etc, but why for a whole DAW? Would you define one for Logic or Cubase as well? > I just tested the Ableton Live and Opusmodus Live Coding system - no problems here, but only on 1 port. It sometimes works for me as well, but sometimes does't, which is a tricky problem. Would like to know if anyone else had that problem. > The :tuning has no timing resolution control. To control the timing you must use :controller keyword. Does that mean so far Opusmodus does not support continuously sending pitchbend messages, only per note onset? What about aftertouch? Thanks! Torsten
  10. If you have Ableton Live, there is a MIDI bottom in the top-right corner. Click that to enter the MIDI map. Then just click on a supported controller in the Live interface and move any MIDI controller to activate a MIDI link. You can edit these in the left-hand list in the MIDI map. Torsten
  11. Did anyone else of you sometimes have problem getting Opusmodus and Ableton Live talk to each other via an IAC bus (on a Mac)? I confirmed that Ableton can receive MIDI notes from other sources on this bus, and that MIDI data from Opusmodus is received by other programs on this bus. Also, the MIDI track indicator shows that the program receives MIDI input from Opusmodus, and just in case in the MIDI track I made sure that all MIDI inputs and channels are received. I also created a new Live set to make sure I did not accidentally do some stupid setting anyway. It worked earlier and strangely it works again after a restart, but I now ran multiple times into the situation that my Ableton tracks gets no notes from Opusmodus, so something seems to be fishy here. Anyone else ran into such problems? Any workaround perhaps? Thanks! Best, Torsten
  12. Actually, in Ableton I can control more params with CC messages via its MIDI map, but it would still be interesting to hear how to control pitchbend and aftertouch. I am aware of the :tuning parameter of DEF-SCORE instruments, but does that support timing information as the :controller argument as well? Thanks! Best, Torsten
  13. Having support for MIDI controller data is great, but is it also possible to send pitchbend and aftertouch? Some Ableton Live instruments need that for more complex control (e.g., Collusion), as otherwise only the Modwheel and no other CC message is supported. Also, I have difficulties understanding what exactly the gen-controller argument span does. Judging from the way the term span is used in the example Controllers 1, I assume this controls the time span the resulting controller movement takes, measured in symbolical note duration, where 1 means a whole note (the term span is used very differently elsewhere, easily causing confusion -- a slightly different argument name would help). So, in effect it should control the length of the resulting list (depending on the set time resolution). However, this is not always the case. What am I missing here? Is there a bug? larger span values result in longer lists, as expected (length (gen-controller 1 (gen-sine 1024 4 1))) => 128 (length (gen-controller 2 (gen-sine 1024 4 1))) => 256 (length (gen-controller 4 (gen-sine 1024 4 1))) => 512 however, for shorter input lists this is not the case (length (gen-controller 1 (gen-sine 128 4 1))) => 128 (length (gen-controller 4 (gen-sine 128 4 1))) => 128 (length (gen-controller 1 (gen-sine 12 4 1))) => 12 (length (gen-controller 4 (gen-sine 12 4 1))) => 12 Does this mean gen-controller always returns at most the number of input values? Further, how does the span affect the result (if it does so) -- are the values simply looped/cut-off or are they interpolated to stretch/shrink over the specified time span? Thanks a lot! Best, Torsten
  14. > in my view it would be nice to EXTEND the "make-omn/single-events"-structure by x-add-datas... Yes, I was wishing for that as well for some time -- I designed extendable music representations myself for other applications before... However, meanwhile I understand that this is not really possible nor desirable for Opusmodus. The current OMN format is already rather flexible (kind of a language), that already allows for pretty much arbitrary symbols as articulations, and articulation combinations (as single symbol joined with +). Allowing for an arbitrary number of symbols as articulations or other data would make parsing (detecting note boundaries) much harder, or would require that OMN would do what many other music representations out there are doing: that individual notes are represented as separate data elements (e.g., separate lists, as output by single-events). Such a representation would be less concise -- you would less want to directly type it by hand anymore. Either approach to extensions would also break very many existing functions. The good news is that by merging multiple articulations in a single articulation symbol you do not need to break the existing functionality, and can still have all the extensibility you need. The only restrictions are that an articulation is always a symbol that has been declared as such before (e.g., numerical data is a bit tricky here, yes). Beyond that, nothing stops you from rolling you own music representation in whatever format you want, but you may want then to always have two additional functions that transform your format into the OMN format and back :) > b) in my way i see completely transparent and easy what's up, and not a mixture of text-attributes/data...? Multiple articulations combined in a single symbol are well readable, I would say -- something that cannot always be said of, say, object-oriented data abstractions of other algorithmic composition systems. I would argue that together with functions such as merge-articulations and disassemble-articulations that I defined above, you can see this as a data abstraction [1], which is extendable. > c) if I could change the OPMO SINGLE-EVENTS-structure, i would extend it like I did ...and not mixing it, isnt' it much more "logic" (but perhaps not for a programmer-brain :-)) In principle there is not really much of a difference between these two positions here, it is up to you to decide. The main downside of your custom format is that you loose all direct interoperability with OMN functions and functionality (e.g., snippet preview), but occasionally the advantages will be outweighing such downsides. In this particular case the only advantage of your approach that I see is that you can (more easily) add numerical data. > a) isn't it better to "seperate" the BASIC-OMN-structure from the additionals? in a way, i'm more independet if OPMO changes some things? That is better answered by Janusz. Best, Torsten [1] https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-13.html#%_chap_2, chapter in Abelson, H. et al. (1985) Structure and Interpretation of Computer Programs. Cambridge, MA: MIT Press; a book that is extremely useful for every Lisp programmer, though it uses the Lisp dialect Scheme.
  15. > You might find the function OMN-RECONSTRUCT useful in your work: Thanks! As always, would be great to have this documented so that users can find this on their own :) More generally, are their some reasons to prefer length symbols beyond their conciseness? Fractions are often more easy to comprehend for me, but I guess this is just a question of time... Best, Torsten
  16. Here is a revision (defun make-corresponding-rest (event) "Turns a single OMN note into a rest of the same note value. Rests remain rests, and rest articulations are preserved. Example: (make-corresponding-rest '(h c4))" (let ((len (omn-encode (first event)))) (cons ;; rests should remain rests (if (> len 0) (* len -1) len) (omn :rest-articulation event)))) (defun insert-articulation (flat-omn-list &rest articulations) "Merge in one or more lists of articulations to an OMN expression. Example: ;; added nil for the rest (extended-single-events '(e c4 mp arco e. d4 -h e. p pizz e e4 arco) '(ponte tasto nil ponte tasto)) => (e c4 mp arco+ponte e. d4 mp tasto -h e. d4 p pizz+ponte e e4 p arco+tasto) BUG: does not skip rests. Wait for omn-replace supports composite articulations to fix" (apply #'append (loop for event in (single-events flat-omn-list) for data in (matrix-transpose optional-data) when (length-notep (first event)) collect (let ((event-art (fourth event))) (append (subseq event 0 3) (list (merge-articulations (if event-art (cons event-art data) data))))) else collect event))) #| ;; automatic orchestration application (add-text-attributes '(trp "trp") '(fl "fl") '(clar "clar")) (extended-single-events '(e c4 mp stacc e. d4 -h e. c4 p ord e e4 stacc) '(trp fl trp trp fl clar) '(flt tr1 tr2 flt tr1 tr2)) ; => '(e c4 mp stacc+trp+flt e. d4 mp fl+tr1 -h e. c4 p ord+trp+flt e e4 p stacc+fl+tr1) |# (defun filter-note-parameters (flat-omn-list parameter &key (remove-non-matching? nil)) "Checks every note whether it contains `parameter'. All notes containing the parameter are preserved, all other notes are turned into rests. If a note contains a combination of articulations, all of them are checked. Args: flat-omn-list: flat OMN list parameter: a length, pitch, OMN velocity or single articulation remove-rests? (default nil): if true, all notes that do not match are removed instead of turned into rests. Examples: (filter-note-parameters '(e c4 mp arco+ponte e. d4 mp tasto -h e. c4 p pizz+ponte e e4 p arco+tasto) 'e.) => (-1/8 e. d4 mp tasto -1/2 e. c4 p pizz+ponte -1/8) (filter-note-parameters '(e c4 mp arco+ponte e. d4 mp tasto -h e. c4 p pizz+ponte e e4 p arco+tasto) 'arco) => (e c4 mp arco+ponte -3/16 -1/2 -3/16 e e4 p arco+tasto) (filter-note-parameters '(e c4 mp arco+ponte e. d4 mp tasto -h e. c4 p pizz+ponte e e4 p arco+tasto) 'arco :remove-non-matching? T) => (e c4 mp arco+ponte e e4 p arco+tasto) " (remove :not-matching (apply #'append (loop for event in (single-events flat-omn-list) when (and (length-notep (first event)) (member (omn-encode parameter) (append (list (omn-encode (first event))) (subseq event 1 3) (disassemble-articulations (fourth event))))) collect event else collect (if remove-non-matching? '(:not-matching) (make-corresponding-rest event)))))) #| ;; continue automatic orchestration application (filter-note-parameters '(e c4 mp stacc+trp+flt e. d4 mp fl+tr1 -h e. c4 p ord+trp+flt e e4 p stacc+fl+tr1) 'trp) ; => (e c4 mp stacc+trp+flt -3/16 -1/2 e. c4 p ord+trp+flt -1/8) (filter-note-parameters '(e c4 mp stacc+trp+flt e. d4 mp fl+tr1 -h e. c4 p ord+trp+flt e e4 p stacc+fl+tr1) 'e.) |#
  17. Feel free to roll your own OMN extensions, if that benefits you, but in this case I don't think that is necessary. It is one of the strength of Opusmodus, that it has a rather simple music representation that is used by so many functions, because that allows you to combine them all. Once you roll your own data you cannot apply other OMN functions anymore to that data. Put differently "It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures." (Alan Perlis -- you can search the web for that quote, lots of discussion there :) OK, I suggest you slightly change your functions such that all your data is wrapped up in a single articulation symbol. This function can handle an arbitrary number of additional data streams to merge into your OMN list. Also, I kept it a flat list, as you do not mean to have bars here. (extended-single-events '(e c4 mp stacc e. -h e. p ord e e4 stacc) '(trp fl trp trp fl clar) '(flt tr1 tr2 flt tr1 tr2)) ; => '(e c4 mp stacc+trp+flt e. c4 mp fl+tr1 -h e. c4 p ord+trp+flt e e4 p stacc+fl+tr1) If you declare all your data first as text attributes, then you can even show it in a score (add-text-attributes '(trp "trp") '(fl "fl") '(clar "clar")) Now lets do the filtering you suggest. I changed your keyword argument into a standard argument, because that argument is always required. Keyword arguments are best suited for optional arguments that document themselves. Also, the result is again a flat list. After declaring your attributes you can directly notate it :) (filter-events-by '(e c4 mp stacc+trp+flt e. c4 mp fl+tr1 -h e. c4 p ord+trp+flt e e4 p stacc+fl+tr1) 'trp) ; => (e c4 mp stacc+trp+flt e. -h e. c4 p ord+trp+flt e) With a slight change of the function below you could also turn all notes that do not meet your test condition into a rest. The function definitions are below. BTW: your function memberp does pretty much what the function member already does and is therefore not needed, because in Lisp any value that is not nil is considered meaning true. Best, Torsten (add-text-attributes '(trp "trp") '(fl "fl") '(clar "clar") ) (defun extended-single-events (flat-omn-list &rest optional-data) (apply #'append (loop for event in (single-events flat-omn-list) for data in (matrix-transpose optional-data) when (length-notep (first event)) collect (let ((event-art (fourth event))) ; (format t "event: ~A, data: ~A, event-art: ~A~%" event data event-art) (append (subseq event 0 3) (list (merge-articulations (if event-art (cons event-art data) data))))) else collect event))) #| ; test (extended-single-events '(e c4 mp stacc e. -h e. p ord e e4 stacc) '(trp fl trp trp fl clar) '(flt tr1 tr2 flt tr1 tr2)) ; => '(e c4 mp stacc+trp+flt e. c4 mp fl+tr1 -h e. c4 p ord+trp+flt e e4 p stacc+fl+tr1) |# (defun filter-events-by (flat-omn-list value) (apply #'append (loop for event in (single-events flat-omn-list) when (and (length-notep (first event)) (member value (append (subseq event 0 3) (disassemble-articulations (fourth event))))) collect event else collect (list (first event))))) #| ; test (filter-events-by '(e c4 mp stacc+trp+flt e. c4 mp fl+tr1 -h e. c4 p ord+trp+flt e e4 p stacc+fl+tr1) 'trp) ; => (e c4 mp stacc+trp+flt e. -h e. c4 p ord+trp+flt e) |# (defun merge-articulations (arts &key (empty-articulations '(default -))) "Merges list of OMN articulations to a combined attribute. Args: arts: a list of OMN articulations empty-attributes: articulations to ignore in a combination. Examples: (merge-articulations '(ten ponte ubow)) => ten+ponte+ubow (merge-articulations '(- stacc)) => stacc (merge-articulations '(default default)) " (intern (reduce #'(lambda (a1 a2) (format nil "~A+~A" a1 a2)) (let ((interm-result (mappend #'(lambda (art) (unless (some #'(lambda (a) (eq art a)) empty-articulations) (list (symbol-name art)))) arts))) (if interm-result interm-result (list (symbol-name (first empty-articulations)))))))) (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 "+")))
  18. To briefly follow up: support of multiple sound libraries for a single instrument by def-score would be preferable over an extended definitions with def-sound-set, because then individual def-sound-set definitions are independent and clean (even sharable, as Stephane did for EWQLSO), and details like the MIDI channel and port remain in the def-score definition. Thanks! Best, Torsten added 2 minutes later BTW: the Xsample library requires so many key-switches, that they layered them in multiple velocity zones. Would that be something def-sound-set should support? Best, Torsten
  19. could you give me an example (input, output) of what you are trying to do. Why would you like to use separate channel for each of the articulations. What samples (virtual instrument) you are using. I would like to use multiple orchestral sound libraries that support different articulations in parallel for "preview mock-ups". For example, one library with standard articulations (e.g., EWQLSO) and one for extended techniques that I am considering to buy (e.g., IRCAM Solo Instruments, https://s3.amazonaws.com/uvi/SCP01-Ircam+Solo+Instruments/ISI_manual.pdf, or Xsample Acoustic Instruments Library, http://www.xsample.de/index_htm_files/Xsample_Library_en.pdf). So far, I used custom Kontakt instruments of extended techniques for the latter. Gilreath (2010) also recommends to combine libraries for adding variety etc. My above-mentioned function would be a workaround to realise such playback, but at the cost of additional explicit staffs in the notation output, so some built-in support for multiple libraries in def-sound-set or a support for specifying multiple libraries for a single instrument in def-score output to different MIDI channels/ports would be welcome! Best, Torsten Gilreath, P. (2010) The Guide to MIDI Orchestration. 4th revised edition. Focal Press.
  20. If optionally it would be possible in def-sound-set to specify MIDI channels and ports and/or specifying that as an articulation for individual notes that would be great. Best, Torsten
  21. 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 "+")))
  22. As you likely already know, what you can do in a single voice/part is specifying various articulations, which can result in different timbres of your playback, including user-defined specifications with the function def-sound-set. Beyond that, you could split your part into multiple parts, in the score, where each is performed by its own MIDI channel (basically reproducing how a Klangfarbenmelodie would be realised in an orchestra setting). The advantage of expressing different timbres of different instruments with multiple parts is that it allows for dove-tailing, where the last note of one instrument and the first note of the next are shared by both for a better connecting. Anyway, if you want to generate only a single part with timbre specifications, why not defining a function that allows to split it into multiple parts for the orchestra later. I proposed such a function for your convenience at Best, Torsten
  23. Great, thanks! Would you consider adding a documentation file for that function? If not, could you at least add a doc string to the code, so that it could be of use for at least more advanced users? For everyone else: when you are searching for a function that is not documented by its own RTF file and thus cannot be found via the standard Opusmodus documentation search, you could use the following function. Some internal Opusmodus functions without standard Opusmodus documentation have at least a documentation string. ;; return all functions that contain 'omn' in their name, together with their documentation string (if there is any). (apropos-function-documentation "omn") The function apropos-function-documentation is defined below. Best, Torsten (defun apropos-function-documentation (my-string &optional (package *package*)) "Lists all functions that contain `my-string' alongside their documentation in a list of pairs (<function-symbol> <doc-string>)" (mapcar #'(lambda (x) (list x (documentation x 'function))) (remove-if-not #'fboundp (apropos-list my-string package))))
×
×
  • Create New...

Important Information

Terms of Use Privacy Policy