On Sat, Dec 05, 2020 at 06:34:52AM +0100, Petr Pařízek wrote:
>    On 5.12.2020 1:07, Knute Snortum wrote:
> 
>      > I'm confused.  Why doesn't the notation create arpeggios in MIDI?  
>
>      The \arpeggio command only prints the relevant symbols in the
>      notated score (i.e. in the PDF output) but does not have any
>      effect on the MIDI file.
>
>      The final part of the documentation on "Ties" contains a snippet
>      that explains that written-out arpeggios can be typed more
>      efficiently by applying the "tieWaitForNote" property and then
>      reseting it again:
>      
> [1]https://lilypond.org/doc/v2.20/Documentation/notation/writing-rhythms#ties
>      But I can't imagine rewriting it manually for every single
>      arpeggiated chord of my original file, considering how many they
>      are there. I'd love to find a way of automating this process but
>      I don't know how I would do that.
[...]

Some time ago I wrote a little .ly snippet to generate midi arpeggios
(see attached).  It takes a number N and a chord, and generates an
arpeggio where the start of each subsequent note of the chord (in the
order they are spelled in the input) is delayed from the previous note
by a duration equal to the length of the chord divided into N units.
For example:

        \midiArpeggio 24 <c e g c>4

divides a quarter note into 24 units, and plays the first c on-beat, the
e 1/24th of a quarter note past the beat, g 2/24th of a quarter note
past the beat, and the last c 3/24th of a quarter note past the beat. By
varying the first argument you can control how fast the chord is rolled
(useful for adapting the generated midi to the current tempo of the
music so that it doesn't sound too fast or too slow).

Unfortunately, the output will look like a horrendous mess if you
typeset it; so I usually use it in a midi-only score tagged by #'midi,
with a layout-only score tagged by #'layout that uses lilypond's
built-in \arpeggio command, e.g., something like this:

        \include "arpeggio.ly"
        myMusic = {
                ...
                \tag #'layout { <c e g c>\arpeggio }
                \tag #'midi { \midiArpeggio 24 <c e g c> }
                ...
        }

        % This score is for printing only
        \score {
                \removeWithTag #'midi \myMusic
                \layout {}
        }

        % This score is for generating midi
        \score {
                \removeWithTag #'layout \myMusic
                \midi {}
        }


T

-- 
My program has no bugs! Only unintentional features...
%
% A scheme function that unpacks a rolled chord into displaced notes with
% adjusted lengths such that it will render nicely in MIDI.
%
\version "2.20.1"

% Returns: A skip followed by the given note, with the skip scaled by n/d of
% the note's length, and the note scaled by (d-n)/d of its original length.
#(define shiftedNote (lambda (num denom note)
    (if (= num 0)
        ; First note is unchanged
        note

        ; Construct a skip + the note, both appropriately scaled
        (let ((dLog (ly:duration-log (ly:music-property note 'duration)))
              (dDot (ly:duration-dot-count (ly:music-property note 'duration)))
              (dFac (ly:duration-factor (ly:music-property note 'duration)))
              (scaledNote (ly:music-deep-copy note)))
            (set! (ly:music-property scaledNote 'duration)
                  (ly:make-duration dLog dDot (*
                    (/ (car dFac) (cdr dFac))
                    (/ (- denom num) denom))))
            (make-music 'SequentialMusic
                'elements (list
                    (make-music 'SkipEvent
                        'duration (ly:make-duration dLog dDot
                            (* (/ (car dFac) (cdr dFac)) (/ num denom))))
                    scaledNote
                )
            )
        )
    )
))

% Generate a rolled chord from the given notes. Each subsequent note is shifted
% by num/denom.
#(define genArpeggio (lambda (num denom notes)
    (if (pair? notes) ; Test for empty list
        ; Recursive case
        (if (null? (ly:music-property (car notes) 'duration))
            ; Skip objects that have no duration
            (genArpeggio (+ 1 num) denom (cdr notes))

            ; Shift notes
            (cons
                (shiftedNote num denom (car notes))
                (genArpeggio (+ 1 num) denom (cdr notes))
            )
        )

        ; Base case
        '()
    )
))

% Params:
%   chord = The chord to arpeggiate.
%   denom = The fraction of the chord's length to use as a rest for each note
%       of the arpeggiated chord. This must not be less than the number of
%       notes in the chord. If it's greater than the number of chord notes, the
%       top note will be held longer.
midiArpeggio = #(define-music-function (denom chord) (number? ly:music?)
    (let ((chordTones (ly:music-property chord 'elements)))
        (make-music 'SimultaneousMusic
            'elements (genArpeggio 0 denom chordTones)
        )
    )
)

% vim:set sw=4 ts=4 et:

Reply via email to