Jump to content

Adding notes to the notes of a melody

From LilyPond wiki

If you want to add notes to the existing notes of a melody (to make chords), you generally have to copy your melody then add lots of <> to each note, which can be a time-consuming.

The \addNote function here makes it faster and simpler.

The syntax is : \addNote \melody \newNotes

Here :
melody = \relative {c'4.-> d8-. c4(\p b8) r c4\f c c2}
newnotes = {e f e d e s e}


Note that with this function, you can easily create some other useful functions. For example, a function \doubleOctave is shown here. This doubles each note with the note an octave higher.

\version "2.24.0"

%% http://lsr.di.unimi.it/LSR/Item?u=1&id=761
%% see also http://lsr.di.unimi.it/LSR/Item?u=1&id=545

%% version 2014/03/24
%% see for snippet upgrade http://gillesth.free.fr/Lilypond/chord/
%% A little doc is also provided !

#(define (noteEvent? music)
(eq? (ly:music-property music 'name) 'NoteEvent))

#(define (no-duration? music)
(not (ly:duration? (ly:music-property music 'duration))))

#(define (expand-q-chords music); for q chords : see chord-repetition-init.ly
(expand-repeat-chords! (list 'rhythmic-event) music))

%%%%%%%%%%%%%%%%%%%%%%%%%%  extractNote  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(define tagNotExtractNote (gensym))
#(use-modules (ice-9 receive)) %% for the use of receive

#(define (chord->note chord n . args)
"Return either the note n of chord chord, keeping articulations or if other
numbers are specified in args, a chord with the matching notes."
(receive (notes others)
 (partition noteEvent? (ly:music-property chord 'elements))
 (if (null? notes)
   chord
   (let* ((len (length notes))
          (res (filter-map
            (lambda(i)
              (and (integer? i)
                   (<= i len)
                   (> i 0)
                   (list-ref notes (1- i)))) ; list-ref is zero-based
            (cons n args)))
           (one-note (cond 
             ((null? res) (list-ref notes (1- len)))
             ((null? (cdr res))(car res))
             (else #f))))
      (if one-note
        (begin
          (ly:music-set-property! one-note 'articulations 
            (append (ly:music-property one-note 'articulations) others))
          one-note)
        (make-event-chord (append res others)))))))
         
#(define (extract-note music n . args)
"Extract the note n of each chords in music, keeping articulations.
If other numbers are given in args, the function returns a chord build with all
matching notes. If no note matches, returns the last note of the chord."
 (map-some-music 
   (lambda (evt)
      (cond 
        ((eq? 'EventChord (ly:music-property evt 'name))
           (let ((tags (ly:music-property evt 'tags)))
              (if (memq tagNotExtractNote tags)
                 (ly:music-set-property! evt 'tags ; only remove the tag
                     (delq tagNotExtractNote tags))
                 (set! evt (apply chord->note evt n args)))
              evt))
        (else (and (ly:music-property evt 'duration #f) evt))))
   (expand-q-chords music)))

extractNote = #(define-music-function (n music )
                                                            (number? ly:music?)
 (extract-note music n))

% usefull for notExtractNote
tagify = #(define-music-function (tag music)(symbol? ly:music?)
"Add `tag in the tags property of all chords"
(music-map
	(lambda (evt)
    (if (eq? 'EventChord (ly:music-property evt 'name))
       (ly:music-set-property! evt 'tags
             (cons tag (ly:music-property evt 'tags))))
		evt)
	music))
notExtractNote = #(define-music-function (music)(ly:music?)
"Avoids music to be extracted by \\extractNote."
#{
  \tagify #tagNotExtractNote $music
#})

%%%%%%%%%%%%%%%%%%%%%%%%%%  extractVoice  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(define tagNotExtractVoice (gensym))

#(define (extract-voice music d deeper-level?) ;; d as a decimal number
   (define (nth-voice voices n)
    (let ((len (length voices)))
     (list-ref voices (1-   ; list-ref is zero-based !
      (if (<= n len) n len)))))
   
   (define (not-extract? x)
     (let ((tags (ly:music-property x 'tags)))
        (and (memq tagNotExtractVoice tags)    ; don't extract anything
             (begin 
               (ly:music-set-property! x 'tags 
                   (delq tagNotExtractVoice tags)) ; only remove the tag
               x))))
   
   (define (myfilter x)
     (or (not-extract? x)
         (case (ly:music-property x 'name #f)
           ((SimultaneousMusic EventChord) x)
           ((OverrideProperty PropertySet VoiceSeparator) #f)
           ((ContextSpeccedMusic)
              (if (eq? (ly:music-property x 'context-type) 'Voice)
                (set! x (myfilter (ly:music-property x 'element #f))))
              x)
           (else (if (ly:music-property x 'duration #f)
             x
             (let ((e (ly:music-property x 'element #f))
                      (es (ly:music-property x 'elements #f)))
               (if e (ly:music-set-property! x 'element (myfilter e)))
               (if (pair? es)(ly:music-set-property! x 'elements
                                (filter myfilter es)))
               x))))))
(map-some-music 
 (lambda(evt)
   (case (ly:music-property evt 'name)
     ((EventChord) evt)
     ((SimultaneousMusic)
        (or (not-extract? evt)
            (let* ((save-d d)                      ; if d = 4.321, we'll get :
                   (n (truncate d))                ; n = 4 (the integer part)
                   (next-d (- (* 10  d)(* 10 n)))  ; next-d = 43.21 - 40 = 3.21
                   (voices (filter myfilter (ly:music-property evt 'elements))))     
              (set! evt (if (or (null? voices)(< n 1))
                 '()
                 (nth-voice voices (inexact->exact n))))
              (if deeper-level? (begin                                   
                (set! d (if (< next-d 1) n next-d))        ; keep n if (truncate next-d) = 0
                (set! evt (extract-voice evt d deeper-level?)))) ; SimultaneousMusic inside?
              (set! d save-d)
              evt)))
     (else (and (ly:music-property evt 'duration #f)
                evt))))
 music))

extractVoice = #(define-music-function (n music )
                                                            (integer? ly:music?)
"Extract in music, the n-th voice of simultaneous music of the same level, keeping 
only basic music events (no more \\Voicexxx or \\new Voice). A Voice separator
doesn't count as a voice."
(extract-voice music n #f))

deepExtractVoice = #(define-music-function (x music )
                                                            (number? ly:music?)
"Behaves like extractVoice, taking first the integer part of x as n argument, but
goes deeper in each simultaneous music, extracting voice of other potential
simultaneous music, taking now as n argument the first digit of the decimal part
of x, then continues always deeper with second digit and so on.
Notes that a digit of 0, means taking previous value of n, so 2 is equivalent to 2,222...
and 2,3 to 2,333..."
(extract-voice music x #t))

notExtractVoice = #(define-music-function (music)(ly:music?)
"Inside an \\extractVoice section, avoids that a part of this section (called
here `music) to be extracted."
#{ \tag #tagNotExtractVoice $music #})

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% derivated functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% If you have enter << <c e g> \\ <ais cis fis> >>, the first function will
%% give you c, the second fis
extractPartUpper = #(define-music-function (music )(ly:music?)
 #{ \extractNote #1000 \extractVoice #1 $music #})

extractPartLower = #(define-music-function (music )(ly:music?)
 #{ \extractNote #1 \extractVoice #1000 $music #})
                          %%%%%% shortcuts %%%%%%%
#(define ePU extractPartUpper)
#(define ePL extractPartLower)


%%%%%%%%%%%%%%%%%%%%% addNote

#(define (add-note music notes-to-add)                ; music and notes-to-add as music
  (define (note->chords-arti note)                    ; note as a NoteEvent
    (receive (note-arti chord-arti)
      (partition      ; separates arti for NoteEvent from arti for EventChord
        (lambda (evt)(memq (ly:music-property evt 'name)
                       (list 'StringNumberEvent 'StrokeFingerEvent 'FingeringEvent)))
        (ly:music-property note 'articulations))
      (ly:music-set-property! note 'articulations note-arti)
      chord-arti))
  (let* ((alist      ; a list of pairs of 2 lists : '(notes . articulations)
          (reverse (let loop ((m (expand-q-chords notes-to-add)) ; q to chords
                              (p '())) ; m = music, p previous value of the list
            (case (ly:music-property m 'name)
              ((or SkipEvent SkipMusic) ; a skip in notes-to-add means : add nothing
                 (cons #f p))           ; add #f to the list
              ((NoteEvent) 
                 (acons (list m) (note->chords-arti m) p))
              ((EventChord)
                 (receive (notes arti) ; separates notes from scripts, dynamics etc
                   (partition noteEvent? (ly:music-property m 'elements))
                   (if (pair? notes)(acons notes arti p) p)))
              (else (let ((e (ly:music-property m 'element)))
                 (fold loop
                       (if (ly:music? e)(loop e p) p)
                       (ly:music-property m 'elements))))))))
        (entry #f)  ; will be (car alist)
        (entry? (lambda() (and
                  (pair? alist)
                  (begin (set! entry (car alist))
                         (set! alist (cdr alist))
                         entry))))
        (do-add (lambda (notes arti)
                  (let* ((dur (ly:music-property (car notes) 'duration))
                         (new-notes (map            ; fix all durations to dur
                           (lambda(evt)(ly:music-set-property! evt 'duration dur)
                                       evt)
                           (car entry)))            ; the list of new notes
                         (new-arti (cdr entry)))    ; the articulations
                     (append new-notes notes new-arti arti)))))
    ;; combine in chords, each element of alist with notes of music  
   (map-some-music
     (lambda(x)
       (case (ly:music-property x 'name)
           ((NoteEvent)(if (entry?)
              (make-event-chord (do-add (list x) (note->chords-arti x)))
              x))
           ((EventChord)
              (if (entry?)(receive (notes arti) ; separates notes from scripts, dynamics etc
                (partition noteEvent? (ly:music-property x 'elements))
                (if (pair? notes)(ly:music-set-property! x 'elements (do-add notes arti)))))
              x)
           (else (and (ly:music-property x 'duration #f) x)))) ; #f means : go deeper
     (expand-q-chords music))))


addNote = #(define-music-function (music notes)
                                                          (ly:music? ly:music?)
"Merges in a chord, the first note or chord in `music, with the first note or chord
in `notes, including articulations, then continues to the second one, and so on.
The duration of notes are taken from `music.
In `notes, only note or chord events are kept."
(add-note #{\relative c' $music  #}   ; the 2 music-parameters will
          #{\relative c' $notes  #})) % be seen in \relative mode
          

%%%%%%%%%%%%%%%%%%%% addVoice
%% Traditionnal way
addVoice = #(define-music-function (music newVoice)
                                                          (ly:music? ly:music?)
#{ << $music \\ $newVoice >> #})


%% Alternate way
addVoiceAlt = #(define-music-function (music newVoice)
                                                          (ly:music? ly:music?)
#{ << { \voiceOne $music } \new Voice { \voiceTwo $newVoice } >>
   \oneVoice #})
                                                          
%%%%%%%%%%%%%%%%%%%%
deleteDynamics = #(define-music-function (music) (ly:music?)
(music-filter (lambda (evt)
                (not (memq (ly:music-property evt 'name) (list 
                           'AbsoluteDynamicEvent 'CrescendoEvent 'DecrescendoEvent))))
   	      music))

%%%%%%%%%%%%%%%%%%%%%%%%
absolute = #(define-music-function (music) (ly:music?)
"A \\relative command will have no effect in the resulting music."
(make-music 'UnrelativableMusic 'element music))

doubleNote = #(define-music-function (music) (ly:music?)
"Double each note with the note an octave higher."
 #{ \addNote \transpose c c' \relative c' { $music } $music #})


%%%%%%%%%%%%%%%%%%%%%%%%
%% Well \shiftDurations do now the same things ...
%% depracated functions

doubleDur = #(define-music-function (music)(ly:music?)
(map-some-music
 (lambda (evt)
   (let ((dur (ly:music-property evt 'duration #f)))
     (and dur (begin
          (ly:music-set-property! evt 'duration (ly:make-duration
            (1- (ly:duration-log dur))
            (ly:duration-dot-count dur)))
          evt))))
 music))

halfDur = #(define-music-function (music)(ly:music?)
(map-some-music
 (lambda (evt)
   (let ((dur (ly:music-property evt 'duration #f)))
     (and dur (begin
         (ly:music-set-property! evt 'duration (ly:make-duration
            (1+ (ly:duration-log dur))
            (ly:duration-dot-count dur)))
         evt))))
 music))
	%%%%%%%%%%%%%%%%%%%%%%%%% TESTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%

melody = {c'4.-> d8-. c4(\p b8) r c4\f c c2}
newNotes = { e f e d e s e }
\score { <<
  \new Staff \relative c' \melody
  \new Staff \addNote \melody \newNotes
  >>
}