]> git.donarmstrong.com Git - lilypond.git/commitdiff
Part-combine: Add a way to override the part-combination decision
authorReinhold Kainhofer <reinhold@kainhofer.com>
Wed, 28 Jul 2010 17:58:59 +0000 (19:58 +0200)
committerReinhold Kainhofer <reinhold@kainhofer.com>
Tue, 28 Sep 2010 16:16:15 +0000 (18:16 +0200)
Add functions partcombineApart[Once], partcombineChords[Once],
partcombineUnisono[Once], partcombineAutomatic[Once] etc. to tweak the
decision of the part-combiner.
Internally, they are implemented using PartCombineForceEvent

These events are not processed by an engraver as most other events,
but rather directly in Scheme by the part-combiner.

Documentation/changes.tely
input/regression/part-combine-force-once.ly [new file with mode: 0644]
input/regression/part-combine-force.ly [new file with mode: 0644]
ly/music-functions-init.ly
scm/define-event-classes.scm
scm/define-music-properties.scm
scm/define-music-types.scm
scm/part-combiner.scm

index c6e1bb47906d8cabd487d9d3f4426f5cd3207c8d..f754c5111f8f962a3d5ab576de3d4ed6acbc3fbf 100644 (file)
@@ -66,6 +66,14 @@ which scares away people.
 
 @end ignore
 
+@item
+The part-combiner's decision to combine/not combine notes can now be customized
+@lilypond[quote,relative=2]
+\partcombine
+\relative c' { c2 \partcombineApart c | \partcombineChordsOnce e' e }
+\relative c' { c2 \partcombineApart c | c c }
+@end lilypond
+
 @item
 Tablature staves show fret numbers only by default. To get the
 former style, @code{\tabFullNotation} is provided.
diff --git a/input/regression/part-combine-force-once.ly b/input/regression/part-combine-force-once.ly
new file mode 100644 (file)
index 0000000..b2b30dc
--- /dev/null
@@ -0,0 +1,30 @@
+
+\header {
+  texidoc ="Overrides for the part-combiner, affecting only one moment.
+  The @code{partcombine...Once} override applies only to one moment, after which the
+  old override -- if any -- is in effect again.
+"
+}
+
+\layout { ragged-right = ##t }
+
+\version "2.13.35"
+
+mI = \relative c' {
+       e4 e \partcombineApartOnce c c |
+       \partcombineApart c \partcombineChordsOnce e e e |
+       c \partcombineUnisonoOnce c c c |
+       \partcombineAutomatic \partcombineSoloIOnce r2 c4 c |
+       \partcombineSoloIIOnce R1 |
+}
+mII = \relative c' {
+       c4 \partcombineApartOnce c c c |
+       c c \partcombineAutomaticOnce e e |
+       c c c c |
+       R1 |
+       r2 c4 c |
+}
+
+\score {
+       \new Staff \partcombine \mI \mII
+}
diff --git a/input/regression/part-combine-force.ly b/input/regression/part-combine-force.ly
new file mode 100644 (file)
index 0000000..b66d1b7
--- /dev/null
@@ -0,0 +1,34 @@
+
+\header {
+  texidoc ="Overrides for the part-combiner. All functions like
+  @code{\partcombineApart} and @code{\partcombineApartOnce} are internally implemented
+  using a dedicated @code{PartCombineForceEvent}.
+"
+}
+
+\layout { ragged-right = ##t }
+
+\version "2.13.35"
+
+mI = \relative c' {
+       e4 e c2 |
+       \partcombineApart c^"apart" e |
+       e e |
+       \partcombineChords e'^"chord" e |
+       \partcombineAutomatic c c\> |
+       \partcombineUnisono c^"unisono" c |
+       \partcombineAutomatic c\! c^"V1 longer" |
+}
+mII = \relative c' {
+       c4 c c2 |
+       c c |
+       \partcombineAutomatic e^"auto" e |
+       a, c |
+       c c' |
+       c c |
+       c
+}
+
+\score {
+       \new Staff \partcombine \mI \mII
+}
index 0e5093e8a5b8c899ba5ad5eda25d6d9957742027..8ac1deda0113e19ca9676a17cbcf573d5e0eeb09 100644 (file)
@@ -599,6 +599,28 @@ that they share a staff.")
    (make-part-combine-music parser
                             (list part1 part2)))
 
+#(define (symbol-or-boolean? x) (or (symbol? x) (boolean? x)))
+partcombineForce =
+#(define-music-function (location parser type once) (symbol-or-boolean? boolean?)
+   (_i "Override the part-combiner.")
+   (make-music 'EventChord
+              'elements (list (make-music 'PartCombineForceEvent
+                                          'forced-type type
+                                          'once once))))
+partcombineApart = \partcombineForce #'apart ##f
+partcombineApartOnce = \partcombineForce #'apart ##t
+partcombineChords = \partcombineForce #'chords ##f
+partcombineChordsOnce = \partcombineForce #'chords ##t
+partcombineUnisono = \partcombineForce #'unisono ##f
+partcombineUnisonoOnce = \partcombineForce #'unisono ##t
+partcombineSoloI = \partcombineForce #'solo1 ##f
+partcombineSoloIOnce = \partcombineForce #'solo1 ##t
+partcombineSoloII = \partcombineForce #'solo2 ##f
+partcombineSoloIIOnce = \partcombineForce #'solo2 ##t
+partcombineAutomatic = \partcombineForce ##f ##f
+partcombineAutomaticOnce = \partcombineForce ##f ##t
+
+
 pitchedTrill =
 #(define-music-function
    (parser location main-note secondary-note)
index 4de8c5d97b37d820a586c3c9683c6a86dbfed43f..e1f3b4aa33b3122aff4d30b256c079c83bc6b8fc 100644 (file)
     (StreamEvent .
                 (RemoveContext ChangeParent Override Revert UnsetProperty
                                SetProperty music-event OldMusicEvent CreateContext Prepare
-                               OneTimeStep Finish)) 
+                               OneTimeStep Finish))
     (music-event . (annotate-output-event
                    arpeggio-event breathing-event extender-event span-event
       rhythmic-event dynamic-event break-event label-event percent-event
-      key-change-event string-number-event stroke-finger-event tie-event part-combine-event
+      key-change-event string-number-event stroke-finger-event tie-event
+      part-combine-event part-combine-force-event
       beam-forbid-event script-event
       tremolo-event bend-after-event fingering-event glissando-event
       harmonic-event hyphen-event laissez-vibrer-event mark-event
-      multi-measure-text-event note-grouping-event 
+      multi-measure-text-event note-grouping-event
       pes-or-flexa-event repeat-tie-event spacing-section-event
       layout-instruction-event completize-extender-event break-span-event))
-    
+
     (layout-instruction-event . (apply-output-event))
     (script-event . (articulation-event text-script-event))
     (part-combine-event . (solo-one-event solo-two-event unisono-event))
@@ -43,7 +44,7 @@
     (dynamic-event . (absolute-dynamic-event))
     (span-event . (span-dynamic-event beam-event episema-event ligature-event
                         pedal-event phrasing-slur-event slur-event staff-span-event
-                        text-span-event trill-span-event tremolo-span-event 
+                        text-span-event trill-span-event tremolo-span-event
                         tuplet-span-event))
     (span-dynamic-event . (decrescendo-event crescendo-event))
     (break-span-event . (break-dynamic-span-event))
@@ -66,7 +67,7 @@
  (lambda (rel)
    (for-each
     (lambda (type)
-      (hashq-set! ancestor-lookup type 
+      (hashq-set! ancestor-lookup type
                  (cons type (hashq-ref ancestor-lookup (car rel) '()))))
     (cdr rel)))
  event-classes)
 (define unlistened-music-event-classes
   '(harmonic-event line-break-event page-break-event page-turn-event label-event
                   solo-one-event solo-two-event skip-event unisono-event
-                  break-dynamic-span-event))
+                  part-combine-force-event break-dynamic-span-event))
 
 ;; produce neater representation of music event tree.
 ;; TODO: switch to this representation for the event-classes list?
 
 ;; check that the music event tree corresponds well with the set of
 ;; available translators; print warnings otherwise.
-(map-tree (lambda (sym) 
+(map-tree (lambda (sym)
            (if (and (symbol? sym)
                     (not (ly:is-listened-event-class sym))
                     (not (assq sym event-classes))
                     (not (memq sym unlistened-music-event-classes)))
-               (ly:programming-error (_ "event class ~A seems to be unused") sym)))      
+               (ly:programming-error (_ "event class ~A seems to be unused") sym)))
          music-event-tree)
 
 (map (lambda (sym)
index d7b602afe72f2b852f01a2659a1b81e5536c291d..23faf5644a7917e18b7b4a8454dba72b1b20aa40 100644 (file)
@@ -84,6 +84,7 @@ a sequential iterator.  Takes a single music parameter.")
      (figure ,integer? "A bass figure.")
      (force-accidental ,boolean? "If set, a cautionary accidental should
 always be printed on this note.")
+     (forced-type ,symbol? "Override for the part-combiner.")
 
      (grob-property ,symbol? "The symbol of the grob property to set.")
      (grob-property-path ,list? "A list of symbols, locating a nested grob
index 4b446d72b7143a0df64bd4f4c1d1fbae3551cc3c..ac2e5f9723cbd0b47ac477c4aafd08d8127ea172 100644 (file)
@@ -349,11 +349,16 @@ Syntax: @code{\\override} [ @var{context} @code{.} ]
        (types . (general-music break-event page-turn-event event))
        ))
 
+    (PartCombineForceEvent
+     . ((description . "Override the part-combiner's strategy.")
+       (types . (general-music part-combine-force-event event))
+       ))
+
     (PartialSet
      . ((description . "Create an anacrusis or upbeat (partial measure).")
        (iterator-ctor . ,ly:partial-iterator::constructor)
        (types . (general-music partial-set))
-       ))
+        ))
 
     (PartCombineMusic
      . ((description . "Combine two parts on a staff, either merged or
index ad828b85b203cb00facb99d81133501f550b3247..2a95e2083bbe5331734a30382ba4abbbbffd945e 100644 (file)
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (define-class <Split-state> ()
+  ;; The automatically determined split configuration
   (configuration #:init-value '() #:accessor configuration)
+  ;; Allow overriding split configuration, takes precedence over configuration
+  (forced-configuration #:init-value #f #:accessor forced-configuration)
   (when-moment #:accessor when #:init-keyword #:when)
   ;; voice-states are states starting with the Split-state or later
   ;;
@@ -263,6 +266,56 @@ list, similar to the Recording_group_engraver in 2.8 and earlier"
         (voice-state-vec2 (make-voice-states evl2))
         (result (make-split-state voice-state-vec1 voice-state-vec2)))
 
+    ;; Go through all moments recursively and check if the events of that
+    ;; moment contain a part-combine-force-event override. If so, store its
+    ;; value in the forced-configuration field, which will override. The
+    ;; previous configuration is used to determine non-terminated settings.
+    (define (analyse-forced-combine result-idx prev-res)
+
+      (define (get-forced-event x)
+       (if (ly:in-event-class? x 'part-combine-force-event)
+           (cons (ly:event-property x 'forced-type) (ly:event-property x 'once))
+           #f))
+      (define (part-combine-events vs)
+       (if (not vs)
+           '()
+           (filter-map get-forced-event (events vs))))
+      ;; end part-combine-events
+
+      ;; forced-result: Take the previous config and analyse whether
+      ;; any change happened.... Return new once and permanent config
+      (define (forced-result evt state)
+       ;; sanity check, evt should always be (new-state . once)
+       (if (not (and (pair? evt) (pair? state)))
+           state
+           (if (cdr evt)
+               ;; Once-event, leave permanent state unchanged
+               (cons (car evt) (cdr state))
+               ;; permanent change, leave once state unchanged
+               (cons (car state) (car evt)))))
+      ;; end forced-combine-result
+
+      ;; body of analyse-forced-combine:
+      (if (< result-idx (vector-length result))
+         (let* ((now-state (vector-ref result result-idx)) ; current result
+                ;; Extract all part-combine force events
+                (ev1 (part-combine-events (car (voice-states now-state))))
+                (ev2 (part-combine-events (cdr (voice-states now-state))))
+                (evts (append ev1 ev2))
+                ;; result is (once-state permament-state):
+                (state (fold forced-result (cons 'automatic prev-res) evts))
+                ;; Now let once override permanent changes:
+                (force-state (if (equal? (car state) 'automatic)
+                                 (cdr state)
+                                 (car state))))
+           (set! (forced-configuration (vector-ref result result-idx))
+                 force-state)
+           ;; For the next moment, ignore the once override (car stat)
+           ;; and pass on the permanent override, stored as (cdr state)
+           (analyse-forced-combine (1+ result-idx) (cdr state)))))
+    ;; end analyse-forced-combine
+
+
     (define (analyse-time-step result-idx)
       (define (put x . index)
        "Put the result to X, starting from INDEX backwards.
@@ -488,14 +541,25 @@ the mark when there are no spanners active.
          (display "***\n")
          (display result)
          (display "***\n")))
+
+    ;; Extract all forced combine strategies, i.e. events inserted by
+    ;; \partcombine(Apart|Automatic|SoloI|SoloII|Chords)[Once]
+    ;; They will in the end override the automaically determined ones.
+    ;; Initial state for both voices is no override
+    (analyse-forced-combine 0 #f)
+    ;; Now go through all time steps in a loop and find a combination strategy
+    ;; based only on the events of that one moment (i.e. neglecting longer
+    ;; periods of solo/apart, etc.)
     (analyse-time-step 0)
     ;; (display result)
+    ;; Check for unisono or unisilence moments
     (analyse-a2 0)
     ;;(display result)
     (analyse-solo12 0)
     ;; (display result)
     (set! result (map
-                 (lambda (x) (cons (when x) (configuration x)))
+                 ;; forced-configuration overrides, if it is set
+                 (lambda (x) (cons (when x) (or (forced-configuration x) (configuration x))))
                  (vector->list result)))
     (if #f ;; pc-debug
         (display result))