\version "2.23.6" % TODO: Rename variables for clarity % TODO: Turn re-implementations of C++ helper functions into exported callbacks % Not in guile core for 1.8 (remove for Guile2) #(define (hash-count pred table) (count identity (hash-map->list pred table))) # (define (hash-non-empty? hash-table) ;; For Guile2, simplfy to ;; (positive? (hash-count (const #t) hash-table))) (pair? (hash-map->list (lambda (key handle) '()) hash-table))) # (define (tie-column::add_tie tie-column tie) ;;; TODO: Make callback from C++ (if (not (grob::has-interface (ly:grob-parent tie Y) 'tie-column-interface)) (begin (if (or (null? (ly:spanner-bound tie-column LEFT)) (> (car (ly:grob-spanned-column-rank-interval tie-column)) ; THINK: is this exactly equivalent to the C++ original? (car (ly:grob-spanned-column-rank-interval tie-column)))) (begin (ly:spanner-set-bound! tie-column LEFT (ly:spanner-bound tie LEFT)) (ly:spanner-set-bound! tie-column RIGHT (ly:spanner-bound tie RIGHT)))) (ly:grob-set-parent! tie Y tie-column) (ly:pointer-group-interface::add-grob tie-column 'ties tie)))) %{ head-event-alist has the fields: '((end-moment . #f) (tie-stream-event . #f) (tie-articulation-event . #f) (tie-from-chord-created . #f) (tie . #f) ) %} # (define (ly:enharmonic-equivalent? p1 p2) (= (ly:pitch-tones p1) (ly:pitch-tones p2))) # (define (ly:tie::head tie dir) (let ((it (ly:spanner-bound tie dir))) (if (grob::has-interface it 'note-head-interface) it #f))) # (define-public (New_tie_engraver context) (define (report-unterminated-tie notehead alist) ;; give notehead argument in order to simplify use of ;; report-unterminated-tie as a proc in hash-for-each (if (not (assq-ref alist 'tie-from-chord-created)) (begin (ly:warning (G_ "unterminated tie")) ; TODO: Warn with source position (ly:grob-suicide! (assq-ref alist 'tie))))) (let ((event-processed #f) (tie-stream-event #f) ; corresponds to event_ in C++ (tie-column #f) (now-heads '()) (heads-to-tie (make-hash-table)) (ties '()) (target (ly:context-name context))) (define (typeset-tie her) ;; this seems not to change anything for "her" if both bounds ;; are note heads ??? (let ((left-head (ly:tie::head her LEFT)) (right-head (ly:tie::head her RIGHT))) (if (not (and left-head right-head)) (begin (ly:warning "lonely tie") (if (not left-head) (set! left-head right-head) (set! right-head left-head)))) (ly:spanner-set-bound! her LEFT left-head) (ly:spanner-set-bound! her RIGHT right-head))) (define (tie-notehead engraver head enharmonic?) (let ((found #f)) (hash-for-each (lambda (registered-head alist) (let* ((right-ev (event-cause head)) (left-head registered-head) (left-ev (event-cause left-head))) (if (and (not found) left-ev right-ev) (let ((p1 (ly:event-property left-ev 'pitch)) (p2 (ly:event-property right-ev 'pitch))) (if (and ((if enharmonic? ly:enharmonic-equivalent? equal?) p1 p2) ;; Do not create tie for events split by ;; Completion_heads_engraver (not (ly:event-property left-ev 'autosplit-end #f))) (let* ((tie (assq-ref alist 'tie)) (end (assq-ref alist 'end-moment)) (tie-event (assq-ref alist 'tie-articulation-event)) (cause (if tie-event tie-event (assq-ref alist 'tie-stream-event))) (cause-direction (ly:event-property cause 'direction #f))) (ly:engraver-announce-end-grob engraver tie cause) (ly:spanner-set-bound! tie RIGHT head) (ly:spanner-set-bound! tie LEFT left-head) (if cause-direction (ly:grob-set-property! tie 'direction cause-direction)) (set! ties (cons tie ties)) (set! found #t) (hash-remove! heads-to-tie registered-head) (hash-for-each (lambda (other-head alist) (if (equal? (assq-ref alist 'end-moment) end) (hash-set! heads-to-tie other-head (assq-set! alist 'tie-from-chord-created #t)))) heads-to-tie))))))) heads-to-tie) found)) (make-engraver ((start-translation-timestep translator) (if (and (hash-non-empty? heads-to-tie) (not (ly:context-property context 'tieWaitForNote #f))) (let ((now (ly:context-current-moment context))) (hash-for-each (lambda (head-event alist) (if (ly:moment~ ;; } (reverse now-heads)) (if (and (not wait) (pair? new-heads-to-tie)) (begin (hash-for-each report-unterminated-tie heads-to-tie) (hash-clear! heads-to-tie))) (for-each (lambda (new-head-entry) (hash-set! heads-to-tie (car new-head-entry) (cdr new-head-entry))) new-heads-to-tie) (set! now-heads '()))) ((stop-translation-timestep engraver) ;; Discard event only if it has been processed with at least one ;; appropriate note. (if event-processed (set! tie-stream-event #f)) (set! event-processed #f))))) to = #(define-event-function (id event) (key? ly:event?) (set! (ly:music-property event 'spanner-target) id) event) \layout { \context { \Voice \remove Tie_engraver \consists #New_tie_engraver } } % -------------------------------------------------------------------- % \new Staff \with { \consists #New_tie_engraver } { << \relative { 4 c8 b a g~ 4 } \\ \relative { s4 c'2 e4 } >> }