[With forward to -user, since this may be interesting now for everybody.]
Nicolas Sceaux wrote:
Le 29 nov. 2009 à 16:15, Alexander Kobel a écrit :
I propose using a different separator for \parallelMusic than "|", and allow to
include bar checks in there.
\parallelMusic as is is very fine and handy, but sometimes you want to enter a
whole phrase of two or three measures in a single go, and include the bar
checks in there for readability and checking later on.
It somewhat disturbs the workflow if you always have to look up where the
voiceN ended up in the last measure, especially if you have larger intervals in
the voices, or reentering the durations if one voice stays in 16, the next in 8
and the third in 2.
What about, e.g., ":"? It's fast to type, and AFAICS, it only is used for chord
mode and figured bass. And I don't see a use for those two in \parallelMusic, since -
well, you usually only have one chord at a time, right?
Another idea: "||", to serve both as bar check and as delimiter. And there's
definitely no use conflict against two bar checks at the same point in time, right?
Currently, \parallelMusic highjacks the usual meaning of the pipe symbol: [...]
This was done so that \parallelMusic has no impact on the parser.
Thanks, Nicolas,
this was exactly the hint I needed... It never occured to me that
parallelMusic might be a simple music function; I figured it's low-level
and pretty involved in parser.
"I can see clearly now, the rain is gone..."
Which allows for a quite simple modification, to make the delimiter be
"||", combining a bar check and a voice cycle: Just count how often the
existing voice change has been called, and only do some work if it's two
times in a row.
The modification is attached, and seems to work flawlessly.
Of course, this still leaves the question whether the delimiter should
be changed, and whether it's good to overload the bar check.
But it seems to me that the convert-ly rule should be so simple that
even I might figure out how to write it, and I'm perfectly satiesfied
with not having to remember the position of yet another symbol on the US
keyboard layout... ;-)
Cheers,
Alexander
parallelMusic =
#(define-music-function (parser location voice-ids music) (list? ly:music?)
(_i "Define parallel music sequences, separated by '||' (double bar check
signs),
and assign them to the identifiers provided in @var{voice-ids}.
@var{voice-ids}: a list of music identifiers (symbols containing only letters)
@var{music}: a music sequence, containing double BarChecks as limiting
expressions.
Example:
@verbatim
\\parallelMusic #'(A B C) {
c c | d d | e e ||
d d | e e | f f ||
e e | f f | g g ||
}
<==>
A = { c c | d d | e e | }
B = { d d | e e | f f | }
C = { e e | f f | g g | }
@end verbatim
")
(let* ((voices (apply circular-list (make-list (length voice-ids) (list))))
(current-voices voices)
(current-sequence (list))
(change-voice-call-count 0))
;;
;; utilities
(define (push-music m)
"Push the music expression into the current sequence"
(set! current-sequence (cons m current-sequence)))
(define (change-voice)
"Stores the previously built sequence into the current voice and
change to the following voice."
;; only do the actual work if change-voice is called the second time in a
row
(if (odd? change-voice-call-count)
(begin
(list-set! current-voices 0 (cons (make-music 'SequentialMusic
'elements (reverse! current-sequence))
(car current-voices)))
(set! current-sequence (list))
(set! current-voices (cdr current-voices))))
;; increase the call counter
(set! change-voice-call-count (- 1 change-voice-call-count)))
(define (bar-check? m)
"Checks whether m is a bar check."
(eq? (ly:music-property m 'name) 'BarCheck))
(define (music-origin music)
"Recursively search an origin location stored in music."
(cond ((null? music) #f)
((not (null? (ly:music-property music 'origin)))
(ly:music-property music 'origin))
(else (or (music-origin (ly:music-property music 'element))
(let ((origins (remove not (map music-origin
(ly:music-property music
'elements)))))
(and (not (null? origins)) (car origins)))))))
;;
;; first, split the music and fill in voices
(map-in-order (lambda (m)
(push-music m)
(if (bar-check? m)
(change-voice)
(set! change-voice-call-count 0))) ;; reset the call counter
(ly:music-property music 'elements))
(if (not (null? current-sequence)) (begin (set! change-voice-call-count 1)
(change-voice)))
;; un-circularize `voices' and reorder the voices
(set! voices (map-in-order (lambda (dummy seqs)
(reverse! seqs))
voice-ids voices))
;;
;; set origin location of each sequence in each voice
;; for better type error tracking
(for-each (lambda (voice)
(for-each (lambda (seq)
(set! (ly:music-property seq 'origin)
(or (music-origin seq) location)))
voice))
voices)
;;
;; check sequence length
(apply for-each (lambda* (#:rest seqs)
(let ((moment-reference (ly:music-length (car seqs))))
(for-each (lambda (seq moment)
(if (not (equal? moment moment-reference))
(ly:music-message seq
"Sections in parallel music don't have the
same length")))
seqs (map-in-order ly:music-length seqs))))
voices)
;;
;; bind voice identifiers to the voices
(map (lambda (voice-id voice)
(ly:parser-define! parser voice-id
(make-music 'SequentialMusic
'origin location
'elements voice)))
voice-ids voices))
;; Return an empty sequence. this function is actually a "void" function.
(make-music 'SequentialMusic 'void #t))
\version "2.12.2"
\include "parallelMusic.ily"
\parallelMusic #'(A B C) {
c4 c c c | c c c c | c c c c ||
a2 a | a a | a a ||
f1 | f | f ||
}
\score {
<<
\new Staff \relative c'' \A
\new Staff \relative c'' \B
\new Staff \relative c' \C
>>
}
_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/lilypond-devel