]> git.donarmstrong.com Git - lilypond.git/blob - scm/translation-functions.scm
Issue 3484: Give input location for "No string for pitch ..." warning
[lilypond.git] / scm / translation-functions.scm
1 ;;;; This file is part of LilyPond, the GNU music typesetter.
2 ;;;;
3 ;;;; (c) 1998--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
4 ;;;;                 Jan Nieuwenhuizen <janneke@gnu.org>
5 ;;;;
6 ;;;; LilyPond is free software: you can redistribute it and/or modify
7 ;;;; it under the terms of the GNU General Public License as published by
8 ;;;; the Free Software Foundation, either version 3 of the License, or
9 ;;;; (at your option) any later version.
10 ;;;;
11 ;;;; LilyPond is distributed in the hope that it will be useful,
12 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ;;;; GNU General Public License for more details.
15 ;;;;
16 ;;;; You should have received a copy of the GNU General Public License
17 ;;;; along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18
19
20 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21 ;; clefs
22
23 (define-public (clef-transposition-markup oct style)
24   "The transposition sign formatting function.  @var{oct} is supposed to be
25 a string holding the transposition number, @var{style} determines the
26 way the transposition number is displayed."
27   (let* ((delim (if (symbol? style)
28                     (case style
29                       ((parenthesized) (cons "(" ")"))
30                       ((bracketed) (cons "[" "]"))
31                       (else (cons "" "")))
32                     (cons "" "")))
33          (text (string-concatenate (list (car delim) oct (cdr delim)))))
34
35     (make-vcenter-markup text)))
36
37
38 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
39 ;; metronome marks
40
41 (define-public (format-metronome-markup event context)
42   (let ((hide-note (ly:context-property context 'tempoHideNote #f))
43         (text (ly:event-property event 'text))
44         (dur (ly:event-property event 'tempo-unit))
45         (count (ly:event-property event 'metronome-count)))
46
47     (metronome-markup text dur count hide-note)))
48
49 (define-public (metronome-markup text dur count hide-note)
50   (let* ((note-mark (if (and (not hide-note) (ly:duration? dur))
51                         (make-smaller-markup
52                          (make-note-by-number-markup (ly:duration-log dur)
53                                                      (ly:duration-dot-count dur)
54                                                      1))
55                         #f))
56          (count-markup (cond ((number? count)
57                               (if (> count 0)
58                                   (make-simple-markup (number->string count))
59                                   #f))
60                              ((pair? count)
61                               (make-concat-markup
62                                (list
63                                 (make-simple-markup (number->string (car count)))
64                                 (make-simple-markup " ")
65                                 (make-simple-markup "–")
66                                 (make-simple-markup " ")
67                                 (make-simple-markup (number->string (cdr count))))))
68                              (else #f)))
69          (note-markup (if (and (not hide-note) count-markup)
70                           (make-concat-markup
71                            (list
72                             (make-general-align-markup Y DOWN note-mark)
73                             (make-simple-markup " ")
74                             (make-simple-markup "=")
75                             (make-simple-markup " ")
76                             count-markup))
77                           #f))
78          (text-markup (if (not (null? text))
79                           (make-bold-markup text)
80                           #f)))
81     (if text-markup
82         (if (and note-markup (not hide-note))
83             (make-line-markup (list text-markup
84                                     (make-concat-markup
85                                      (list (make-simple-markup "(")
86                                            note-markup
87                                            (make-simple-markup ")")))))
88             (make-line-markup (list text-markup)))
89         (if note-markup
90             (make-line-markup (list note-markup))
91             (make-null-markup)))))
92
93 (define-public (format-mark-alphabet mark context)
94   (make-bold-markup (make-markalphabet-markup (1- mark))))
95
96 (define-public (format-mark-box-alphabet mark context)
97   (make-bold-markup (make-box-markup (make-markalphabet-markup (1- mark)))))
98
99 (define-public (format-mark-circle-alphabet mark context)
100   (make-bold-markup (make-circle-markup (make-markalphabet-markup (1- mark)))))
101
102 (define-public (format-mark-letters mark context)
103   (make-bold-markup (make-markletter-markup (1- mark))))
104
105 (define-public (format-mark-numbers mark context)
106   (make-bold-markup (number->string mark)))
107
108 (define-public (format-mark-barnumbers mark context)
109   (make-bold-markup (number->string (ly:context-property context
110                                                          'currentBarNumber))))
111
112 (define-public (format-mark-box-letters mark context)
113   (make-bold-markup (make-box-markup (make-markletter-markup (1- mark)))))
114
115 (define-public (format-mark-circle-letters mark context)
116   (make-bold-markup (make-circle-markup (make-markletter-markup (1- mark)))))
117
118 (define-public (format-mark-box-numbers mark context)
119   (make-bold-markup (make-box-markup (number->string mark))))
120
121 (define-public (format-mark-circle-numbers mark context)
122   (make-bold-markup (make-circle-markup (number->string mark))))
123
124 (define-public (format-mark-box-barnumbers mark context)
125   (make-bold-markup (make-box-markup
126                      (number->string (ly:context-property context
127                                                           'currentBarNumber)))))
128
129 (define-public (format-mark-circle-barnumbers mark context)
130   (make-bold-markup (make-circle-markup
131                      (number->string (ly:context-property context
132                                                           'currentBarNumber)))))
133
134
135 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
136 ;; Bass figures.
137
138 (define-public (format-bass-figure figure event context)
139   (let* ((fig (ly:event-property event 'figure))
140          (fig-markup (if (number? figure)
141
142                          ;; this is not very elegant, but center-aligning
143                          ;; all digits is problematic with other markups,
144                          ;; and shows problems in the (lack of) overshoot
145                          ;; of feta-alphabet glyphs.
146                          ((if (<= 10 figure)
147                               (lambda (y) (make-translate-scaled-markup
148                                            (cons -0.7 0) y))
149                               identity)
150
151                           (cond
152                            ((eq? #t (ly:event-property event 'diminished))
153                             (markup #:slashed-digit figure))
154                            ((eq? #t (ly:event-property event 'augmented-slash))
155                             (markup #:backslashed-digit figure))
156                            (else (markup #:number (number->string figure 10)))))
157                          #f))
158
159          (alt (ly:event-property event 'alteration))
160          (alt-markup
161           (if (number? alt)
162               (markup
163                #:general-align Y DOWN #:fontsize
164                (if (not (= alt DOUBLE-SHARP))
165                    -2 2)
166                (alteration->text-accidental-markup alt))
167               #f))
168
169          (plus-markup (if (eq? #t (ly:event-property event 'augmented))
170                           (markup #:number "+")
171                           #f))
172
173          (alt-dir (ly:context-property context 'figuredBassAlterationDirection))
174          (plus-dir (ly:context-property context 'figuredBassPlusDirection)))
175
176     (if (and (not fig-markup) alt-markup)
177         (begin
178           (set! fig-markup (markup #:left-align #:pad-around 0.3 alt-markup))
179           (set! alt-markup #f)))
180
181
182     ;; hmm, how to get figures centered between note, and
183     ;; lone accidentals too?
184
185     ;;    (if (markup? fig-markup)
186     ;;  (set!
187     ;;   fig-markup (markup #:translate (cons 1.0 0)
188     ;;                      #:center-align fig-markup)))
189
190     (if alt-markup
191         (set! fig-markup
192               (markup #:put-adjacent
193                       X (if (number? alt-dir)
194                             alt-dir
195                             LEFT)
196                       fig-markup
197                       #:pad-x 0.2 alt-markup)))
198
199     (if plus-markup
200         (set! fig-markup
201               (if fig-markup
202                   (markup #:put-adjacent
203                           X (if (number? plus-dir)
204                                 plus-dir
205                                 LEFT)
206                           fig-markup
207                           #:pad-x 0.2 plus-markup)
208                   plus-markup)))
209
210     (if (markup? fig-markup)
211         (markup #:fontsize -2 fig-markup)
212         empty-markup)))
213
214
215 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
216 ;; fret diagrams
217
218 (define (create-fretboard context grob placement-list)
219   "Convert @var{placement-list} into a fretboard @var{grob}."
220
221   (let* ((tunings (ly:context-property context 'stringTunings))
222          (my-string-count (length tunings))
223          (details (ly:grob-property grob 'fret-diagram-details)))
224
225     ;; Add string-count from string-tunings to fret-diagram-details.
226     (set! (ly:grob-property grob 'fret-diagram-details)
227           (acons 'string-count my-string-count details))
228     ;; Create the dot-placement list for the grob
229     (set! (ly:grob-property grob 'dot-placement-list) placement-list)))
230
231 (define-public
232   (determine-frets context notes specified-info . rest)
233   "Determine string numbers and frets for playing @var{notes}
234 as a chord, given specified information @var{specified-info}.
235 @var{specified-info} is a list with two list elements,
236 specified strings @code{defined-strings} and
237 specified fingerings @code{defined-fingers}.  Only a fingering of@tie{}0
238 will affect the fret selection, as it specifies an open string.
239 If @code{defined-strings} is @code{'()}, the context property
240 @code{defaultStrings} will be used as a list of defined strings.
241 Will look for predefined fretboards if @code{predefinedFretboardTable}
242 is not @code {#f}.  If @var{rest} is present, it contains the
243 @code{FretBoard} grob, and a fretboard will be
244 created.  Otherwise, a list of @code{(string fret finger)} lists will
245 be returned."
246
247   ;;  helper functions
248
249   (define (string-frets->placement-list string-frets string-count)
250     "Convert @var{string-frets} to @code{fret-diagram-verbose}
251 dot placement entries."
252     (let* ((placements (list->vector
253                         (map (lambda (x) (list 'mute  x))
254                              (iota string-count 1)))))
255
256       (for-each (lambda (sf)
257                   (let* ((string (car sf))
258                          (fret (cadr sf))
259                          (finger (caddr sf)))
260                     (vector-set!
261                      placements
262                      (1- string)
263                      (if (= 0 fret)
264                          (list 'open string)
265                          (if finger
266                              (list 'place-fret string fret finger)
267                              (list 'place-fret string fret))))))
268                 string-frets)
269       (vector->list placements)))
270
271   (define (placement-list->string-frets placement-list)
272     "Convert @var{placement-list} to string-fret list."
273     (map (lambda (x) (if (eq? (car x) 'place-fret)
274                          (cdr x)
275                          (list (cadr x) 0)))
276          (filter (lambda (l) (or (eq? (car l) 'place-fret)
277                                  (eq? (car l) 'open)))
278                  placement-list)))
279
280   (define (entry-count art-list)
281     "Count the number of entries in a list of articulations."
282     (length (filter (lambda (x) (not (null? x)))
283                     art-list)))
284
285   (define (string-number event)
286     "Get the string-number from @var{event}.  Return @var{#f}
287 if no string-number is present."
288     (let ((num (ly:event-property event 'string-number)))
289       (and (integer? num) (positive? num) num)))
290
291   (define (determine-frets-and-strings
292            notes
293            defined-strings
294            defined-fingers
295            minimum-fret
296            maximum-stretch
297            tuning)
298     "Determine the frets and strings used to play the notes in
299 @var{notes}, given @var{defined-strings} and @var{defined-fingers}
300 along with @var{minimum-fret}, @var{maximum-stretch}, and
301 @var{tuning}.  Returns a list of @code{(string fret finger) lists."
302
303
304     (define restrain-open-strings (ly:context-property context
305                                                        'restrainOpenStrings
306                                                        #f))
307     (define specified-frets '())
308     (define free-strings (iota (length tuning) 1))
309
310     (define (calc-fret pitch string tuning)
311       "Calculate the fret to play @var{pitch} on @var{string} with
312 @var{tuning}."
313       (* 2  (- (ly:pitch-tones pitch) (ly:pitch-tones (list-ref tuning (1- string))))))
314
315     (define (note-pitch note)
316       "Get the pitch (in semitones) from @var{note}."
317       (ly:event-property note 'pitch))
318
319     (define (note-finger ev)
320       "Get the fingering from @var{ev}.  Return @var{#f}
321 if no fingering is present."
322       (let* ((articulations (ly:event-property ev 'articulations))
323              (finger-found #f))
324         (map (lambda (art)
325                (let* ((num (ly:event-property art 'digit)))
326
327                  (if (and (ly:in-event-class? art 'fingering-event)
328                           (number? num)
329                           (> num 0))
330                      (set! finger-found num))))
331              articulations)
332         finger-found))
333
334     (define (delete-free-string string)
335       (if (number? string)
336           (set! free-strings
337                 (delete string free-strings))))
338
339     (define (close-enough fret)
340       "Decide if @var{fret} is acceptable, given the already used frets."
341       (every (lambda (specced-fret)
342                (or (zero? specced-fret)
343                    (zero? fret)
344                    (>= maximum-stretch (abs (- fret specced-fret)))))
345              specified-frets))
346
347     (define (string-qualifies string pitch)
348       "Can @var{pitch} be played on @var{string}, given already placed
349 notes?"
350       (let* ((fret (calc-fret pitch string tuning)))
351         (and (or (and (not restrain-open-strings)
352                       (zero? fret))
353                  (>= fret minimum-fret))
354              (integer? fret)
355              (close-enough fret))))
356
357     (define (open-string string pitch)
358       "Is @var{pitch} and open-string note on @var{string}, given
359 the current tuning?"
360       (let* ((fret (calc-fret pitch string tuning)))
361         (zero? fret)))
362
363     (define (set-fret! pitch-entry string finger)
364       (let ((this-fret (calc-fret (car pitch-entry)
365                                   string
366                                   tuning)))
367         (if (< this-fret 0)
368             (ly:warning (_ "Negative fret for pitch ~a on string ~a")
369                         (car pitch-entry) string)
370             (if (not (integer? this-fret))
371                 (ly:warning (_ "Missing fret for pitch ~a on string ~a")
372                             (car pitch-entry) string)))
373         (delete-free-string string)
374         (set! specified-frets (cons this-fret specified-frets))
375         (list-set! string-fret-fingers
376                    (cdr pitch-entry)
377                    (list string this-fret finger))))
378
379     (define (kill-note! string-fret-fingers note-index)
380       (list-set! string-fret-fingers note-index (list #f #t)))
381
382     (define string-fret-fingers
383       (map (lambda (string finger)
384              (if (null? finger)
385                  (list string #f)
386                  (list string #f finger)))
387            defined-strings defined-fingers))
388
389     ;;; body of determine-frets-and-strings
390     (let* ((pitches (map note-pitch notes))
391            (pitch-alist (map cons pitches (iota (length pitches)))))
392
393       ;; handle notes with strings assigned and fingering of 0
394       (for-each
395        (lambda (pitch-entry string-fret-finger)
396          (let* ((string (list-ref string-fret-finger 0))
397                 (finger (if (= (length string-fret-finger) 3)
398                             (list-ref string-fret-finger 2)
399                             '()))
400                 (pitch (car pitch-entry))
401                 (digit (if (null? finger)
402                            #f
403                            finger)))
404            (if (or (not (null? string))
405                    (eqv? digit 0))
406                (if (eqv? digit 0)
407                    ;; here we handle fingers of 0 -- open strings
408                    (let ((fit-string
409                           (find (lambda (string)
410                                   (open-string string pitch))
411                                 free-strings)))
412                      (if fit-string
413                          (set-fret! pitch-entry fit-string #f)
414                          (ly:warning (_ "No open string for pitch ~a")
415                                      pitch)))
416                    ;; here we handle assigned strings
417                    (let ((this-fret
418                           (calc-fret pitch string tuning))
419                          (handle-negative
420                           (ly:context-property context
421                                                'handleNegativeFrets
422                                                'recalculate)))
423                      (cond ((or (and (>= this-fret 0) (integer? this-fret))
424                                 (eq? handle-negative 'include))
425                             (set-fret! pitch-entry string finger))
426                            ((eq? handle-negative 'recalculate)
427                             (begin
428                               (ly:warning
429                                (_ "Requested string for pitch requires negative fret: string ~a pitch ~a")
430                                string
431                                pitch)
432                               (ly:warning (_ "Ignoring string request and recalculating."))
433                               (list-set! string-fret-fingers
434                                          (cdr pitch-entry)
435                                          (if (null? finger)
436                                              (list '() #f)
437                                              (list '() #f finger)))))
438                            ((eq? handle-negative 'ignore)
439                             (begin
440                               (ly:warning
441                                (_ "Requested string for pitch requires negative fret: string ~a pitch ~a")
442                                string
443                                pitch)
444                               (ly:warning (_ "Ignoring note in tablature."))
445                               (kill-note! string-fret-fingers
446                                           (cdr pitch-entry))))))))))
447        pitch-alist string-fret-fingers)
448       ;; handle notes without strings assigned -- sorted by pitch, so
449       ;; we need to use the alist to have the note number available
450       (for-each
451        (lambda (pitch-entry)
452          (let* ((string-fret-finger (list-ref string-fret-fingers
453                                               (cdr pitch-entry)))
454                 (string (list-ref string-fret-finger 0))
455                 (finger (if (= (length string-fret-finger) 3)
456                             (list-ref string-fret-finger 2)
457                             '()))
458                 (pitch (car pitch-entry))
459                 (fit-string
460                  (find (lambda (string)
461                          (string-qualifies string pitch))
462                        free-strings)))
463            (if (not (list-ref string-fret-finger 1))
464                (if fit-string
465                    (set-fret! pitch-entry fit-string finger)
466                    (begin
467                      (ly:event-warning
468                       (list-ref notes (cdr pitch-entry))
469                       (_ "No string for pitch ~a (given frets ~a)")
470                       pitch
471                       specified-frets)
472                      (kill-note! string-fret-fingers
473                                  (cdr pitch-entry)))))))
474        (sort pitch-alist (lambda (pitch-entry-a pitch-entry-b)
475                            (ly:pitch<? (car pitch-entry-b)
476                                        (car pitch-entry-a)))))
477       string-fret-fingers)) ;; end of determine-frets-and-strings
478
479   (define (get-predefined-fretboard predefined-fret-table tuning pitches)
480     "Search through @var{predefined-fret-table} looking for a predefined
481 fretboard with a key of @var{(tuning . pitches)}.  The search will check
482 both up and down an octave in order to accomodate transposition of the
483 chords.  Returns a placement-list."
484
485     (define (get-fretboard key)
486       (let ((hash-handle
487              (hash-get-handle predefined-fret-table key)))
488         (if hash-handle
489             (cdr hash-handle)  ; return table entry
490             '())))
491
492     ;; body of get-predefined-fretboard
493     (let ((test-fretboard (get-fretboard (cons tuning pitches))))
494       (if (not (null? test-fretboard))
495           test-fretboard
496           (let ((test-fretboard
497                  (get-fretboard
498                   (cons tuning (map (lambda (x) (shift-octave x 1)) pitches)))))
499             (if (not (null? test-fretboard))
500                 test-fretboard
501                 (get-fretboard
502                  (cons tuning (map (lambda (x) (shift-octave x -1))
503                                    pitches))))))))
504
505   ;; body of determine-frets
506   (let* ((predefined-fret-table
507           (ly:context-property context 'predefinedDiagramTable))
508          (tunings (ly:context-property context 'stringTunings))
509          (string-count (length tunings))
510          (grob (if (null? rest) '() (car rest)))
511          (pitches (map (lambda (x) (ly:event-property x 'pitch)) notes))
512          (defined-strings (map (lambda (x)
513                                  (if (null? x)
514                                      x
515                                      (or (string-number x) '())))
516                                (car specified-info)))
517          (defined-fingers (map (lambda (x)
518                                  (if (null? x)
519                                      x
520                                      (ly:event-property x 'digit)))
521                                (cadr specified-info)))
522          (default-strings (ly:context-property context 'defaultStrings '()))
523          (strings-used (if (and (zero? (entry-count defined-strings))
524                                 (not (zero? (entry-count default-strings))))
525                            default-strings
526                            defined-strings))
527          (predefined-fretboard
528           (if predefined-fret-table
529               (get-predefined-fretboard
530                predefined-fret-table
531                tunings
532                pitches)
533               '())))
534     (if (null? predefined-fretboard)
535         (let ((string-frets
536                (determine-frets-and-strings
537                 notes
538                 strings-used
539                 defined-fingers
540                 (ly:context-property context 'minimumFret 0)
541                 (ly:context-property context 'maximumFretStretch 4)
542                 tunings)))
543           (if (null? grob)
544               string-frets
545               (create-fretboard
546                context grob (string-frets->placement-list
547                              (filter (lambda (entry)
548                                        (car entry))
549                                      string-frets)
550                              string-count))))
551         (if (null? grob)
552             (placement-list->string-frets predefined-fretboard)
553             (create-fretboard context grob predefined-fretboard)))))
554
555
556
557 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
558 ;; tablature
559
560 ;; The TabNoteHead tablatureFormat callbacks.
561
562 ;; Calculate the fret from pitch and string number as letter
563 ;; The fret letter is taken from 'fretLabels if present
564 (define-public (fret-letter-tablature-format
565                 context string-number fret-number)
566   (let ((labels (ly:context-property context 'fretLabels)))
567     (make-vcenter-markup
568      (cond
569       ((= 0 (length labels))
570        (string (integer->char (+ fret-number (char->integer #\a)))))
571       ((and (<= 0 fret-number) (< fret-number (length labels)))
572        (list-ref labels fret-number))
573       (else
574        (ly:warning (_ "No label for fret ~a (on string ~a);
575 only ~a fret labels provided")
576                    fret-number string-number (length labels))
577        ".")))))
578
579 ;; Display the fret number as a number
580 (define-public (fret-number-tablature-format
581                 context string-number fret-number)
582   (make-vcenter-markup
583    (format #f "~a" fret-number)))
584
585 ;; The 5-string banjo has got a extra string, the fifth (duh), which
586 ;; starts at the fifth fret on the neck.  Frets on the fifth string
587 ;; are referred to relative to the other frets:
588 ;;   the "first fret" on the fifth string is really the sixth fret
589 ;;   on the banjo neck.
590 ;; We solve this by defining a new fret-number-tablature function:
591 (define-public (fret-number-tablature-format-banjo
592                 context string-number fret-number)
593   (make-vcenter-markup
594    (number->string (cond
595                     ((and (> fret-number 0) (= string-number 5))
596                      (+ fret-number 5))
597                     (else fret-number)))))
598
599 ;;  Tab note head staff position functions
600 ;;
601 ;;  Define where in the staff to display a given string.  Some forms of
602 ;;  tablature put the tab note heads in the spaces, rather than on the
603 ;;  lines
604
605 (define-public (tablature-position-on-lines context string-number)
606   (let* ((string-tunings (ly:context-property context 'stringTunings))
607          (string-count (length string-tunings))
608          (string-one-topmost (ly:context-property context 'stringOneTopmost))
609          (staff-line (- (* 2 string-number) string-count 1)))
610     (if string-one-topmost
611         (- staff-line)
612         staff-line)))
613
614 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
615 ;; bar numbers
616
617 (define-public ((every-nth-bar-number-visible n) barnum mp)
618   (= 0 (modulo barnum n)))
619
620 (define-public ((modulo-bar-number-visible n m) barnum mp)
621   (and (> barnum 1) (= m (modulo barnum n))))
622
623 (define-public ((set-bar-number-visibility n) tr)
624   (let ((bn (ly:context-property tr 'currentBarNumber)))
625     (ly:context-set-property! tr 'barNumberVisibility
626                               (modulo-bar-number-visible n (modulo bn n)))))
627
628 (define-public (first-bar-number-invisible barnum mp)
629   (> barnum 1))
630
631 (define-public (first-bar-number-invisible-save-broken-bars barnum mp)
632   (or (> barnum 1)
633       (> (ly:moment-main-numerator mp) 0)))
634
635 (define-public (first-bar-number-invisible-and-no-parenthesized-bar-numbers barnum mp)
636   (and (> barnum 1)
637        (= (ly:moment-main-numerator mp) 0)))
638
639 (define-public (robust-bar-number-function barnum measure-pos alt-number context)
640   (define (get-number-and-power an pow)
641     (if (<= an alt-number)
642         (get-number-and-power (+ an (expt 26 (1+ pow))) (1+ pow))
643         (cons (+ alt-number (- (expt 26 pow) an)) (1- pow))))
644   (define (make-letter so-far an pow)
645     (if (< pow 0)
646         so-far
647         (let ((pos (modulo (quotient an (expt 26 pow)) 26)))
648           (make-letter (string-append so-far
649                                       (substring "abcdefghijklmnopqrstuvwxyz"
650                                                  pos
651                                                  (1+ pos)))
652                        an
653                        (1- pow)))))
654   (let* ((number-and-power (get-number-and-power 0 0))
655          (begin-measure (= 0 (ly:moment-main-numerator measure-pos)))
656          (maybe-open-parenthesis (if begin-measure "" "("))
657          (maybe-close-parenthesis (if begin-measure "" ")")))
658     (markup (string-append maybe-open-parenthesis
659                            (number->string barnum)
660                            (make-letter ""
661                                         (car number-and-power)
662                                         (cdr number-and-power))
663                            maybe-close-parenthesis))))
664
665 (define-public (all-bar-numbers-visible barnum mp) #t)
666
667
668 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
669 ;; percent repeat counters
670
671 (define-public ((every-nth-repeat-count-visible n) count context)
672   (= 0 (modulo count n)))
673
674 (define-public (all-repeat-counts-visible count context) #t)
675
676 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
677 ;; make-engraver helper macro
678
679 (defmacro-public make-engraver forms
680   "Helper macro for creating Scheme engravers.
681
682 The usual form for an engraver is an association list (or alist)
683 mapping symbols to either anonymous functions or to another such
684 alist.
685
686 @code{make-engraver} accepts forms where the first element is either
687 an argument list starting with the respective symbol, followed by the
688 function body (comparable to the way @code{define} is used for
689 defining functions), or a single symbol followed by subordinate forms
690 in the same manner.  You can also just make an alist pair
691 literally (the @samp{car} is quoted automatically) as long as the
692 unevaluated @samp{cdr} is not a pair.  This is useful if you already
693 have defined your engraver functions separately.
694
695 Symbols mapping to a function would be @code{initialize},
696 @code{start-translation-timestep}, @code{process-music},
697 @code{process-acknowledged}, @code{stop-translation-timestep}, and
698 @code{finalize}.  Symbols mapping to another alist specified in the
699 same manner are @code{listeners} with the subordinate symbols being
700 event classes, and @code{acknowledgers} and @code{end-acknowledgers}
701 with the subordinate symbols being interfaces."
702   (let loop ((forms forms))
703     (if (cheap-list? forms)
704         `(list
705           ,@(map (lambda (form)
706                    (if (pair? (car form))
707                        `(cons ',(caar form) (lambda ,(cdar form) ,@(cdr form)))
708                        `(cons ',(car form) ,(loop (cdr form)))))
709                  forms))
710         forms)))