lilypond-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Voice switching étude


From: Dan Eble
Subject: Re: Voice switching étude
Date: Fri, 2 Jan 2015 15:07:13 -0500

On Dec 10, 2014, at 23:52 , Dan Eble <address@hidden> wrote:
> 
> Here’s a trick: routing music to different voices without using the 
> Part_combine_iterator.  Instead, it uses the same infrastructure as for staff 
> switching.

updated to generate per-part \change commands from the split list generated by 
\partcombine
— 
Dan

\version "2.19.15"

% These are the parts that will be combined.  The explicit \change
% commands mimic \partcombineApart; they override the automated
% decisions and send each part to its own voice.
one = \relative { f'4 b b \change Voice = "1" b }
two = \relative { b'4 b f \change Voice = "2" f }

% Generate a split list using \partcombine.  Context changes in the
% input parts cause warnings, so strip them for this step.
killContextChanges =
#(define-music-function
  (parser location music) (ly:music?)
  (music-filter
   (lambda (m) (not (eq? (ly:music-property m 'name) 'ContextChange)))
   music))

combined = \partcombine \killContextChanges \one \killContextChanges \two

%
% Translate the split list to a sequence of \change commands for each part.
%

#(define part-one-slot-map
  ;; each entry is (split state . context id)
  '((apart . 1)
    (apart-silence . 1)
    (chords . shared)
    (silence1 . shared)
    (silence2 . null)
    (solo1 . shared)
    (solo2 . null)
    (unisono . shared)
    (unisilence . shared)))

#(define part-two-slot-map
  ;; each entry is (split state . context id)
  '((apart . 2)
    (apart-silence . 2)
    (chords . shared)
    (silence1 . null)
    (silence2 . shared)
    (solo1 . null)
    (solo2 . shared)
    (unisono . null)
    (unisilence . null)))

voiceChanges =
#(define-music-function (parser location slot-map partcombinemusic)
  (list? ly:music?)

  (let ((m (list))
        (prevMoment ZERO-MOMENT)
        (prevSlot (assq-ref slot-map 'apart)))

   (define (handle-split split)
    (let* ((moment (car split))
           (slot (assq-ref slot-map (cdr split))))
     
     (if (not (eq? prevSlot slot))
      (let ((dur (ly:moment-sub moment prevMoment)))
       (if (not (equal? dur ZERO-MOMENT))
        (set! m (cons (make-music 'SkipEvent
                       'duration (make-duration-of-length dur)) m)))
       (set! m (cons (make-music 'ContextChange
                      'change-to-id (symbol->string slot)
                      'change-to-type 'Voice) m))
       (set! prevMoment moment)
       (set! prevSlot slot)))
   ))
   
   (for-each handle-split (ly:music-property partcombinemusic 'split-list))
   (make-sequential-music (reverse! m))))

oneChanges = \voiceChanges #part-one-slot-map \combined
twoChanges = \voiceChanges #part-two-slot-map \combined

%#(display (ly:music-property combined 'split-list))
%\displayMusic \oneChanges
%\displayMusic \twoChanges

% Wrap each part in a "Part" context which can be moved from one Voice
% context to another.
%
% A hierarchy such as Staff/VoiceSlot/Voice might make more sense in
% musical terms than Staff/Voice/Part.
\layout {
  \context {
    \name "Part"
    \type "Engraver_group"
  }
  \context {
    \Voice
    \accepts "Part"
  }
}

\score {
  \new Staff <<
    \new Voice = "1" \with { \voiceOne } <<
      \new Part << \oneChanges \one >>
    >>

    \new Voice = "2" \with { \voiceTwo } <<
      \new Part << \twoChanges \two >>
    >>

    \new Voice = "shared" \with { \override NoteHead.color = #red } <<
      % Contexts need to be kept alive in order to change to them.
      #(skip-of-length one)
      #(skip-of-length two)
    >>

    \new NullVoice = "null" <<
      #(skip-of-length one)
      #(skip-of-length two)
    >>
  >>
}




reply via email to

[Prev in Thread] Current Thread [Next in Thread]