Am Mi., 20. Juli 2022 um 18:43 Uhr schrieb Martín Rincón Botero <
martinrinconbot...@gmail.com>:

> Hello,
>
> I grabbed this snippet a while ago from somewhere™ which puts a box
> around music (an "improvisation box"). It works perfectly in Lilypond
> 2.22.1, but not in Lilypond 2.23.3+. Does somebody know how to make it work
> in more recent versions? This is the snippet:
>
> \version "2.19.15"
>
> \header {
>   tagline = ##f
> }
>
> #(define-event-class 'music-boxer-event 'span-event)
>
> #(define-event-class 'box-event 'music-event)
>
> #(define (add-grob-definition grob-name grob-entry)
>    (let* ((meta-entry   (assoc-get 'meta grob-entry))
>           (class        (assoc-get 'class meta-entry))
>           (ifaces-entry (assoc-get 'interfaces meta-entry)))
>      ;; change ly:grob-properties? to list? to work from 2.19.12 back to
> at least 2.18.2
>      (set-object-property! grob-name 'translation-type?
> ly:grob-properties?)
>      (set-object-property! grob-name 'is-grob? #t)
>      (set! ifaces-entry (append (case class
>                                   ((Item) '(item-interface))
>                                   ((Spanner) '(spanner-interface))
>                                   ((Paper_column) '((item-interface
>
>  paper-column-interface)))
>                                   ((System) '((system-interface
>                                                spanner-interface)))
>                                   (else '(unknown-interface)))
>                           ifaces-entry))
>      (set! ifaces-entry (uniq-list (sort ifaces-entry symbol<?)))
>      (set! ifaces-entry (cons 'grob-interface ifaces-entry))
>      (set! meta-entry (assoc-set! meta-entry 'name grob-name))
>      (set! meta-entry (assoc-set! meta-entry 'interfaces
>                         ifaces-entry))
>      (set! grob-entry (assoc-set! grob-entry 'meta meta-entry))
>      (set! all-grob-descriptions
>            (cons (cons grob-name grob-entry)
>              all-grob-descriptions))))
>
> #(define (make-box thick padding xext yext)
>    (let ((xext (interval-widen xext padding))
>          (yext (interval-widen yext padding)))
>    (ly:stencil-add
>     (make-filled-box-stencil xext (cons (- (car yext) thick) (car yext)))
>     (make-filled-box-stencil xext (cons (cdr yext) (+ (cdr yext) thick)))
>     (make-filled-box-stencil (cons (cdr xext) (+ (cdr xext) thick)) yext)
>     (make-filled-box-stencil (cons (- (car xext) thick) (car xext))
> yext))))
>
> #(define (music-boxer-stencil grob)
>    (let* ((elts (ly:grob-object grob 'elements))
>           (refp-X (ly:grob-common-refpoint-of-array grob elts X))
>           (X-ext (ly:relative-group-extent elts refp-X X))
>           (refp-Y (ly:grob-common-refpoint-of-array grob elts Y))
>           (Y-ext (ly:relative-group-extent elts refp-Y Y))
>           (padding (ly:grob-property grob 'padding 0.3))
>           (stil (make-box 0.1 padding X-ext Y-ext))
>           (offset (ly:grob-relative-coordinate grob refp-X X)))
>      (ly:stencil-translate-axis stil (- offset) X)))
>
> #(define box-stil music-boxer-stencil)
>
> #(add-grob-definition
>   'Box
>   `(
>      (stencil . ,box-stil)
>      (meta . ((class . Item)
>
;; add:
               (classes . (Item))

>               (interfaces . ())))))
>
> #(add-grob-definition
>   'MusicBoxer
>   `(
>      (stencil . ,music-boxer-stencil)
>      (meta . ((class . Spanner)
>
;; add:
              (classes . (Spanner))

>               (interfaces . ())))))
>
>
> #(define box-types
>    '(
>       (BoxEvent
>        . ((description . "A box encompassing music at a single timestep.")
>           (types . (general-music box-event music-event event))
>           ))
>       ))
>
> #(define music-boxer-types
>    '(
>       (MusicBoxerEvent
>        . ((description . "Used to signal where boxes encompassing music
> start and stop.")
>           (types . (general-music music-boxer-event span-event event))
>           ))
>       ))
>
>
> #(set!
>   music-boxer-types
>   (map (lambda (x)
>          (set-object-property! (car x)
>            'music-description
>            (cdr (assq 'description (cdr x))))
>          (let ((lst (cdr x)))
>            (set! lst (assoc-set! lst 'name (car x)))
>            (set! lst (assq-remove! lst 'description))
>            (hashq-set! music-name-to-property-table (car x) lst)
>            (cons (car x) lst)))
>     music-boxer-types))
>
> #(set!
>   box-types
>   (map (lambda (x)
>          (set-object-property! (car x)
>            'music-description
>            (cdr (assq 'description (cdr x))))
>          (let ((lst (cdr x)))
>            (set! lst (assoc-set! lst 'name (car x)))
>            (set! lst (assq-remove! lst 'description))
>            (hashq-set! music-name-to-property-table (car x) lst)
>            (cons (car x) lst)))
>     box-types))
>
> #(set! music-descriptions
>        (append music-boxer-types music-descriptions))
>
> #(set! music-descriptions
>        (append box-types music-descriptions))
>
> #(set! music-descriptions
>        (sort music-descriptions alist<?))
>
>
> #(define (add-bound-item spanner item)
>    (if (null? (ly:spanner-bound spanner LEFT))
>        (ly:spanner-set-bound! spanner LEFT item)
>        (ly:spanner-set-bound! spanner RIGHT item)))
>
> musicBoxerEngraver =
> #(lambda (context)
>    (let ((span '())
>          (finished '())
>          (current-event '())
>          (event-start '())
>          (event-stop '()))
>
>      `((listeners
>         (music-boxer-event .
>           ,(lambda (engraver event)
>              (if (= START (ly:event-property event 'span-direction))
>                  (set! event-start event)
>                  (set! event-stop event)))))
>
>        (acknowledgers
>         (note-column-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:spanner? span)
>                  (begin
>                   (ly:pointer-group-interface::add-grob span 'elements
> grob)
>                   (add-bound-item span grob)))
>              (if (ly:spanner? finished)
>                  (begin
>                   (ly:pointer-group-interface::add-grob finished 'elements
> grob)
>                   (add-bound-item finished grob)))))
>
>         (inline-accidental-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:spanner? span)
>                  (begin
>                   (ly:pointer-group-interface::add-grob span 'elements
> grob)))
>              (if (ly:spanner? finished)
>                  (ly:pointer-group-interface::add-grob finished 'elements
> grob))))
>
>         (script-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:spanner? span)
>                  (begin
>                   (ly:pointer-group-interface::add-grob span 'elements
> grob)))
>              (if (ly:spanner? finished)
>                  (ly:pointer-group-interface::add-grob finished 'elements
> grob))))
>
>         (finger-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:spanner? span)
>                  (begin
>                   (ly:pointer-group-interface::add-grob span 'elements
> grob)))
>              (if (ly:spanner? finished)
>                  (ly:pointer-group-interface::add-grob finished 'elements
> grob))))
>
>         ;; add additional interfaces to acknowledge here
>
>         )
>
>        (process-music .
>          ,(lambda (trans)
>             (if (ly:stream-event? event-stop)
>                 (if (null? span)
>                     (ly:warning "No start to this box.")
>                     (begin
>                      (set! finished span)
>                      (ly:engraver-announce-end-grob trans finished
> event-start)
>                      (set! span '())
>                      (set! event-stop '()))))
>             (if (ly:stream-event? event-start)
>                 (begin
>                  (set! span (ly:engraver-make-grob trans 'MusicBoxer
> event-start))
>                  (set! event-start '())))))
>
>        (stop-translation-timestep .
>          ,(lambda (trans)
>             (if (and (ly:spanner? span)
>                      (null? (ly:spanner-bound span LEFT)))
>                 (ly:spanner-set-bound! span LEFT
>                   (ly:context-property context 'currentMusicalColumn)))
>             (if (ly:spanner? finished)
>                 (begin
>                  (if (null? (ly:spanner-bound finished RIGHT))
>                      (ly:spanner-set-bound! finished RIGHT
>                        (ly:context-property context
> 'currentMusicalColumn)))
>                  (set! finished '())
>                  (set! event-start '())
>                  (set! event-stop '())))))
>
>        (finalize
>         (lambda (trans)
>           (if (ly:spanner? finished)
>               (begin
>                (if (null? (ly:spanner-bound finished RIGHT))
>                    (set! (ly:spanner-bound finished RIGHT)
>                          (ly:context-property context
> 'currentMusicalColumn)))
>                (set! finished '())))
>           (if (ly:spanner? span)
>               (begin
>                (ly:warning "unterminated box :-(")
>                (ly:grob-suicide! span)
>                (set! span '()))))))))
>
>
> boxEngraver =
> #(lambda (context)
>    (let ((box '())
>          (ev '()))
>
>      `((listeners
>         (box-event .
>           ,(lambda (engraver event)
>              (set! ev event))))
>
>        (acknowledgers
>         (note-column-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:grob? box)
>                  (begin
>                   ; (set! (ly:grob-parent box X) grob) ;; ??
>                    (set! (ly:grob-parent box Y) grob)
>                  (ly:pointer-group-interface::add-grob box 'elements
> grob)))))
>
>         (inline-accidental-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:item? box)
>                  (ly:pointer-group-interface::add-grob box 'elements
> grob))))
>
>         (script-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:item? box)
>                  (ly:pointer-group-interface::add-grob box 'elements
> grob))))
>
>         (finger-interface .
>           ,(lambda (engraver grob source-engraver)
>              (if (ly:item? box)
>                  (ly:pointer-group-interface::add-grob box 'elements
> grob))))
>
>         ;; add additional interfaces to acknowledge here
>
>         )
>
>        (process-music .
>          ,(lambda (trans)
>             (if (ly:stream-event? ev)
>                 (begin
>                  (set! box (ly:engraver-make-grob trans 'Box ev))
>                  (set! ev '())))))
>        (stop-translation-timestep .
>          ,(lambda (trans)
>             (set! box '()))))))
>
> musicBoxerStart =
> #(make-span-event 'MusicBoxerEvent START)
>
> musicBoxerEnd =
> #(make-span-event 'MusicBoxerEvent STOP)
>
> box = #(make-music 'BoxEvent)
>
> \layout {
>   \context {
>     \Global
>     \grobdescriptions #all-grob-descriptions
>   }
>   \context {
>     \Score
>     \consists \musicBoxerEngraver % for spans
>     \consists \boxEngraver
>   }
> }
>
>
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% EXAMPLE
> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
>
> melody = \relative c'' {
>   \set fingeringOrientations = #'(left)
>   %1
>   \repeat volta 2 {
>     %\once\override Score.Box.padding = 1
>     \box <g-3  c-2 f-1>1
>     \musicBoxerStart d8-4 g,-0 d' g, d'-4 g,-0 d' \musicBoxerEnd g,
>   }
>
>   %2
>   \repeat volta 2 {
>     \box <d'-4  c'-2 f-1>1\f\fermata
>     \musicBoxerStart g8-3 d-0 g d g8-4 d-0 g \musicBoxerEnd d\accent
>   }
> }
>
> \score {
>   \new Staff \melody
> }
>
> It should produce:
>
> It produces instead: fatal error: meta.classes must be non-empty list,
> found #f
>

Should work now for recent 2.23.

HTH,
  Harm

Reply via email to