along with @var{minimum-fret}, @var{maximum-stretch}, and
@var{tuning}. Returns a list of @code{(string fret finger) lists."
+
+ (define restrain-open-strings (ly:context-property context
+ 'restrainOpenStrings
+ #f))
(define specified-frets '())
(define free-strings (iota (length tuning) 1))
(define (calc-fret pitch string tuning)
"Calculate the fret to play @var{pitch} on @var{string} with
@var{tuning}."
- (- (ly:pitch-semitones pitch) (ly:pitch-semitones (list-ref tuning (1- string)))))
+ (* 2 (- (ly:pitch-tones pitch) (ly:pitch-tones (list-ref tuning (1- string))))))
(define (note-pitch note)
"Get the pitch (in semitones) from @var{note}."
(map (lambda (art)
(let* ((num (ly:event-property art 'digit)))
- (if (and (eq? 'fingering-event (ly:event-property art 'class))
+ (if (and (ly:in-event-class? art 'fingering-event)
(number? num)
(> num 0))
(set! finger-found num))))
#t
(map (lambda (specced-fret)
(or (eq? 0 specced-fret)
+ (and (not restrain-open-strings)
+ (eq? 0 fret))
(>= maximum-stretch (abs (- fret specced-fret)))))
specified-frets))))
"Can @var{pitch} be played on @var{string}, given already placed
notes?"
(let* ((fret (calc-fret pitch string tuning)))
- (and (>= fret minimum-fret)
+ (and (or (and (not restrain-open-strings)
+ (eq? fret 0))
+ (>= fret minimum-fret))
+ (integer? fret)
(close-enough fret))))
(define (open-string string pitch)
tuning)))
(if (< this-fret 0)
(ly:warning (_ "Negative fret for pitch ~a on string ~a")
- (car pitch-entry) string))
+ (car pitch-entry) string)
+ (if (not (integer? this-fret))
+ (ly:warning (_ "Missing fret for pitch ~a on string ~a")
+ (car pitch-entry) string)))
(delete-free-string string)
(set! specified-frets (cons this-fret specified-frets))
(list-set! string-fret-fingers
(ly:context-property context
'handleNegativeFrets
'recalculate)))
- (cond ((or (>= this-fret 0)
+ (cond ((or (and (>= this-fret 0) (integer? this-fret))
(eq? handle-negative 'include))
(set-fret! pitch-entry string finger))
((eq? handle-negative 'recalculate)
(= 0 (modulo count n)))
(define-public (all-repeat-counts-visible count context) #t)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; make-engraver helper macro
+
+(defmacro-public make-engraver forms
+ "Helper macro for creating Scheme engravers.
+
+The usual form for an engraver is an association list (or alist)
+mapping symbols to either anonymous functions or to another such
+alist.
+
+@code{make-engraver} accepts forms where the first element is either
+an argument list starting with the respective symbol, followed by the
+function body (comparable to the way @code{define} is used for
+defining functions), or a single symbol followed by subordinate forms
+in the same manner. You can also just make an alist pair
+literally (the @samp{car} is quoted automatically) as long as the
+unevaluated @samp{cdr} is not a pair. This is useful if you already
+have defined your engraver functions separately.
+
+Symbols mapping to a function would be @code{initialize},
+@code{start-translation-timestep}, @code{process-music},
+@code{process-acknowledged}, @code{stop-translation-timestep}, and
+@code{finalize}. Symbols mapping to another alist specified in the
+same manner are @code{listeners} with the subordinate symbols being
+event classes, and @code{acknowledgers} and @code{end-acknowledgers}
+with the subordinate symbols being interfaces."
+ (let loop ((forms forms))
+ (if (cheap-list? forms)
+ `(list
+ ,@(map (lambda (form)
+ (if (pair? (car form))
+ `(cons ',(caar form) (lambda ,(cdar form) ,@(cdr form)))
+ `(cons ',(car form) ,(loop (cdr form)))))
+ forms))
+ forms)))