AM Posted May 22, 2017 Posted May 22, 2017 extendig SINGLE-EVENTS with optional-datas (like instrument-name, or whatelse) and reading/filtering this EVENT-LIST by a key-value -> useful for instrumentation... greetings andré ;;; --------------------------------------------------------------------------------------------- ;;; extending single-events with optional-datas ;;; --------------------------------------------------------------------------------------------- ;; SUBFUNCTION (defun memberp (n liste) (not (equal 'nil (member n liste)))) ;; MAINFUNCTION (defun create-extended-single-events (omn-list &key (optional-data1 nil) (optional-data2 nil) (optional-data3 nil)) (loop for i in (single-events omn-list) for data-cnt = 0 then (incf data-cnt) when (< (car (omn :length i)) 0) collect (append (list (first i)) (gen-repeat 6 'nil)) else collect (append (loop repeat 4 for cnt = 0 then (incf cnt) collect (nth cnt i)) (append (list (nth data-cnt optional-data1) (nth data-cnt optional-data2) (nth data-cnt optional-data3)))))) (create-extended-single-events '(e c4 mp stacc e. -h e. p ord e e4 stacc) :optional-data1 '(trp fl trp trp fl clar) :optional-data2 '(1 3 2 4 3 5 3 1 1)) ;; events are extended with the optional-data1-x => ((e c4 mp stacc trp 1 nil) (e. c4 mp nil fl 3 nil) (-h nil nil nil nil nil nil) (e. c4 p ord trp 4 nil) (e e4 p stacc fl 3 nil)) ;;; --------------------------------------------------------------------------------------------- ;;; reads events by key-values!! ;;; --------------------------------------------------------------------------------------------- ;;; now, with this function, you can filter all EVENTS with key-value X. all others will be replaced ;;; by RESTS, so the time-length-structure will be not destroyed. you can say: i need all EVENTS ;;; with key-value 'trp in the trumpet-voice, or all EVENTS with key-value 'c4 for .... (defun read-single-events-by (event-stream &key (key-value 'c4)) (loop for i in event-stream when (memberp key-value i) collect i else collect (append (list (length-invert (first i))) (gen-repeat 6 'nil)))) (read-single-events-by '((e c4 mp stacc trp 1 nil) (e. c4 p ord fl 3 nil) (e e4 p stacc trp 2 nil)) :key-value 'trp) ;; shows all EVENTS with key-value 'trp (other events are replaced by rests) => ((e c4 mp stacc trp 1 nil) (-3/16 nil nil nil nil nil nil) (e e4 p stacc trp 2 nil)) (read-single-events-by '((e c4 mp stacc trp 1 nil) (e. c4 p ord fl 3 nil) (e e4 p stacc trp 2 nil)) :key-value '3) ;; shows all EVENTS with key-value '3 (other events are replaced by rests) => ((-1/8 nil nil nil nil nil nil) (e. c4 p ord fl 3 nil) (-1/8 nil nil nil nil nil nil)) (read-single-events-by '((e c4 mp stacc trp 1 nil) (e. c4 p ord fl 3 nil) (e e4 p stacc trp 2 nil)) :key-value 'ord) ;; shows all EVENTS with key-value 'ord (other events are replaced by rests) => ((-1/8 nil nil nil nil nil nil) (e. c4 p ord fl 3 nil) (-1/8 nil nil nil nil nil nil)) Quote
torstenanders Posted May 22, 2017 Posted May 22, 2017 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 "+"))) opmo 1 Quote
torstenanders Posted May 22, 2017 Posted May 22, 2017 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.) |# opmo and AM 2 Quote
opmo Posted May 22, 2017 Posted May 22, 2017 You might find the function OMN-RECONSTRUCT useful in your work: (omn-reconstruct '(e c4 mp stacc -3/16 -1/2 e. c4 p ord -1/8)) => (e c4 mp stacc -e. -h e. c4 p ord -e) (omn-reconstruct '(e c4 mp stacc -3/16 -1/2 e. c4 p ord -1/8 (leg q c4 d4 e4))) => (e c4 mp stacc -e. -h e. c4 p ord -e q c4 leg d4 leg e4) (omn-reconstruct '((e c4 p stacc -3/16) (-1/2 e. c4 ord) (-1/8 (leg q c4 d4 e4)))) => ((e c4 p stacc -e.) (-h e. c4 ord) (-e q c4 leg d4 leg e4)) (omn-reconstruct '((e c4 p stacc -3/16) (-1/2 e. c4 ord) (-1/8 (leg q c4 d4 e4))) :velocityp t) => ((e c4 p stacc -e.) (-h e. c4 p ord) (-e q c4 p leg d4 leg e4)) Quote
torstenanders Posted May 22, 2017 Posted May 22, 2017 > 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 Quote
AM Posted May 22, 2017 Author Posted May 22, 2017 Quote 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 :) i see and unterstand what you mean... (as a non-programmer :-)), so i'm asking here some naive questions: 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? b) in my way i see completely transparent and easy what's up, and not a mixture of text-attributes/data...? 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 :-)) ...but... it is great to have such good inputs, that's what i'm looking for in this FORUM, thanx a lot torsten!!! herzlich andré added 8 minutes later in my view it would be nice to EXTEND the "make-omn/single-events"-structure by x-add-datas... like: (make-omn :length :pitch :velocity :articulation :data1 :data2 ... ... ...) Quote
AM Posted May 22, 2017 Author Posted May 22, 2017 data could also be midi-channel, prog-number.... Quote
torstenanders Posted May 22, 2017 Posted May 22, 2017 > 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. lviklund and AM 2 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.