Re: A challenging music transformation...
Hello Gordon, I’ve created a function for replacing arbitrary chords with other chords. Note that this requires the use of absolute notation, because to support relative notation I’d need to implement my own parser just to know what octave the notes are in. Cheers, Valentin\version "2.22" % returns a string representation of a pitch #(define (pitch->str p) (string-append "Po" (number->string (ly:pitch-octave p)) "n" (number->string (ly:pitch-notename p % returns a symbol representation of a list of pitches #(define (signature pitches) (let ((sp (sort pitches ly:pitchsymbol (apply string-append (map pitch->str sp) % takes a alist that maps symbol representations of chords to a list of pitches and some music % and replaces all occurences of chords with the key pitches in the alist with chords with the % pitches associated with that key replaceChords = #(define-music-function (replacements mus) (list? ly:music?) ; get a list of all notes in music (define (extract-notes music) (let ((name (ly:music-property music 'name)) (elt (ly:music-property music 'element)) (elts (ly:music-property music 'elements))) (if (eq? name 'NoteEvent) (let ((nelt (if (null? elt) elt (extract-notes elt))) (nelts (apply append (map extract-notes elts (cons music (append nelt nelts))) (let ((nelt (if (null? elt) elt (extract-notes elt))) (nelts (apply append (map extract-notes elts (append nelt nelts) ; get the duration of a chord from one of the notes in the chord (define (extract-duration notes) (if (null? notes) 0 (ly:music-property (first notes) 'duration))) ; get pitch from a note (define (extract-one-pitch note) (ly:music-property note 'pitch)) ; get list of pitches from a list of notes (define (extract-pitch notes) (map extract-one-pitch notes)) ; check if argument is a note (define (is-note? music) (eq? (ly:music-property music 'name) 'NoteEvent)) ; check if argument is not a note (define (is-not-note? music) (not (is-note? music))) ; remove all notes from 'elements (define (remove-notes music) (let ((elt (ly:music-property music 'element)) (elts (ly:music-property music 'elements))) (if (not (null? elt)) (if (is-note? elt) (ly:music-set-property! music 'element '()) (remove-notes elt))) (ly:music-set-property! music 'elements (filter is-not-note? elts)) (map remove-notes (ly:music-property music 'elements ; add specified notes to 'elements (define (add-notes music pitches dur) (define (add-note p) (if (not (null? p)) (begin (ly:music-set-property! music 'elements (cons (make-music 'NoteEvent 'pitch (car p) 'duration (ly:make-duration 2)) (ly:music-property music 'elements))) (add-note (cdr p) (add-note pitches)) ; parse music tree (define (walk-music-tree music) (let ((name (ly:music-property music 'name)) (elt (ly:music-property music 'element)) (elts (ly:music-property music 'elements))) ; is music a chord? (if (eq? name 'EventChord) (let* ((note-raw (extract-notes music)) (duration (extract-duration note-raw)) (notes (sort (extract-pitch note-raw) ly:pitchpitches mus) (define (extract-notes music) (let ((name (ly:music-property music 'name)) (elt (ly:music-property music 'element)) (elts (ly:music-property music 'elements))) (if (eq? name 'NoteEvent) (let ((nelt (if (null? elt) elt (extract-notes elt))) (nelts (apply append (map extract-notes elts (cons music (append nelt nelts))) (let ((nelt (if (null? elt) elt (extract-notes elt))) (nelts (apply append (map extract-notes elts (append nelt nelts) (define (extract-one-pitch note) (ly:music-property note 'pitch)) (define (extract-pitch notes) (map extract-one-pitch notes)) ; is music a chord? (let ((note-raw (extract-notes mus))) (sort (extract-pitch note-raw) ly:pitchpitches mus))) % takes music of the form { a1 a2 b1 b2 ... } and returns a replacement alist % for replacing a1 with a2, b1 with b2, ... replistfrommusic=#(define-scheme-function (mus) (ly:music?) (define (walk-elts elts) (if (or (null? elts) (null? (cdr elts))) '() (cons (cons (sigchord
Re: A challenging music transformation...
Gordon Bower writes: > Your problem is simpler in one essential way: you are neither adding nor > subtracting any notes, just repitching existing notes. I feel fairly > confident I could solve yours -- use map-some-music to find every > NoteEvent, and for each one, read the old pitch, look up the corresponding > new pitch, and write the new pitch. > > The writers of \addNote and \pitchChange found ways to deal with having the > replacement have more notes than the original did. "Convert a note into a > chord with all the same slurs, dots, etc etc hanging off it" still eludes > me. It seems that articulations get stored in two different places > according to whether they apply to a note or to a chord. LilyPond will typeset articulations placed into the 'articulations property of an EventChord even though that is not the "correct" way to write them. That makes it somewhat easier to arrive at working transformations. -- David Kastrup
Re: A challenging music transformation...
Your problem is simpler in one essential way: you are neither adding nor subtracting any notes, just repitching existing notes. I feel fairly confident I could solve yours -- use map-some-music to find every NoteEvent, and for each one, read the old pitch, look up the corresponding new pitch, and write the new pitch. The writers of \addNote and \pitchChange found ways to deal with having the replacement have more notes than the original did. "Convert a note into a chord with all the same slurs, dots, etc etc hanging off it" still eludes me. It seems that articulations get stored in two different places according to whether they apply to a note or to a chord. GRB On Wed, Aug 25, 2021 at 5:37 PM Craig Comstock wrote: > Maybe check this thread out. It mostly worked for me but I think your > problem if solved will help me even more! > > https://mail.gnu.org/archive/html/lilypond-user/2021-08/msg00018.html > > >
Re: A challenging music transformation...
Maybe check this thread out. It mostly worked for me but I think your problem if solved will help me even more! https://mail.gnu.org/archive/html/lilypond-user/2021-08/msg00018.html Sent from my iPhone > On Aug 25, 2021, at 5:47 PM, David Kastrup wrote: > > Gordon Bower writes: > >> In a nutshell, I would like to write a function that would let me replace a >> given note or chord with a different chord -- I might have a table of what >> gets replaced with what -- but leave all the articulations the same. >> >> Am I missing something obvious? >> >> The \addNote snippet does something similar - except that there isn't a >> convenient way to delete the original note after the replacements are added. >> >> The \changePitch snippet also does something similar. (But I am envision a >> chord-by-chord replacement of a whole line.) >> >> My own efforts at trying to make sense of how music-map or map-some-music >> work have *cough* not been going well. Would be nice not to completely >> reinvent a wheel here. >> >> If I do have to write it myself: I think I could figure out how to extract >> a list of pitches in all the NoteEvents in a chord, and push new NoteEvents >> to the chord - but I don't have much grasp as to how I replace a single >> note by a chord. >> >> (Why would I want to do this? I am trying to write a function that turns >> the written notes of the accordion's left hand into the sounding notes. >> Written notes between c, and b, get turned into parallel octaves depending >> which reed banks are sounding; written notes between d and c' get turned >> into 3- 6- or 9-note chords, depending which reed banks are sounding.) > > It's probably not going to help a whole lot, cough cough. But here is > the code I used to illustrate the 168 different bass registrations of > my accordion in a reasonably compact arrangement. Of course you'll > likely need to outcomment the image inclusion. > > > \version "2.19.22" \paper { ragged-last = ##f } #(define (number-or-pitch? a) > (or (integer? a) (ly:pitch? a))) bass-staff = "lo" bass-clef = "bass" > bass-desc = \markuplist { } bari-low-desc = \markuplist { (stem down, solid > heads) } bv-one = { \voiceOne \harmonicsOn } bv-two = { \voiceTwo > \harmonicsOn } tv-one = { \voiceOne \harmonicsOn } tv-two = \voiceTwo sv-one > = { \voiceOne \harmonicsOn } sv-two = \voiceTwo %%{ bass-staff = "hi" > bass-clef = "treble" bass-desc = \markuplist { (stem down, diamond heads) } > bari-low-desc = \markuplist { (stem up, solid heads) } bv-one = { \voiceFour > \harmonicsOn } bv-two = { \voiceTwo \harmonicsOn } tv-one = { \voiceOne > \harmonicsOn } tv-two = \voiceThree sv-one = { \voiceOne \harmonicsOn } > sv-two = \voiceTwo %} #(define stats (make-hash-table 20)) #(define > (add-to-stats pitch chordname count) (let ((p (hashv-create-handle! stats > count '())) (num (modulo (ly:pitch-semitones (ly:pitch-diff pitch #{ a #})) > 12))) (set-cdr! p (cons (cons num chordname) (cdr p) #(define > (stats-list) (define (sort-pairs lst) (map cdr (sort-list lst car<))) (map > (lambda (e) #{ \markuplist \override-lines #'(word-space . 1) \wordwrap-lines > #(cons (format #f "~d reeds:" (car e)) (fold-right (lambda (next lst) (cons > (string-append next (if (null? lst) "." ",")) lst)) '() (sort-pairs (reverse > (cdr e) #}) (sort-list (hash-table->alist stats) car<))) chordmap = > #(define-music-function (start width music) (number-or-pitch? index? > ly:music?) (if (ly:pitch? start) (set! start (ly:pitch-semitones start))) > (map-some-music (lambda (m) (and (music-is-of-type? m 'event-chord) (begin > (set! (ly:music-property m 'elements) (append-map (lambda (x) (let ((p > (ly:music-property x 'pitch))) (if (ly:pitch? p) (let loop ((n (ly:make-pitch > (- (ly:pitch-octave p) (floor (/ (- (ly:pitch-semitones p) start) 12))) > (ly:pitch-notename p) (ly:pitch-alteration p))) (l '())) (if (< > (ly:pitch-semitones n) (+ start width)) (loop (ly:pitch-transpose > (ly:make-pitch 1 0 0) n) (cons (music-clone x 'pitch n) l)) l)) (list x > (ly:music-property m 'elements))) m))) music)) #(define (conflate keys vals) > (define (folder k v res) (if (or (null? res) (not (equal? v (cdar res > (cons (cons (list k) v) res) (cons (cons (cons k (caar res)) (cdar res)) (cdr > res (fold-right folder '() keys vals)) #(define (one-set start count > music) (map (lambda (kv) (let ((keys (car kv)) (m (cdr kv))) (cons (if (null? > (cdr keys)) (format "~d" (car keys)) (format "~d–~d" (car keys) (last keys))) > m))) (conflate (iota count 1) (map (lambda (i) #{ \chordmap #i #12 $music #}) > (iota count (ly:pitch-semitones start)) #(define (bass-format-set pitch > name) (let ((bass #{ \chordmap e,, 12 < $pitch >4 #}) (accf #{ \chordmap a, > 41 < $pitch >4 #}) (accp (one-set #{ a, #} 20 #{ < $pitch >4 #}))) > (add-to-stats pitch (string-upcase name) (+ 2 (* 2 (length (ly:music-property > accf ' elements) #{ << \context Staff = "hi" << \new Voice
A challenging music transformation...
In a nutshell, I would like to write a function that would let me replace a given note or chord with a different chord -- I might have a table of what gets replaced with what -- but leave all the articulations the same. Am I missing something obvious? The \addNote snippet does something similar - except that there isn't a convenient way to delete the original note after the replacements are added. The \changePitch snippet also does something similar. (But I am envision a chord-by-chord replacement of a whole line.) My own efforts at trying to make sense of how music-map or map-some-music work have *cough* not been going well. Would be nice not to completely reinvent a wheel here. If I do have to write it myself: I think I could figure out how to extract a list of pitches in all the NoteEvents in a chord, and push new NoteEvents to the chord - but I don't have much grasp as to how I replace a single note by a chord. (Why would I want to do this? I am trying to write a function that turns the written notes of the accordion's left hand into the sounding notes. Written notes between c, and b, get turned into parallel octaves depending which reed banks are sounding; written notes between d and c' get turned into 3- 6- or 9-note chords, depending which reed banks are sounding.) GRB