Autor Thema: D.S. al Fine - für MIDI-Ausgabe mittels Steuermarken  (Gelesen 2093 mal)

Arnold

  • Member
D.S. al Fine - für MIDI-Ausgabe mittels Steuermarken
« am: Montag, 2. September 2013, 10:53 »
Hallo,

meine vorläfuige Lösung für das Extrahieren eines "D.S. al Fine" scheint ja schon anwendbar zu sein, aber vor allem die Visualisierung der dazu zu setzenden Steuermarken gefällt mir noch nicht.
Zuerst einmal das Beispiel:
\version "2.16.0"
%\version "2.14.2" - \inStaffSegno is missing, use in this case \bar "S|:" instead.
%\version "2.17.15"
%%%%%Start   From LSR "senza ripetizioni"
#(define-public (volta-repeats-only-once music)
  "This function replaces all 'percent volta' with 'sequentil music', discaring all but the last alternative."
  (let*
   ((es (ly:music-property music 'elements))
    (e  (ly:music-property music 'element)))
   (if (memq 'repeated-music (ly:music-property music 'types))
    (let*
     ((props (ly:music-mutable-properties music))
      (old-name (ly:music-property music 'name)))
     (if (equal? old-name 'VoltaRepeatedMusic)
      (let*
       ((newprops (alist-copy props))
        (newprops (assoc-remove! newprops 'repeat-count))
        (last-of-elements (last-pair es))
        (newprops (assoc-set! newprops 'elements '()))
        (newprops (assoc-set! newprops 'element '()))
        (flattened  (flatten-alist newprops))
       )
       (set! es (cons e (if (pair? last-of-elements) last-of-elements '())))
       (set! e '())
       (set! music (apply make-music (cons 'SequentialMusic flattened)))))))
   (if (pair? es)
    (set! (ly:music-property music 'elements) (map volta-repeats-only-once es)))
   (if (ly:music? e)
    (set! (ly:music-property music 'element) (volta-repeats-only-once e)))
   music))

senzaRipet =
#(define-music-function (parser location music) (ly:music?)
 (volta-repeats-only-once music))
%%%%%End   From LSR "senza ripetizioni"



controlPoint =
#(define-music-function (parser location symb) (symbol?)
  (_i "Create a not printable mark, used for manipulation of sequential music.")
  (make-music 'Music 'markerName      symb
                     'functionalUse   'control-point
                     'uniqueRulesetID "ArnoldTheresius/extract-from-sequential-music.ly"))

#(define (get-control-point-symbol mus)
  (if (and (ly:music? mus)
           (eq?    (ly:music-property mus 'name)
                   'Music)
           (equal? (ly:music-property mus 'uniqueRulesetID)
                   "ArnoldTheresius/extract-from-sequential-music.ly")
           (eq?    (ly:music-property mus 'functionalUse)
                   'control-point))
   (ly:music-property mus 'markerName)
   '()))

#(define (check-controlpoint-availability start-sym select-start end-sym select-end elems)
  (if (or select-start select-end)
   (let ((current-state (if select-start 0 1))
         (count #f))
    (for-each (lambda (x)
      (case current-state
       ((0) (if select-start
         (let ((s (get-control-point-symbol x)))
          (if (and (not (null? s)) (eq? s start-sym))
           (set! current-state 1)))))
       ((1) (if select-end
         (let ((s (get-control-point-symbol x)))
          (if (and (not (null? s)) (eq? s end-sym))
           (set! current-state 2)
           (set! count #t)))
         (set! count #t))))) elems)
    (if select-end
     (if (and (equal? current-state 2) count) #t #f) ; is it 'terminated'? and not empty?
     (if (and (equal? current-state 1) count) #t #f) ; is it 'started'? and not empty?
   )) #f ; from #'START to #'END - that's irrelevant
 ))

#(define (extract-in-sequential-mus start-sym end-sym mus)
  (if (and (ly:music? mus)
           (eq? (ly:music-property mus 'name) 'SequentialMusic))
   (let ((es (ly:music-property mus 'elements))
         (select-start (not (eq? start-sym 'START)))
         (select-end (not (eq? end-sym 'END))))
    (if (check-controlpoint-availability start-sym select-start end-sym select-end es)
     (let ((new-es '())
           (current-state (if select-start 0 1)))
      (for-each (lambda (x)
        (case current-state
         ((0) (if select-start
           (let ((s (get-control-point-symbol x)))
            (if (and (not (null? s)) (eq? s start-sym))
             (set! current-state 1)))))
         ((1) (if select-end
           (let ((s (get-control-point-symbol x)))
            (if (and (not (null? s)) (eq? s end-sym))
             (set! current-state 2)
             (set! new-es (append! new-es (cons x '())))))
           (set! new-es (append! new-es (cons x '())))))))
       es)
      (ly:music-set-property! mus 'elements new-es)))))
  mus)

extractFromSequentialMusic =
#(define-music-function (parser location start-sym end-sym mus) (symbol? symbol? ly:music?)
  (music-map (lambda (x) (extract-in-sequential-mus start-sym end-sym x)) (ly:music-deep-copy mus)))

%% !! Warning: This function shall not be used if musicMap will call it in parallel processing
%% !!          in multiple threads or processes. The 'counter' is not synchronized.
#(define (visualize-cps-in-mus counter mus)
  (if (and (ly:music? mus)
           (eq? (ly:music-property mus 'name) 'SequentialMusic))
   (let* ((es (ly:music-property mus 'elements))
          (pos (1+ (cdr counter)))
          (contains-cp #f)
          (new-es (map (lambda (x)
            (let ((cp-sym (get-control-point-symbol x)))
             (if (null? cp-sym) x
              (let ((cp-sym-string (string-append
                  (symbol->string cp-sym) "/" (ly:number->string pos))))
               (set! contains-cp #t)
               (make-music 'SkipEvent
                'articulations (list (make-music 'TextScriptEvent
                   'tweaks (acons 'outside-staff-priority 2001 '())
                   'direction 1
                   'text (markup #:line (#:with-color (list 1.0 0.0 0.0) (#:combine
                      (#:draw-line (cons 0 -3)) (#:box cp-sym-string))))))
                'duration (ly:make-duration 2 0 0)))))) es)))
    (set-car! counter (1+ (car counter)))
    (if contains-cp
     (let ((start-string (string-append "START/" (ly:number->string pos)))
           (end-string (string-append "END/" (ly:number->string pos))))
      (ly:music-set-property! mus 'elements (append
        (cons (make-music 'SkipEvent
          'articulations (list (make-music 'TextScriptEvent
             'tweaks (acons 'outside-staff-priority 2002 '())
             'direction 1
             'text (markup #:scale '(0.5 . 0.5) (#:line (#:with-color (list 0.7 0.3 0.2) (#:combine
                 (#:draw-line (cons 0 -2.5)) (#:box start-string)))))))
          'duration (ly:make-duration 2 0 0)) '())
        new-es
        (cons (make-music 'SkipEvent
          'articulations (list (make-music 'TextScriptEvent
             'tweaks (acons 'outside-staff-priority 2002 '())
             'direction 1
             'text (markup #:scale '(0.5 . 0.5) (#:line (#:with-color (list 0.7 0.3 0.2) (#:combine
                 (#:draw-line (cons 0 -2.5)) (#:box end-string)))))))
          'duration (ly:make-duration 2 0 0)) '())))
      (set-cdr! counter pos))))) mus)

visualizeControlPoints =
#(define-music-function (parser location mus) (ly:music?)
  (let ((counter (cons 0 0)))
   (music-map (lambda (x) (visualize-cps-in-mus counter x)) (ly:music-deep-copy mus))))


Music = {
  %% outer Sequential Music Object starts here
  \override Score.RehearsalMark #'self-alignment-X = #RIGHT
  \override Score.RehearsalMark #'break-visibility = #begin-of-line-invisible
  \partial 4 c'4
  \controlPoint #'M1   %% I put a Control Point named M1 here
                       %% into the outer Sequential Music Object
  \inStaffSegno %%% before version 2.16.0: use '\bar "S|:"' instead!
  \repeat volta 2 {
    %% here start the first Sequential Music Object inside
    %% the Repeat Volta Structure
    e'1 g'
    %% here ends the first Sequential Music Object inside
    %% the Repeat Volta Structure
  }
  \alternative {
    {
      %% here start the second Sequential Music Object inside
      %% the Repeat Volta Structure
      d'2. b4
      %% here ends the second Sequential Music Object inside
      %% the Repeat Volta Structure
    }
    {
      %% here start the third Sequential Music Object inside
      %% the Repeat Volta Structure
      c'2 r4 \mark "Fine" \controlPoint #'M3 g'8 g'
      %% here ends the third Sequential Music Object inside
      %% the Repeat Volta Structure
      %% It contains the Contol Point named M3
    }
  }
  \controlPoint #'M2   %% I put a Control Point named M3 here
                       %% into the outer Sequential Music Object
  f'1 b
  a2 r4 e'\mark "D.S. al Fine"
  \bar "|."
  %% here ends the outer Sequential Music Object
  %% It contains the Control Points name M1 and M2
}

\markup \fontsize #-2 \justify {
  This example of a Music is enriched with »control points«.
  They are placed as MusicEvent in the Sequential Music Elements.
  And no engraver evaluates them, they will be invisible.
}
%{ Dieses Beispiel wurde mit ControlPoints (Steuerungsmarken) angereichert.
   Sie wurden als Musik-Event in the Sequentiellen Musik Elemente hinzugefügt.
   Da kein Engraver diese Musik-Events beachtet, sind sie unsichtbar.
%}

\score {
  \new Staff { \Music }
  \header {
    piece = "{ \Music } = "
  }
}

\markup \fontsize #-2 \justify {
  \bold "\\visualizeControlPoints" converts these control points into a text markup
  (which looks like a flag) and enumberates the »SequentialMusic« elements in which
  the contol points were found.
}
%{ \visualizeControlPoints konvertiert diese ControlPoints in Textnotizen im Stil von
   Fähnchen, und zählt dabei die Sequentiellen Musik Elemente durch, in welchen mindestens
   ein ControlPoint gefunden wurde (die Zahl nach dem Schrägstrich).
%}

\score {
  \new Staff { \visualizeControlPoints \Music }
  \header {
    piece = "{ \visualizeControlPoints \Music } = "
  }
}

\markup \fontsize #-2 \justify {
  \bold "\\extractFromSequentialMusic" will extract the music events between the given
  control points from any sequential music element found in the music structure.
  If the control points do not match then the original sequential music element is returned.
  As the sequential music elements often are nested in many levels, the control points
  must be positioned carefully.
}
%{ \extractFromSequentialMusic filtert den angegebenen Bereich heraus, aber nur
   diejenigen Sequentiellen Musik Elemente sind betroffen, in denen auch die angegebenen
   Marken (ControlPoints) vorhanden sind.
   Da die Sequentiellen Musik Elemente meist mit vielen Unterebenen verschachtelt vorliegen
   muß man schon die ControlPoints wohlüberlegt setzen.
%}

\score {
  \new Staff {
    s4*0^\markup \fontsize #-4 "(primary use: for midi output)"
    \Music
    \extractFromSequentialMusic #'M1 #'M2
    \extractFromSequentialMusic #'START #'M3
    \senzaRipet \Music
    \bar "|."
  }
  \header {
    piece = \markup { \column {
      "{"
      "  \Music"
      "  \extractFromSequentialMusic #'M1 #'M2"
      "    \extractFromSequentialMusic #'START #'M3"
      "      \senzaRipet %%% (see also: LSR \"senza ripetizioni\") is mostly also used here"
      "        \Music"
      "  \bar \"|.\""
      "} = "
    }}
  }
}
Ich setze ein "benutzerdefiniertes Music-Event" als "Steuermarke" in die Musikbeschreibung. Diese kann ich dann zum Filtern (nur innerhalb der elemens-Liste eines Sequential-Music-Events) heranziehen, oder "für die Kontrolle" in andere Music-Events umwandeln.

Nun ja. Die Marken müssen in jeder Stimme definiert sein, um einen mehrstimmigen Satz korrekt zu bearbeiten. Eine nutzerfreundlichere Alternative wäre also ein "Performer", welcher ähnlich dem \partcombine eine ganze Multi-Staff-Anordnung filtern könnte, auch wenn nur in einer einzigen Stimme die Marken definiert sind, und der nicht auf einzelne SequentialMusic-Elemente beschränkt ist. Das zu programmieren sehe ich mich im Augenblick nicht in der Lage.

Die Visualisierung der Marken mit "RehearsalMark" statt "TextScript" auf einem 's4*0' wäre mir lieber (wohl sicherer um die Haltebögen und Vorschläge nicht zu beeinflussen), würde aber mit anderen (vorhandenen) RehearsalMarks in  Konflikt geraten.
Und für einen RehearsalMarkCloneEngraver für meine Steuermarken, der die Fähnchen-Grobs unabhängig von den normalen RehearsalMarks hinzufügt, reichten meine Kenntnisse (noch) nicht aus - ich bin nicht weitergekommen, wie die neuen Grobs zu erzeugen und ins Grob-Netzwerk einzuhängen sind.

Ich habe auch noch keine Idee, wie der Umfang der durchnummerierten SequentialMusic-Elemente vernünftig darzustellen wäre.

Schon mal vielen Dank im voraus für
- Anregungen, Lösungswege
- Hinweise, welche Konstellationen noch zu Problemen führen.

Arnold