;;;; This file is part of LilyPond, the GNU music typesetter.
;;;;
-;;;; Copyright (C) 1998--2011 Jan Nieuwenhuizen <janneke@gnu.org>
+;;;; Copyright (C) 1998--2012 Jan Nieuwenhuizen <janneke@gnu.org>
;;;; Han-Wen Nienhuys <hanwen@xs4all.nl>
;;;;
;;;; LilyPond is free software: you can redistribute it and/or modify
;;;; You should have received a copy of the GNU General Public License
;;;; along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
-;; (use-modules (ice-9 optargs))
+; for define-safe-public when byte-compiling using Guile V2
+(use-modules (scm safe-utility-defs))
+
+(use-modules (ice-9 optargs))
;;; ly:music-property with setter
;;; (ly:music-property my-music 'elements)
@var{music}."
(let ((es (ly:music-property music 'elements))
(e (ly:music-property music 'element)))
- (set! (ly:music-property music 'elements)
- (map (lambda (y) (music-map function y)) es))
+ (if (pair? es)
+ (set! (ly:music-property music 'elements)
+ (map (lambda (y) (music-map function y)) es)))
(if (ly:music? e)
(set! (ly:music-property music 'element)
(music-map function e)))
(inner-music-filter pred? e)
e))
(filtered-es (filter ly:music? (map (lambda (y) (inner-music-filter pred? y)) es))))
- (set! (ly:music-property music 'element) filtered-e)
- (set! (ly:music-property music 'elements) filtered-es)
- (set! (ly:music-property music 'articulations) filtered-as)
+ (if (not (null? e))
+ (set! (ly:music-property music 'element) filtered-e))
+ (if (not (null? es))
+ (set! (ly:music-property music 'elements) filtered-es))
+ (if (not (null? as))
+ (set! (ly:music-property music 'articulations) filtered-as))
;; if filtering emptied the expression, we remove it completely.
(if (or (not (pred? music))
(and (eq? filtered-es '()) (not (ly:music? e))
music
(make-music 'Music))) ;must return music.
-(define-public (display-music music)
+(define*-public (display-music music #:optional (port (current-output-port)))
"Display music, not done with @code{music-map} for clarity of
presentation."
-
- (display music)
- (display ": { ")
+ (display music port)
+ (display ": { " port)
(let ((es (ly:music-property music 'elements))
(e (ly:music-property music 'element)))
- (display (ly:music-mutable-properties music))
+ (display (ly:music-mutable-properties music) port)
(if (pair? es)
- (begin (display "\nElements: {\n")
- (map display-music es)
- (display "}\n")))
+ (begin (display "\nElements: {\n" port)
+ (for-each (lambda (m) (display-music m port)) es)
+ (display "}\n" port)))
(if (ly:music? e)
(begin
- (display "\nChild:")
- (display-music e))))
- (display " }\n")
+ (display "\nChild:" port)
+ (display-music e port))))
+ (display " }\n" port)
music)
;;;
(ly:duration? obj)
`(ly:make-duration ,(ly:duration-log obj)
,(ly:duration-dot-count obj)
- ,(car (ly:duration-factor obj))
- ,(cdr (ly:duration-factor obj))))
+ ,(ly:duration-scale obj)))
(;; note pitch
(ly:pitch? obj)
`(ly:make-pitch ,(ly:pitch-octave obj)
(use-modules (ice-9 pretty-print))
(define*-public (display-scheme-music obj #:optional (port (current-output-port)))
"Displays `obj', typically a music expression, in a friendly fashion,
-which often can be read back in order to generate an equivalent expression.
-
-Returns `obj'.
-"
+which often can be read back in order to generate an equivalent expression."
(pretty-print (music->make-music obj) port)
- (newline)
- obj)
+ (newline port))
;;;
;;; Scheme music expression --> Lily-syntax-using string translator
(use-modules (srfi srfi-39)
(scm display-lily))
-(define*-public (display-lily-music expr parser #:key force-duration)
+(define*-public (display-lily-music expr parser #:optional (port (current-output-port))
+ #:key force-duration)
"Display the music expression using LilyPond syntax"
(memoize-clef-names supported-clefs)
(parameterize ((*indent* 0)
(*previous-duration* (ly:make-duration 2))
(*force-duration* force-duration))
- (display (music->lily-string expr parser))
- (newline)))
+ (display (music->lily-string expr parser) port)
+ (newline port)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-public (shift-one-duration-log music shift dot)
"Add @var{shift} to @code{duration-log} of @code{'duration} in
-@var{music} and optionally @var{dot} to any note encountered. This
-scales the music up by a factor `2^@var{shift} * (2 - (1/2)^@var{dot})'."
+@var{music} and optionally @var{dot} to any note encountered.
+The number of dots in the shifted music may not be less than zero."
(let ((d (ly:music-property music 'duration)))
(if (ly:duration? d)
- (let* ((cp (ly:duration-factor d))
- (nd (ly:make-duration (+ shift (ly:duration-log d))
- (+ dot (ly:duration-dot-count d))
- (car cp)
- (cdr cp))))
+ (let* ((cp (ly:duration-scale d))
+ (nd (ly:make-duration
+ (+ shift (ly:duration-log d))
+ (max 0 (+ dot (ly:duration-dot-count d)))
+ cp)))
(set! (ly:music-property music 'duration) nd)))
music))
(define (first-note-duration music)
"Finds the duration of the first NoteEvent by searching depth-first
through MUSIC."
- (if (memq 'note-event (ly:music-property music 'types))
+ ;; NoteEvent or a non-expanded chord-repetition
+ ;; We just take anything that actually sports an announced duration.
+ (if (ly:duration? (ly:music-property music 'duration))
(ly:music-property music 'duration)
(let loop ((elts (if (ly:music? (ly:music-property music 'element))
(list (ly:music-property music 'element))
(set! (ly:music-property r 'repeat-count) (max times 1))
(set! (ly:music-property r 'elements) talts)
(if (and (equal? name "tremolo")
- (or (pair? (ly:music-property main 'elements))
- (ly:music? (ly:music-property main 'element))))
+ (pair? (extract-named-music main '(EventChord NoteEvent))))
;; This works for single-note and multi-note tremolos!
(let* ((children (if (music-is-of-type? main 'sequential-music)
;; \repeat tremolo n { ... }
- (length (extract-named-music main 'EventChord))
+ (length (extract-named-music main '(EventChord
+ NoteEvent)))
;; \repeat tremolo n c4
1))
;; # of dots is equal to the 1 in bitwise representation (minus 1)!
1))
(tremolo-type (ash 1 duration-log)))
(set! (ly:music-property r 'tremolo-type) tremolo-type)
- (if (not (integer? mult))
- (ly:warning (_ "invalid tremolo repeat count: ~a") times))
+ (if (not (and (integer? mult) (= (logcount mult) 1)))
+ (ly:music-warning
+ main
+ (ly:format (_ "invalid tremolo repeat count: ~a") times)))
;; Adjust the time of the notes
(ly:music-compress r (ly:make-moment 1 children))
;; Adjust the displayed note durations
calculate the number of slashes based on the durations. Returns @code{0}
if durations in @var{music} vary, allowing slash beats and double-percent
beats to be distinguished."
- (let* ((durs (map (lambda (elt)
- (duration-of-note elt))
- (extract-named-music music 'EventChord)))
+ (let* ((durs (map duration-of-note
+ (extract-named-music music '(EventChord NoteEvent
+ RestEvent SkipEvent))))
(first-dur (car durs)))
(if (every (lambda (d) (equal? d first-dur)) durs)
(let ((es (ly:music-property music 'elements))
(e (ly:music-property music 'element)))
- (if (memq 'repeated-music (ly:music-property music 'types))
+ (if (music-is-of-type? music 'repeated-music)
(let* ((props (ly:music-mutable-properties music))
(old-name (ly:music-property music 'name))
(flattened (flatten-alist props)))
(set! music (apply make-music (cons 'UnfoldedRepeatedMusic
flattened)))
- (if (equal? old-name 'TremoloRepeatedMusic)
- (let* ((seq-arg? (memq 'sequential-music
- (ly:music-property e 'types)))
- (count (ly:music-property music 'repeat-count))
- (dot-shift (if (= 0 (remainder count 3))
- -1 0))
- (child-count (if seq-arg?
- (length (ly:music-property e 'elements))
- 0)))
-
- (if (= 0 -1)
- (set! count (* 2 (quotient count 3))))
-
- (shift-duration-log music (+ (if (= 2 child-count)
- 1 0)
- (ly:intlog2 count)) dot-shift)
-
- (if seq-arg?
- (ly:music-compress e (ly:make-moment child-count 1)))))))
+ (if (and (equal? old-name 'TremoloRepeatedMusic)
+ (pair? (extract-named-music e '(EventChord NoteEvent))))
+ ;; This works for single-note and multi-note tremolos!
+ (let* ((children (if (music-is-of-type? e 'sequential-music)
+ ;; \repeat tremolo n { ... }
+ (length (extract-named-music e '(EventChord
+ NoteEvent)))
+ ;; \repeat tremolo n c4
+ 1))
+ (times (ly:music-property music 'repeat-count))
+
+ ;; # of dots is equal to the 1 in bitwise representation (minus 1)!
+ (dots (1- (logcount (* times children))))
+ ;; The remaining missing multiplicator to scale the notes by
+ ;; times * children
+ (mult (/ (* times children (ash 1 dots)) (1- (ash 2 dots))))
+ (shift (- (ly:intlog2 (floor mult)))))
+
+ ;; Adjust the time of the notes
+ (ly:music-compress music (ly:make-moment children 1))
+ ;; Adjust the displayed note durations
+ (shift-duration-log music (- shift) (- dots))))))
(if (pair? es)
(set! (ly:music-property music 'elements)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; property setting music objs.
+(define-safe-public (check-grob-path path #:optional parser location
+ #:key
+ (start 0)
+ default
+ (min 1)
+ max)
+ "Check a grob path specification @var{path}, a symbol list (or a
+single symbol), for validity and possibly complete it. Returns the
+completed specification, or @code{#f} if invalid. If optional
+@var{parser} is given, a syntax error is raised in that case,
+optionally using @var{location}. If an optional keyword argument
+@code{#:start @var{start}} is given, the parsing starts at the given
+index in the sequence @samp{Context.Grob.property.sub-property...},
+with the default of @samp{0} implying the full path.
+
+If there is no valid first element of @var{path} fitting at the given
+path location, an optionally given @code{#:default @var{default}} is
+used as the respective element instead without checking it for
+validity at this position.
+
+The resulting path after possibly prepending @var{default} can be
+constrained in length by optional arguments @code{#:min @var{min}} and
+@code{#:max @var{max}}, defaulting to @samp{1} and unlimited,
+respectively."
+ (let ((path (if (symbol? path) (list path) path)))
+ ;; A Guile 1.x bug specific to optargs precludes moving the
+ ;; defines out of the let
+ (define (unspecial? s)
+ (not (or (object-property s 'is-grob?)
+ (object-property s 'backend-type?))))
+ (define (grob? s)
+ (object-property s 'is-grob?))
+ (define (property? s)
+ (object-property s 'backend-type?))
+ (define (check c p) (c p))
+
+ (let* ((checkers
+ (and (< start 3)
+ (drop (list unspecial? grob? property?) start)))
+ (res
+ (cond
+ ((null? path)
+ ;; tricky. Should we make use of the default when the
+ ;; list is empty? In most cases, this question should be
+ ;; academical as an empty list can only be generated by
+ ;; Scheme and is likely an error. We consider this a case
+ ;; of "no valid first element, and default given".
+ ;; Usually, invalid use cases should be caught later using
+ ;; the #:min argument, and if the user explicitly does not
+ ;; catch this, we just follow through.
+ (if default (list default) '()))
+ ((not checkers)
+ ;; no checkers, so we have a valid first element and just
+ ;; take the path as-is.
+ path)
+ (default
+ (if ((car checkers) (car path))
+ (and (every check (cdr checkers) (cdr path))
+ path)
+ (and (every check (cdr checkers) path)
+ (cons default path))))
+ (else
+ (and (every check checkers path)
+ path)))))
+ (if (and res
+ (if max (<= min (length res) max)
+ (<= min (length res))))
+ res
+ (begin
+ (if parser
+ (ly:parser-error parser
+ (format #f (_ "bad grob property path ~a")
+ path)
+ location))
+ #f)))))
+
(define-public (make-grob-property-set grob gprop val)
"Make a @code{Music} expression that sets @var{gprop} to @var{val} in
@var{grob}. Does a pop first, i.e., this is not an override."
Fingering
LaissezVibrerTie
LigatureBracket
+ MultiMeasureRest
PhrasingSlur
RepeatTie
Rest
(make-sequential-music
(append
(map (lambda (x) (make-grob-property-set x 'direction
- (if (odd? n) -1 1)))
+ (if (odd? n) -1 1)))
direction-polyphonic-grobs)
(list
(make-property-set 'graceSettings
(Voice Fingering font-size -8)
(Voice StringNumber font-size -8)))
- (make-grob-property-set 'NoteColumn 'horizontal-shift (quotient n 2))
- (make-grob-property-set 'MultiMeasureRest 'staff-position (if (odd? n) -4 4))))))
+ (make-grob-property-set 'NoteColumn 'horizontal-shift (quotient n 2))))))
+
+
+(define-safe-public (make-voice-props-override n)
+ (make-sequential-music
+ (append
+ (map (lambda (x) (make-grob-property-override x 'direction
+ (if (odd? n) -1 1)))
+ direction-polyphonic-grobs)
+ (list
+ (make-property-set 'graceSettings
+ ;; TODO: take this from voicedGraceSettings or similar.
+ '((Voice Stem font-size -3)
+ (Voice Flag font-size -3)
+ (Voice NoteHead font-size -3)
+ (Voice TabNoteHead font-size -4)
+ (Voice Dots font-size -3)
+ (Voice Stem length-fraction 0.8)
+ (Voice Stem no-stem-extend #t)
+ (Voice Beam beam-thickness 0.384)
+ (Voice Beam length-fraction 0.8)
+ (Voice Accidental font-size -4)
+ (Voice AccidentalCautionary font-size -4)
+ (Voice Script font-size -3)
+ (Voice Fingering font-size -8)
+ (Voice StringNumber font-size -8)))
+
+ (make-grob-property-override 'NoteColumn 'horizontal-shift (quotient n 2))
+ (make-grob-property-override 'MultiMeasureRest 'staff-position (if (odd? n) -4 4))))))
(define-safe-public (make-voice-props-revert)
(make-sequential-music
(make-music 'PropertyUnset
'symbol sym))
-;;; Need to keep this definition for \time calls from parser
-(define-public (make-time-signature-set num den)
- "Set properties for time signature @var{num}/@var{den}."
- (make-music 'TimeSignatureMusic
- 'numerator num
- 'denominator den
- 'beat-structure '()))
-
-;;; Used for calls that include beat-grouping setting
-(define-public (set-time-signature num den . rest)
- "Set properties for time signature @var{num}/@var{den}.
-If @var{rest} is present, it is used to set @code{beatStructure}."
- (ly:export
- (make-music 'TimeSignatureMusic
- 'numerator num
- 'denominator den
- 'beat-structure (if (null? rest) rest (car rest)))))
-
(define-safe-public (make-articulation name)
(make-music 'ArticulationEvent
'articulation-type name))
(let ((ts (ly:music-property m 'types)))
(memq 'separator ts)))
+;;; expanding repeat chords
+(define-public (copy-repeat-chord original-chord repeat-chord duration
+ event-types)
+ "Copies all events in @var{event-types} (be sure to include
+@code{rhythmic-events}) from @var{original-chord} over to
+@var{repeat-chord} with their articulations filtered as well. Any
+duration is replaced with the specified @var{duration}."
+ ;; First remove everything from event-types that can already be
+ ;; found in the repeated chord. We don't need to look for
+ ;; articulations on individual events since they can't actually get
+ ;; into a repeat chord given its input syntax.
+
+ (define (keep-element? m)
+ (any (lambda (t) (music-is-of-type? m t))
+ event-types))
+ (define origin (ly:music-property repeat-chord 'origin #f))
+ (define (set-origin! l)
+ (if origin
+ (for-each (lambda (m) (set! (ly:music-property m 'origin) origin)) l))
+ l)
+
+ (for-each
+ (lambda (field)
+ (for-each (lambda (e)
+ (for-each (lambda (x)
+ (set! event-types (delq x event-types)))
+ (ly:music-property e 'types)))
+ (ly:music-property repeat-chord field)))
+ '(elements articulations))
+
+ ;; now treat the elements
+ (set! (ly:music-property repeat-chord 'elements)
+ (let ((elts
+ (set-origin! (ly:music-deep-copy
+ (filter keep-element?
+ (ly:music-property original-chord
+ 'elements))))))
+ (for-each
+ (lambda (m)
+ (let ((arts (ly:music-property m 'articulations)))
+ (if (pair? arts)
+ (set! (ly:music-property m 'articulations)
+ (set-origin! (filter! keep-element? arts))))
+ (if (ly:duration? (ly:music-property m 'duration))
+ (set! (ly:music-property m 'duration) duration))))
+ elts)
+ (append! elts (ly:music-property repeat-chord 'elements))))
+ (let ((arts (filter keep-element?
+ (ly:music-property original-chord
+ 'articulations))))
+ (if (pair? arts)
+ (set! (ly:music-property repeat-chord 'articulations)
+ (append!
+ (set-origin! (ly:music-deep-copy arts))
+ (ly:music-property repeat-chord 'articulations))))))
+
+
+(define-public (expand-repeat-chords! event-types music)
+ "Walks through @var{music} and fills repeated chords (notable by
+having a duration in @code{duration}) with the notes from their
+respective predecessor chord."
+ (let loop ((music music) (last-chord #f))
+ (if (music-is-of-type? music 'event-chord)
+ (let ((chord-repeat (ly:music-property music 'duration)))
+ (cond
+ ((not (ly:duration? chord-repeat))
+ (if (any (lambda (m) (ly:duration?
+ (ly:music-property m 'duration)))
+ (ly:music-property music 'elements))
+ music
+ last-chord))
+ (last-chord
+ (set! (ly:music-property music 'duration) '())
+ (copy-repeat-chord last-chord music chord-repeat event-types)
+ music)
+ (else
+ (ly:music-warning music (_ "Bad chord repetition"))
+ #f)))
+ (let ((elt (ly:music-property music 'element)))
+ (fold loop (if (ly:music? elt) (loop elt last-chord) last-chord)
+ (ly:music-property music 'elements)))))
+ music)
+
;;; splitting chords into voices.
(define (voicify-list lst number)
"Make a list of Musics.
(if (ly:music? e)
(set! (ly:music-property m 'element) (voicify-music e)))
(if (and (equal? (ly:music-property m 'name) 'SimultaneousMusic)
- (reduce (lambda (x y ) (or x y)) #f (map music-separator? es)))
+ (any music-separator? es))
(set! m (context-spec-music (voicify-chord m) 'Staff)))
m))
(define-public (empty-music)
- (ly:export (make-music 'Music)))
+ (make-music 'Music))
;; Make a function that checks score element for being of a specific type.
(define-public (make-type-checker symbol)
(define-public (music-has-type music type)
(memq type (ly:music-property music 'types)))
-(define-public (music-clone music)
- (define (alist->args alist acc)
- (if (null? alist)
- acc
- (alist->args (cdr alist)
- (cons (caar alist) (cons (cdar alist) acc)))))
-
- (apply
- make-music
- (ly:music-property music 'name)
- (alist->args (ly:music-mutable-properties music) '())))
+(define-public (music-clone music . music-properties)
+ "Clone @var{music} and set properties according to
+@var{music-properties}, a list of alternating property symbols and
+values:
+@example\n(music-clone start-span 'span-direction STOP)
+@end example
+Only properties that are not overriden by @var{music-properties} are
+actually fully cloned."
+ (let ((old-props (list-copy (ly:music-mutable-properties music)))
+ (new-props '())
+ (m (ly:make-music (ly:prob-immutable-properties music))))
+ (define (set-props mus-props)
+ (if (and (not (null? mus-props))
+ (not (null? (cdr mus-props))))
+ (begin
+ (set! old-props (assq-remove! old-props (car mus-props)))
+ (set! new-props
+ (assq-set! new-props
+ (car mus-props) (cadr mus-props)))
+ (set-props (cddr mus-props)))))
+ (set-props music-properties)
+ (for-each
+ (lambda (pair)
+ (set! (ly:music-property m (car pair))
+ (ly:music-deep-copy (cdr pair))))
+ old-props)
+ (for-each
+ (lambda (pair)
+ (set! (ly:music-property m (car pair)) (cdr pair)))
+ new-props)
+ m))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; warn for bare chords at start.
(new-settings (append current
(list (list context-name grob sym val)))))
(ly:context-set-property! where 'graceSettings new-settings)))
- (ly:export (context-spec-music (make-apply-context set-prop) 'Voice)))
+ (context-spec-music (make-apply-context set-prop) 'Voice))
(define-public (remove-grace-property context-name grob sym)
"Remove all @var{sym} for @var{grob} in @var{context-name}."
(set! new-settings (delete x new-settings)))
prop-settings)
(ly:context-set-property! where 'graceSettings new-settings)))
- (ly:export (context-spec-music (make-apply-context delete-prop) 'Voice)))
+ (context-spec-music (make-apply-context delete-prop) 'Voice))
(defmacro-public define-syntax-function (type args signature . body)
"Helper macro for `ly:make-music-function'.
Syntax:
- (define-syntax-function (result-type? parser location arg1 arg2 ...) (result-type? arg1-type arg2-type ...)
+ (define-syntax-function result-type? (parser location arg1 arg2 ...) (arg1-type arg2-type ...)
...function body...)
argX-type can take one of the forms @code{predicate?} for mandatory
`(define-syntax-function scheme? ,@rest))
+(defmacro-public define-void-function rest
+ "This defines a Scheme function like @code{define-scheme-function} with
+void return value (i.e., what most Guile functions with `unspecified'
+value return). Use this when defining functions for executing actions
+rather than returning values, to keep Lilypond from trying to interpret
+the return value."
+ `(define-syntax-function (void? *unspecified*) ,@rest *unspecified*))
+
(defmacro-public define-event-function rest
"Defining macro returning event functions.
Syntax:
(if (vector? (ly:music-property quote-music 'quoted-events))
(let* ((dir (ly:music-property quote-music 'quoted-voice-direction))
- (clef (ly:music-property quote-music 'quoted-music-clef))
- (main-voice (if (eq? 1 dir) 1 0))
- (cue-voice (if (eq? 1 dir) 0 1))
+ (clef (ly:music-property quote-music 'quoted-music-clef #f))
+ (main-voice (case dir ((1) 1) ((-1) 0) (else #f)))
+ (cue-voice (and main-voice (- 1 main-voice)))
(main-music (ly:music-property quote-music 'element))
(return-value quote-music))
- (if (or (eq? 1 dir) (eq? -1 dir))
-
- ;; if we have stem dirs, change both quoted and main music
- ;; to have opposite stems.
- (begin
- (set! return-value
- ;; cannot context-spec Quote-music, since context
- ;; for the quotes is determined in the iterator.
- (make-sequential-music
- (list
- (if (null? clef)
- (make-music 'Music)
- (make-cue-clef-set clef))
- (context-spec-music (make-voice-props-set cue-voice) 'CueVoice "cue")
- quote-music
- (context-spec-music (make-voice-props-revert) 'CueVoice "cue")
- (if (null? clef)
- (make-music 'Music)
- (make-cue-clef-unset)))))
- (set! main-music
- (make-sequential-music
- (list
- (make-voice-props-set main-voice)
- main-music
- (make-voice-props-revert))))
- (set! (ly:music-property quote-music 'element) main-music)))
-
- return-value)
+ (if main-voice
+ (set! (ly:music-property quote-music 'element)
+ (make-sequential-music
+ (list
+ (make-voice-props-override main-voice)
+ main-music
+ (make-voice-props-revert)))))
+
+ ;; if we have stem dirs, change both quoted and main music
+ ;; to have opposite stems.
+
+ ;; cannot context-spec Quote-music, since context
+ ;; for the quotes is determined in the iterator.
+
+ (make-sequential-music
+ (delq! #f
+ (list
+ (and clef (make-cue-clef-set clef))
+
+ ;; Need to establish CueVoice context even in #CENTER case
+ (context-spec-music
+ (if cue-voice
+ (make-voice-props-override cue-voice)
+ (make-music 'Music))
+ 'CueVoice "cue")
+ quote-music
+ (and cue-voice
+ (context-spec-music
+ (make-voice-props-revert) 'CueVoice "cue"))
+ (and clef (make-cue-clef-unset))))))
quote-music))
(define-public ((quote-substitute quote-tab) music)
(define-public toplevel-music-functions
(list
+ (lambda (music parser) (expand-repeat-chords!
+ (cons 'rhythmic-event
+ (ly:parser-lookup parser '$chord-repeat-events))
+ music))
(lambda (music parser) (voicify-music music))
(lambda (x parser) (music-map music-check-error x))
(lambda (x parser) (music-map precompute-music-length x))
(check-pitch-against-signature context pitch barnum laziness octaveness))
(define (key-entry-notename entry)
- "Return the pitch of an entry in localKeySignature. The entry is either of the form
- '(notename . alter) or '((octave . notename) . (alter barnum . measurepos))."
- (if (number? (car entry))
- (car entry)
- (cdar entry)))
+ "Return the pitch of an @var{entry} in @code{localKeySignature}.
+The @samp{car} of the entry is either of the form @code{notename} or
+of the form @code{(octave . notename)}. The latter form is used for special
+key signatures or to indicate an explicit accidental.
+
+The @samp{cdr} of the entry is either a rational @code{alter} indicating
+a key signature alteration, or of the form
+@code{(alter . (barnum . measurepos))} indicating an alteration caused by
+an accidental in music."
+ (if (pair? (car entry))
+ (cdar entry)
+ (car entry)))
(define (key-entry-octave entry)
- "Return the octave of an entry in localKeySignature (or #f if the entry does not have
- an octave)."
+ "Return the octave of an entry in @code{localKeySignature}
+or @code{#f} if the entry does not have an octave.
+See @code{key-entry-notename} for details."
(and (pair? (car entry)) (caar entry)))
(define (key-entry-bar-number entry)
- "Return the bar number of an entry in localKeySignature (or #f if the entry does not
- have a bar number)."
- (and (pair? (car entry)) (caddr entry)))
+ "Return the bar number of an entry in @code{localKeySignature}
+or @code {#f} if the entry does not have a bar number.
+See @code{key-entry-notename} for details."
+ (and (pair? (cdr entry)) (caddr entry)))
(define (key-entry-measure-position entry)
- "Return the measure position of an entry in localKeySignature (or #f if the entry does
- not have a measure position)."
- (and (pair? (car entry)) (cdddr entry)))
+ "Return the measure position of an entry in @code{localKeySignature}
+or @code {#f} if the entry does not have a measure position.
+See @code{key-entry-notename} for details."
+ (and (pair? (cdr entry)) (cdddr entry)))
(define (key-entry-alteration entry)
"Return the alteration of an entry in localKeySignature.
For convenience, returns @code{0} if entry is @code{#f}."
(if entry
- (if (number? (car entry))
+ (if (number? (cdr entry))
(cdr entry)
(cadr entry))
0))
(let* ((entry (car keysig))
(entryoct (key-entry-octave entry))
(entrynn (key-entry-notename entry))
- (oct (ly:pitch-octave pitch))
(nn (ly:pitch-notename pitch)))
(if (and (equal? nn entrynn)
- (or (and accept-global (not entryoct))
- (and accept-local (equal? oct entryoct))))
+ (or (not entryoct)
+ (= entryoct (ly:pitch-octave pitch)))
+ (if (key-entry-bar-number entry)
+ accept-local
+ accept-global))
entry
(find-pitch-entry (cdr keysig) pitch accept-global accept-local)))))
(entry (find-pitch-entry keysig pitch #t #t)))
(if (not entry)
(cons #f #f)
- (let* ((global-entry (find-pitch-entry keysig pitch #f #f))
- (key-acc (key-entry-alteration global-entry))
- (acc (ly:pitch-alteration pitch))
- (entrymp (key-entry-measure-position entry))
+ (let* ((entrymp (key-entry-measure-position entry))
(entrybn (key-entry-bar-number entry)))
- (cons #f (not (or (equal? acc key-acc)
- (and (equal? entrybn barnum) (equal? entrymp measurepos)))))))))
+ (cons #f (not (and (equal? entrybn barnum) (equal? entrymp measurepos))))))))
(define-public (set-accidentals-properties extra-natural
auto-accs auto-cauts
(car rest) 'Staff))
(pcontext (if (pair? rest)
(car rest) 'GrandStaff)))
- (ly:export
- (cond
+ (cond
;; accidentals as they were common in the 18th century.
((equal? style 'default)
(set-accidentals-properties #t
`(Staff ,(make-accidental-rule 'same-octave 0))
'()
context))
- ;; accidentals from one voice do NOT get cancelled in other voices
+ ;; accidentals from one voice do NOT get canceled in other voices
((equal? style 'voice)
(set-accidentals-properties #t
`(Voice ,(make-accidental-rule 'same-octave 0))
'()
context))
;; accidentals as suggested by Kurt Stone, Music Notation in the 20th century.
- ;; This includes all the default accidentals, but accidentals also needs cancelling
+ ;; This includes all the default accidentals, but accidentals also needs canceling
;; in other octaves and in the next measure.
((equal? style 'modern)
(set-accidentals-properties #f
context))
;; Multivoice accidentals to be read both by musicians playing one voice
;; and musicians playing all voices.
- ;; Accidentals are typeset for each voice, but they ARE cancelled across voices.
+ ;; Accidentals are typeset for each voice, but they ARE canceled across voices.
((equal? style 'modern-voice)
(set-accidentals-properties #f
`(Voice ,(make-accidental-rule 'same-octave 0)
,(make-accidental-rule 'same-octave 1))
context))
;; stone's suggestions for accidentals on grand staff.
- ;; Accidentals are cancelled across the staves in the same grand staff as well
+ ;; Accidentals are canceled across the staves in the same grand staff as well
((equal? style 'piano)
(set-accidentals-properties #f
`(Staff ,(make-accidental-rule 'same-octave 0)
context))
(else
(ly:warning (_ "unknown accidental style: ~S") style)
- (make-sequential-music '()))))))
+ (make-sequential-music '())))))
(define-public (invalidate-alterations context)
"Invalidate alterations in @var{context}.
(set! (ly:context-property context 'localKeySignature)
(map-in-order
(lambda (entry)
- (let* ((localalt (key-entry-alteration entry))
- (localoct (key-entry-octave entry)))
+ (let* ((localalt (key-entry-alteration entry)))
(if (or (accidental-invalid? localalt)
- (not localoct)
+ (not (key-entry-bar-number entry))
(= localalt
(key-entry-alteration
(find-pitch-entry
keysig
- (ly:make-pitch localoct
+ (ly:make-pitch (key-entry-octave entry)
(key-entry-notename entry)
0)
#t #t))))
entry
(cons (car entry) (cons 'clef (cddr entry))))))
(ly:context-property context 'localKeySignature)))))
-
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-public (skip-of-length mus)
(ly:music-property (car evs) 'pitch))))
(define-public (duration-of-note event-chord)
- (let ((evs (filter (lambda (x)
- (music-has-type x 'rhythmic-event))
- (ly:music-property event-chord 'elements))))
-
- (and (pair? evs)
- (ly:music-property (car evs) 'duration))))
+ (cond
+ ((pair? event-chord)
+ (or (duration-of-note (car event-chord))
+ (duration-of-note (cdr event-chord))))
+ ((ly:music? event-chord)
+ (let ((dur (ly:music-property event-chord 'duration)))
+ (if (ly:duration? dur)
+ dur
+ (duration-of-note (ly:music-property event-chord 'elements)))))
+ (else #f)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(define-public (map-some-music map? music)
+ "Walk through @var{music}, transform all elements calling @var{map?}
+and only recurse if this returns @code{#f}."
+ (let loop ((music music))
+ (or (map? music)
+ (let ((elt (ly:music-property music 'element))
+ (elts (ly:music-property music 'elements))
+ (arts (ly:music-property music 'articulations)))
+ (if (ly:music? elt)
+ (set! (ly:music-property music 'element)
+ (loop elt)))
+ (if (pair? elts)
+ (set! (ly:music-property music 'elements)
+ (map loop elts)))
+ (if (pair? arts)
+ (set! (ly:music-property music 'articulations)
+ (map loop arts)))
+ music))))
+
+(define-public (for-some-music stop? music)
+ "Walk through @var{music}, process all elements calling @var{stop?}
+and only recurse if this returns @code{#f}."
+ (let loop ((music music))
+ (if (not (stop? music))
+ (let ((elt (ly:music-property music 'element)))
+ (if (ly:music? elt)
+ (loop elt))
+ (for-each loop (ly:music-property music 'elements))
+ (for-each loop (ly:music-property music 'articulations))))))
+
+(define-public (fold-some-music pred? proc init music)
+ "This works recursively on music like @code{fold} does on a list,
+calling @samp{(@var{pred?} music)} on every music element. If
+@code{#f} is returned for an element, it is processed recursively
+with the same initial value of @samp{previous}, otherwise
+@samp{(@var{proc} music previous)} replaces @samp{previous}
+and no recursion happens.
+The top @var{music} is processed using @var{init} for @samp{previous}."
+ (let loop ((music music) (previous init))
+ (if (pred? music)
+ (proc music previous)
+ (fold loop
+ (fold loop
+ (let ((elt (ly:music-property music 'element)))
+ (if (null? elt)
+ previous
+ (loop elt previous)))
+ (ly:music-property music 'elements))
+ (ly:music-property music 'articulations)))))
+
+(define-public (extract-music music pred?)
+ "Return a flat list of all music matching @var{pred?} inside of
+@var{music}, not recursing into matches themselves."
+ (reverse! (fold-some-music pred? cons '() music)))
+
(define-public (extract-named-music music music-name)
- "Return a flat list of all music named @var{music-name} from @var{music}."
- (let ((extracted-list
- (if (ly:music? music)
- (if (eq? (ly:music-property music 'name) music-name)
- (list music)
- (let ((elt (ly:music-property music 'element))
- (elts (ly:music-property music 'elements)))
- (if (ly:music? elt)
- (extract-named-music elt music-name)
- (if (null? elts)
- '()
- (map (lambda(x)
- (extract-named-music x music-name ))
- elts)))))
- '())))
- (flatten-list extracted-list)))
+ "Return a flat list of all music named @var{music-name} (either a
+single event symbol or a list of alternatives) inside of @var{music},
+not recursing into matches themselves."
+ (extract-music
+ music
+ (if (cheap-list? music-name)
+ (lambda (m) (memq (ly:music-property m 'name) music-name))
+ (lambda (m) (eq? (ly:music-property m 'name) music-name)))))
+
+(define-public (extract-typed-music music type)
+ "Return a flat list of all music with @var{type} (either a single
+type symbol or a list of alternatives) inside of @var{music}, not
+recursing into matches themselves."
+ (extract-music
+ music
+ (if (cheap-list? type)
+ (lambda (m)
+ (any (lambda (t) (music-is-of-type? m t)) type))
+ (lambda (m) (music-is-of-type? m type)))))
+
+(define*-public (event-chord-wrap! music #:optional parser)
+ "Wrap isolated rhythmic events and non-postevent events in
+@var{music} inside of an @code{EventChord}. If the optional
+@var{parser} argument is given, chord repeats @samp{q} are expanded
+using the default settings. Otherwise, you need to cater for them
+yourself."
+ (map-some-music
+ (lambda (m)
+ (cond ((music-is-of-type? m 'event-chord)
+ (if (pair? (ly:music-property m 'articulations))
+ (begin
+ (set! (ly:music-property m 'elements)
+ (append (ly:music-property m 'elements)
+ (ly:music-property m 'articulations)))
+ (set! (ly:music-property m 'articulations) '())))
+ m)
+ ((music-is-of-type? m 'rhythmic-event)
+ (let ((arts (ly:music-property m 'articulations)))
+ (if (pair? arts)
+ (set! (ly:music-property m 'articulations) '()))
+ (make-event-chord (cons m arts))))
+ (else #f)))
+ (if parser
+ (expand-repeat-chords!
+ (cons 'rhythmic-event
+ (ly:parser-lookup parser '$chord-repeat-events))
+ music)
+ music)))
(define-public (event-chord-notes event-chord)
"Return a list of all notes from @var{event-chord}."
"Return a list of all pitches from @var{event-chord}."
(map (lambda (x) (ly:music-property x 'pitch))
(event-chord-notes event-chord)))
+
+(defmacro-public make-relative (pitches last-pitch music)
+ "The list of pitch-carrying variables in @var{pitches} is used as a
+sequence for creating relativable music from @var{music}.
+The variables in @var{pitches} are, when considered inside of
+@code{\\relative}, all considered to be specifications to the preceding
+variable. The first variable is relative to the preceding musical
+context, and @var{last-pitch} specifies the pitch passed as relative
+base onto the following musical context."
+
+ ;; pitch and music generator might be stored instead in music
+ ;; properties, and it might make sense to create a music type of its
+ ;; own for this kind of construct rather than using
+ ;; RelativeOctaveMusic
+ (define ((make-relative::to-relative-callback pitches p->m p->p) music pitch)
+ (let* ((chord (make-event-chord
+ (map
+ (lambda (p)
+ (make-music 'NoteEvent
+ 'pitch p))
+ pitches)))
+ (pitchout (begin
+ (ly:make-music-relative! chord pitch)
+ (event-chord-pitches chord))))
+ (set! (ly:music-property music 'element)
+ (apply p->m pitchout))
+ (apply p->p pitchout)))
+ `(make-music 'RelativeOctaveMusic
+ 'to-relative-callback
+ (,make-relative::to-relative-callback
+ (list ,@pitches)
+ (lambda ,pitches ,music)
+ (lambda ,pitches ,last-pitch))
+ 'element ,music))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; The following functions are all associated with the crossStaff
+; function
+
+(define (close-enough? x y)
+ "Values are close enough to ignore the difference"
+ (< (abs (- x y)) 0.0001))
+
+(define (extent-combine extents)
+ "Combine a list of extents"
+ (if (pair? (cdr extents))
+ (interval-union (car extents) (extent-combine (cdr extents)))
+ (car extents)))
+
+(define ((stem-connectable? ref root) stem)
+ "Check if the stem is connectable to the root"
+ ; The root is always connectable to itself
+ (or (eq? root stem)
+ (and
+ ; Horizontal positions of the stems must be almost the same
+ (close-enough? (car (ly:grob-extent root ref X))
+ (car (ly:grob-extent stem ref X)))
+ ; The stem must be in the direction away from the root's notehead
+ (positive? (* (ly:grob-property root 'direction)
+ (- (car (ly:grob-extent stem ref Y))
+ (car (ly:grob-extent root ref Y))))))))
+
+(define (stem-span-stencil span)
+ "Connect stems if we have at least one stem connectable to the root"
+ (let* ((system (ly:grob-system span))
+ (root (ly:grob-parent span X))
+ (stems (filter (stem-connectable? system root)
+ (ly:grob-object span 'stems))))
+ (if (<= 2 (length stems))
+ (let* ((yextents (map (lambda (st)
+ (ly:grob-extent st system Y)) stems))
+ (yextent (extent-combine yextents))
+ (layout (ly:grob-layout root))
+ (blot (ly:output-def-lookup layout 'blot-diameter)))
+ ; Hide spanned stems
+ (map (lambda (st)
+ (set! (ly:grob-property st 'transparent) #t))
+ stems)
+ ; Draw a nice looking stem with rounded corners
+ (ly:round-filled-box (ly:grob-extent root root X) yextent blot))
+ ; Nothing to connect, don't draw the span
+ #f)))
+
+(define ((make-stem-span! stems trans) root)
+ "Create a stem span as a child of the cross-staff stem (the root)"
+ (let ((span (ly:engraver-make-grob trans 'Stem '())))
+ (ly:grob-set-parent! span X root)
+ (set! (ly:grob-object span 'stems) stems)
+ ; Suppress positioning, the stem code is confused by this weird stem
+ (set! (ly:grob-property span 'X-offset) 0)
+ (set! (ly:grob-property span 'stencil) stem-span-stencil)))
+
+(define-public (cross-staff-connect stem)
+ "Set cross-staff property of the stem to this function to connect it to
+other stems automatically"
+ #t)
+
+(define (stem-is-root? stem)
+ "Check if automatic connecting of the stem was requested. Stems connected
+to cross-staff beams are cross-staff, but they should not be connected to
+other stems just because of that."
+ (eq? cross-staff-connect (ly:grob-property-data stem 'cross-staff)))
+
+(define (make-stem-spans! ctx stems trans)
+ "Create stem spans for cross-staff stems"
+ ; Cannot do extensive checks here, just make sure there are at least
+ ; two stems at this musical moment
+ (if (<= 2 (length stems))
+ (let ((roots (filter stem-is-root? stems)))
+ (map (make-stem-span! stems trans) roots))))
+
+(define-public (Span_stem_engraver ctx)
+ "Connect cross-staff stems to the stems above in the system"
+ (let ((stems '()))
+ (make-engraver
+ ; Record all stems for the given moment
+ (acknowledgers
+ ((stem-interface trans grob source)
+ (set! stems (cons grob stems))))
+ ; Process stems and reset the stem list to empty
+ ((process-acknowledged trans)
+ (make-stem-spans! ctx stems trans)
+ (set! stems '())))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; The following is used by the alterBroken function.
+
+(define-public ((value-for-spanner-piece arg) grob)
+ "Associate a piece of broken spanner @var{grob} with an element
+of list @var{arg}."
+ (let* ((orig (ly:grob-original grob))
+ (siblings (ly:spanner-broken-into orig)))
+
+ (define (helper sibs arg)
+ (if (null? arg)
+ arg
+ (if (eq? (car sibs) grob)
+ (car arg)
+ (helper (cdr sibs) (cdr arg)))))
+
+ (if (>= (length siblings) 2)
+ (helper siblings arg)
+ (car arg))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; measure counter
+
+(define (measure-counter-stencil grob)
+ "Print a number for a measure count. The number is centered using
+the extents of @code{BreakAlignment} grobs associated with
+@code{NonMusicalPaperColumn} grobs. In the case of an unbroken measure, these
+columns are the left and right bounds of a @code{MeasureCounter} spanner.
+Broken measures are numbered in parentheses."
+ (let* ((orig (ly:grob-original grob))
+ (siblings (ly:spanner-broken-into orig)) ; have we been split?
+ (bounds (ly:grob-array->list (ly:grob-object grob 'columns)))
+ (refp (ly:grob-system grob))
+ ; we use the first and/or last NonMusicalPaperColumn grob(s) of
+ ; a system in the event that a MeasureCounter spanner is broken
+ (all-cols (ly:grob-array->list (ly:grob-object refp 'columns)))
+ (all-cols
+ (filter
+ (lambda (col) (eq? #t (ly:grob-property col 'non-musical)))
+ all-cols))
+ (left-bound
+ (if (or (null? siblings) ; spanner is unbroken
+ (eq? grob (car siblings))) ; or the first piece
+ (car bounds)
+ (car all-cols)))
+ (right-bound
+ (if (or (null? siblings)
+ (eq? grob (car (reverse siblings))))
+ (car (reverse bounds))
+ (car (reverse all-cols))))
+ (elts-L (ly:grob-array->list (ly:grob-object left-bound 'elements)))
+ (elts-R (ly:grob-array->list (ly:grob-object right-bound 'elements)))
+ (break-alignment-L
+ (filter
+ (lambda (elt) (grob::has-interface elt 'break-alignment-interface))
+ elts-L))
+ (break-alignment-R
+ (filter
+ (lambda (elt) (grob::has-interface elt 'break-alignment-interface))
+ elts-R))
+ (break-alignment-L-ext (ly:grob-extent (car break-alignment-L) refp X))
+ (break-alignment-R-ext (ly:grob-extent (car break-alignment-R) refp X))
+ (num (markup (number->string (ly:grob-property grob 'count-from))))
+ (num
+ (if (or (null? siblings)
+ (eq? grob (car siblings)))
+ num
+ (make-parenthesize-markup num)))
+ (num (grob-interpret-markup grob num))
+ (num (ly:stencil-aligned-to num X (ly:grob-property grob 'self-alignment-X)))
+ (num
+ (ly:stencil-translate-axis
+ num
+ (+ (interval-length break-alignment-L-ext)
+ (* 0.5
+ (- (car break-alignment-R-ext)
+ (cdr break-alignment-L-ext))))
+ X)))
+ num))