Okay, I'm almost finished with the autochange stuff (staff, not clef).
I like the current implementation, but the tweakable parameter called
"margin" on line 24 of autochange_revised.scm is for the moment stuck
there. So, for right now, if the user wants to change that parameter,
it must be done from within the scheme file, which is obviously not
preferable. I intend to define a new context-property called
"autochangeMargin" so that the user can do this sort of thing:
\new PianoStaff \autochange \relative {
\set autochangeMargin = #2
c8 d e f g f e d
c b a g f g a b
c d e f g f e d
}
However, I can't figure out how to do that. I understand that I need
to:
1) add an entry to define-context-properties.scm
2) maybe initialize a value somewhere in engraver-init.ly (?)
But I'm stuck because I don't know how to access the context from
within the "make-autochange-music" function in autochange_revised.scm.
The arguments accepted by that function right now are "parser" and
"music", and I toyed around with the idea of changing the definition
of autochange in music-functions-init.ly from this:
autochange =
#(define-music-function (parser location music) (ly:music?)
(_i "Make voices that switch between staves automatically")
(make-autochange-music parser music))
to this:
autochange =
#(define-music-function (parser location music) (ly:music?)
(_i "Make voices that switch between staves automatically")
(make-autochange-music parser location music))
ie. passing the location argument to the function, with the vague idea
that somehow the location argument would get me closer to the context.
Can someone please tell me how to get the context in this situation,
so that I can do this:
(define margin (ly:context-property context 'autochangeMargin))
I imagine that it's entirely doable in Scheme, but perhaps I need C++.
Man, I sure hope not, because I'm so close.
Anyone know how to do this?
Thanks.
- Mark
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; autochange - fairly related to part combining.
; copied from lily-library.scm
(define (sign x)
(if (= x 0)
0
(if (< x 0) -1 1)))
(define (notes-get-pitches notes)
(map (lambda (x) (ly:event-property x 'pitch))
notes))
(define (get-avg-pitch-steps pitches)
(round (apply average (map ly:pitch-steps pitches))))
(define (get-pitch-steps-range pitches)
(let ((pitch-steps (map ly:pitch-steps pitches)))
(cons (apply min pitch-steps) (apply max pitch-steps))))
(define-public (make-autochange-music parser music)
;; when the absolute-value of a chord's average staff-position
;; exceeds this value, allow the chord to change staves.
(define margin 2)
(define (generate-split-list change-moment event-list last-profile acc)
;; acc is a reversed list of (moment . staff) pairs,
;; where staff is 1 or -1.
;; last-profile is (last-staff . last-extremity)
;; extremity is highest staff-position for lower-staff chords
;; and lowest staff-position for upper-staff chords.
(if (null? event-list)
acc
(let* ((now-tun (caar event-list))
(evs (map car (cdar event-list)))
(now (car now-tun)) ; a moment
(notes (filter (lambda (x)
(equal? (ly:event-property x 'class)
'note-event))
evs))
(pitches (notes-get-pitches notes))
(this-avg (if (pair? notes)
(get-avg-pitch-steps pitches)
#f))
(this-range (if (pair? notes)
(get-pitch-steps-range pitches)
'(0 . 0)))
(last-staff (car last-profile))
(last-extremity (cdr last-profile))
(is-single-note (= (car this-range) (cdr this-range)))
(not-usually-here (not (= last-staff (sign this-avg))))
(exceeds-margin (< margin (abs this-avg)))
(exceeds-extremity (< (abs last-extremity)
(abs this-avg)))
(this-staff (cond
((= 0 this-avg) last-staff)
((or (and is-single-note
not-usually-here
exceeds-margin)
(and (not is-single-note)
not-usually-here
(or exceeds-margin
exceeds-extremity)))
(sign this-avg))
(else last-staff))) ; -1 or 1
(this-extremity (if (positive? this-staff)
(car this-range)
(cdr this-range)))
(this-profile (cons this-staff this-extremity)))
;; tail recursive.
(if (and this-avg (not (= last-staff this-staff)))
(generate-split-list #f
(cdr event-list)
this-profile
(cons (cons (if change-moment
change-moment
now)
(sign this-avg))
acc))
(generate-split-list (if this-avg #f now)
(cdr event-list)
this-profile
acc)))))
(let* ((m (make-music 'AutoChangeMusic))
(m1 (make-non-relative-music
(context-spec-music music 'Voice "one")))
(context-list (recording-group-emulate
music
(ly:parser-lookup parser
'partCombineListener)))
(evs (car context-list))
(rev (reverse! (cdar context-list)))
(split (reverse! (generate-split-list
#f
rev
'(1 . 0) ; first staff must default to 1.
'())
'())))
(set! (ly:music-property m 'element) music)
(set! (ly:music-property m 'split-list) split)
m))
\version "2.13.3"
% comment out the following line to see current behavior:
#(load "autochange_revised.scm")
uppersFirstShort = \relative {
<b g>8 <c g> <c a> <d a>
<d b> <e b> <e c> <f c>
<f c> <e c> <e b> <d b>
<d a> <c a> <c g> <b g>
}
lowersFirstShort = \relative {
<g b>8 <g c> <a c> <a d>
<b d> <b e> <c e> <c f>
<c f> <c e> <b e> <b d>
<a d> <a c> <g c> <g b>
}
uppersFirstLong = \relative {
<b g>8 <c g> <c a> <d a>
<d b> <e b> <e c> <f c>
<f d> <g d> <g e> <a e>
<a e> <g e> <g d> <f d>
<f c> <e c> <e b> <d b>
<d a> <c a> <c g> <b g>
}
lowersFirstLong = \relative {
<g b>8 <g c> <a c> <a d>
<b d> <b e> <c e> <c f>
<d f> <d g> <e g> <e a>
<e a> <e g> <d g> <d f>
<c f> <c e> <b e> <b d>
<a d> <a c> <g c> <g b>
}
\new PianoStaff \autochange \uppersFirstShort
\new PianoStaff \autochange \lowersFirstShort
\new PianoStaff \autochange \uppersFirstLong
\new PianoStaff \autochange \lowersFirstLong
\new PianoStaff \autochange \relative {
<b, e'>8 <d' f> <b, e'> <d' f>
<a d'> <g b> <a d'> <g b>
}
\new PianoStaff \autochange \relative {
c8 d e f g f e d
c b a g f g a b
c d e f g f e d
}
\new PianoStaff \autochange \relative {
<c, e>8 <d f> <e g> <f a>
<g b> <a c> <b d> <c e>
<d f> <e g> <f a> <g b>
<g b> <f a> <e g> <d f>
<c e> <b d> <a c> <g b>
<f a> <e g> <d f> <c e>
}
_______________________________________________
lilypond-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/lilypond-devel