]> git.donarmstrong.com Git - lilypond.git/blob - scm/translation-functions.scm
Issue 2978: Simplify calculation of pitch-alist in determine-frets-and-strings
[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-octavation-markup oct style)
24   "The octavation sign formatting function.  @var{oct} is supposed to be
25 a string holding the octavation number, @var{style} determines the
26 way the octavation 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 (determine-frets-and-strings
286             notes
287             defined-strings
288             defined-fingers
289             minimum-fret
290             maximum-stretch
291             tuning)
292     "Determine the frets and strings used to play the notes in
293 @var{notes}, given @var{defined-strings} and @var{defined-fingers}
294 along with @var{minimum-fret}, @var{maximum-stretch}, and
295 @var{tuning}.  Returns a list of @code{(string fret finger) lists."
296
297
298     (define restrain-open-strings (ly:context-property context
299                                                        'restrainOpenStrings
300                                                        #f))
301     (define specified-frets '())
302     (define free-strings (iota (length tuning) 1))
303
304     (define (calc-fret pitch string tuning)
305       "Calculate the fret to play @var{pitch} on @var{string} with
306 @var{tuning}."
307       (* 2  (- (ly:pitch-tones pitch) (ly:pitch-tones (list-ref tuning (1- string))))))
308
309     (define (note-pitch note)
310       "Get the pitch (in semitones) from @var{note}."
311       (ly:event-property note 'pitch))
312
313     (define (note-finger ev)
314       "Get the fingering from @var{ev}.  Return @var{#f}
315 if no fingering is present."
316       (let* ((articulations (ly:event-property ev 'articulations))
317              (finger-found #f))
318         (map (lambda (art)
319                (let* ((num (ly:event-property art 'digit)))
320
321                  (if (and (ly:in-event-class? art 'fingering-event)
322                           (number? num)
323                           (> num 0))
324                    (set! finger-found num))))
325              articulations)
326         finger-found))
327
328     (define (string-number event)
329       "Get the string-number from @var{event}.  Return @var{#f}
330 if no string-number is present."
331       (let ((num (ly:event-property event 'string-number)))
332         (if (number? num)
333           num
334           #f)))
335
336     (define (delete-free-string string)
337       (if (number? string)
338         (set! free-strings
339           (delete string free-strings))))
340
341     (define (close-enough fret)
342       "Decide if @var{fret} is acceptable, given the already used frets."
343       (if (null? specified-frets)
344         #t
345         (reduce
346           (lambda (x y)
347             (and x y))
348           #t
349           (map (lambda (specced-fret)
350                  (or (eq? 0 specced-fret)
351                      (and (not restrain-open-strings)
352                      (eq? 0 fret))
353                      (>= maximum-stretch (abs (- fret specced-fret)))))
354                specified-frets))))
355
356     (define (string-qualifies string pitch)
357       "Can @var{pitch} be played on @var{string}, given already placed
358 notes?"
359       (let* ((fret (calc-fret pitch string tuning)))
360         (and (or (and (not restrain-open-strings)
361                       (eq? fret 0))
362                  (>= fret minimum-fret))
363              (integer? fret)
364              (close-enough fret))))
365
366     (define (open-string string pitch)
367       "Is @var{pitch} and open-string note on @var{string}, given
368 the current tuning?"
369       (let* ((fret (calc-fret pitch string tuning)))
370         (eq? fret 0)))
371
372     (define (set-fret! pitch-entry string finger)
373       (let ((this-fret (calc-fret (car pitch-entry)
374                                   string
375                                   tuning)))
376         (if (< this-fret 0)
377           (ly:warning (_ "Negative fret for pitch ~a on string ~a")
378                       (car pitch-entry) string)
379           (if (not (integer? this-fret))
380               (ly:warning (_ "Missing fret for pitch ~a on string ~a")
381                           (car pitch-entry) string)))
382         (delete-free-string string)
383         (set! specified-frets (cons this-fret specified-frets))
384         (list-set! string-fret-fingers
385                    (cdr pitch-entry)
386                    (list string this-fret finger))))
387
388     (define (kill-note! string-fret-fingers note-index)
389       (list-set! string-fret-fingers note-index (list #f #t)))
390
391     (define string-fret-fingers
392              (map (lambda (string finger)
393                     (if (null? finger)
394                         (list string #f)
395                         (list string #f finger)))
396                   defined-strings defined-fingers))
397
398     ;;; body of determine-frets-and-strings
399     (let* ((pitches (map note-pitch notes))
400            (pitch-alist (map cons pitches (iota (length pitches)))))
401
402       ;; handle notes with strings assigned and fingering of 0
403       (for-each
404         (lambda (pitch-entry string-fret-finger)
405           (let* ((string (list-ref string-fret-finger 0))
406                  (finger (if (eq? (length string-fret-finger) 3)
407                              (list-ref string-fret-finger 2)
408                              '()))
409                  (pitch (car pitch-entry))
410                  (digit (if (null? finger)
411                             #f
412                             finger)))
413             (if (or (not (null? string))
414                     (eq? digit 0))
415                 (if (eq? digit 0)
416                     ;; here we handle fingers of 0 -- open strings
417                     (let ((fit-string
418                             (find (lambda (string)
419                                     (open-string string pitch))
420                                   free-strings)))
421                       (if fit-string
422                           (set-fret! pitch-entry fit-string #f)
423                           (ly:warning (_ "No open string for pitch ~a")
424                                       pitch)))
425                     ;; here we handle assigned strings
426                     (let ((this-fret
427                             (calc-fret pitch string tuning))
428                           (handle-negative
429                             (ly:context-property context
430                                                  'handleNegativeFrets
431                                                  'recalculate)))
432                       (cond ((or (and (>= this-fret 0) (integer? this-fret))
433                                  (eq? handle-negative 'include))
434                              (set-fret! pitch-entry string finger))
435                             ((eq? handle-negative 'recalculate)
436                              (begin
437                                (ly:warning
438                                  (_ "Requested string for pitch requires negative fret: string ~a pitch ~a")
439                                  string
440                                  pitch)
441                                (ly:warning (_ "Ignoring string request and recalculating."))
442                                (list-set! string-fret-fingers
443                                           (cdr pitch-entry)
444                                           (if (null? finger)
445                                               (list '() #f)
446                                               (list '() #f finger)))))
447                             ((eq? handle-negative 'ignore)
448                              (begin
449                                (ly:warning
450                                  (_ "Requested string for pitch requires negative fret: string ~a pitch ~a")
451                                  string
452                                  pitch)
453                                (ly:warning (_ "Ignoring note in tablature."))
454                                (kill-note! string-fret-fingers
455                                            (cdr pitch-entry))))))))))
456         pitch-alist string-fret-fingers)
457     ;; handle notes without strings assigned -- sorted by pitch, so
458     ;; we need to use the alist to have the note number available
459     (for-each
460       (lambda (pitch-entry)
461         (let* ((string-fret-finger (list-ref string-fret-fingers
462                                              (cdr pitch-entry)))
463                (string (list-ref string-fret-finger 0))
464                (finger (if (eq? (length string-fret-finger) 3)
465                            (list-ref string-fret-finger 2)
466                            '()))
467                (pitch (car pitch-entry))
468                (fit-string
469                  (find (lambda (string)
470                          (string-qualifies string pitch))
471                        free-strings)))
472           (if (not (list-ref string-fret-finger 1))
473               (if fit-string
474                   (set-fret! pitch-entry fit-string finger)
475                   (begin
476                     (ly:warning (_ "No string for pitch ~a (given frets ~a)")
477                                 pitch
478                                 specified-frets)
479                     (kill-note! string-fret-fingers
480                                 (cdr pitch-entry)))))))
481       (sort pitch-alist (lambda (pitch-entry-a pitch-entry-b)
482                           (ly:pitch<? (car pitch-entry-b)
483                                       (car pitch-entry-a)))))
484     string-fret-fingers)) ;; end of determine-frets-and-strings
485
486   (define (get-predefined-fretboard predefined-fret-table tuning pitches)
487     "Search through @var{predefined-fret-table} looking for a predefined
488 fretboard with a key of @var{(tuning . pitches)}.  The search will check
489 both up and down an octave in order to accomodate transposition of the
490 chords.  Returns a placement-list."
491
492     (define (get-fretboard key)
493       (let ((hash-handle
494              (hash-get-handle predefined-fret-table key)))
495         (if hash-handle
496             (cdr hash-handle)  ; return table entry
497             '())))
498
499     ;; body of get-predefined-fretboard
500     (let ((test-fretboard (get-fretboard (cons tuning pitches))))
501       (if (not (null? test-fretboard))
502           test-fretboard
503           (let ((test-fretboard
504                  (get-fretboard
505                   (cons tuning (map (lambda (x) (shift-octave x 1)) pitches)))))
506             (if (not (null? test-fretboard))
507                 test-fretboard
508                 (get-fretboard
509                  (cons tuning (map (lambda (x) (shift-octave x -1))
510                                    pitches))))))))
511
512   ;; body of determine-frets
513   (let* ((predefined-fret-table
514           (ly:context-property context 'predefinedDiagramTable))
515          (tunings (ly:context-property context 'stringTunings))
516          (string-count (length tunings))
517          (grob (if (null? rest) '() (car rest)))
518          (pitches (map (lambda (x) (ly:event-property x 'pitch)) notes))
519          (defined-strings (map (lambda (x)
520                                  (if (null? x)
521                                      x
522                                      (ly:event-property x 'string-number)))
523                                (car specified-info)))
524          (defined-fingers (map (lambda (x)
525                                  (if (null? x)
526                                      x
527                                      (ly:event-property x 'digit)))
528                                (cadr specified-info)))
529          (default-strings (ly:context-property context 'defaultStrings '()))
530          (strings-used (if (and (zero? (entry-count defined-strings))
531                                 (not (zero? (entry-count default-strings))))
532                            default-strings
533                            defined-strings))
534          (predefined-fretboard
535           (if predefined-fret-table
536               (get-predefined-fretboard
537                predefined-fret-table
538                tunings
539                pitches)
540               '())))
541      (if (null? predefined-fretboard)
542          (let ((string-frets
543                 (determine-frets-and-strings
544                  notes
545                  strings-used
546                  defined-fingers
547                  (ly:context-property context 'minimumFret 0)
548                  (ly:context-property context 'maximumFretStretch 4)
549                  tunings)))
550             (if (null? grob)
551                 string-frets
552                 (create-fretboard
553                  context grob (string-frets->placement-list
554                                 (filter (lambda (entry)
555                                           (car entry))
556                                         string-frets)
557                                 string-count))))
558          (if (null? grob)
559              (placement-list->string-frets predefined-fretboard)
560              (create-fretboard context grob predefined-fretboard)))))
561
562
563
564 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
565 ;; tablature
566
567 ;; The TabNoteHead tablatureFormat callbacks.
568
569 ;; Calculate the fret from pitch and string number as letter
570 ;; The fret letter is taken from 'fretLabels if present
571 (define-public (fret-letter-tablature-format
572                 context string-number fret-number)
573  (let ((labels (ly:context-property context 'fretLabels)))
574   (make-vcenter-markup
575    (cond
576     ((= 0 (length labels))
577      (string (integer->char (+ fret-number (char->integer #\a)))))
578     ((and (<= 0 fret-number) (< fret-number (length labels)))
579      (list-ref labels fret-number))
580     (else
581      (ly:warning (_ "No label for fret ~a (on string ~a);
582 only ~a fret labels provided")
583                 fret-number string-number (length labels))
584      ".")))))
585
586 ;; Display the fret number as a number
587 (define-public (fret-number-tablature-format
588                 context string-number fret-number)
589   (make-vcenter-markup
590     (format #f "~a" fret-number)))
591
592 ;; The 5-string banjo has got a extra string, the fifth (duh), which
593 ;; starts at the fifth fret on the neck.  Frets on the fifth string
594 ;; are referred to relative to the other frets:
595 ;;   the "first fret" on the fifth string is really the sixth fret
596 ;;   on the banjo neck.
597 ;; We solve this by defining a new fret-number-tablature function:
598 (define-public (fret-number-tablature-format-banjo
599                 context string-number fret-number)
600  (make-vcenter-markup
601   (number->string (cond
602                    ((and (> fret-number 0) (= string-number 5))
603                     (+ fret-number 5))
604                    (else fret-number)))))
605
606 ;;  Tab note head staff position functions
607 ;;
608 ;;  Define where in the staff to display a given string.  Some forms of
609 ;;  tablature put the tab note heads in the spaces, rather than on the
610 ;;  lines
611
612 (define-public (tablature-position-on-lines context string-number)
613  (let* ((string-tunings (ly:context-property context 'stringTunings))
614         (string-count (length string-tunings))
615         (string-one-topmost (ly:context-property context 'stringOneTopmost))
616         (staff-line (- (* 2 string-number) string-count 1)))
617   (if string-one-topmost
618       (- staff-line)
619       staff-line)))
620
621 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
622 ;; bar numbers
623
624 (define-public ((every-nth-bar-number-visible n) barnum mp)
625   (= 0 (modulo barnum n)))
626
627 (define-public ((modulo-bar-number-visible n m) barnum mp)
628   (and (> barnum 1) (= m (modulo barnum n))))
629
630 (define-public ((set-bar-number-visibility n) tr)
631   (let ((bn (ly:context-property tr 'currentBarNumber)))
632     (ly:context-set-property! tr 'barNumberVisibility
633                               (modulo-bar-number-visible n (modulo bn n)))))
634
635 (define-public (first-bar-number-invisible barnum mp)
636   (> barnum 1))
637
638 (define-public (first-bar-number-invisible-save-broken-bars barnum mp)
639   (or (> barnum 1)
640       (> (ly:moment-main-numerator mp) 0)))
641
642 (define-public (first-bar-number-invisible-and-no-parenthesized-bar-numbers barnum mp)
643   (and (> barnum 1)
644        (= (ly:moment-main-numerator mp) 0)))
645
646 (define-public (robust-bar-number-function barnum measure-pos alt-number context)
647   (define (get-number-and-power an pow)
648     (if (<= an alt-number)
649         (get-number-and-power (+ an (expt 26 (1+ pow))) (1+ pow))
650         (cons (+ alt-number (- (expt 26 pow) an)) (1- pow))))
651   (define (make-letter so-far an pow)
652     (if (< pow 0)
653       so-far
654       (let ((pos (modulo (quotient an (expt 26 pow)) 26)))
655         (make-letter (string-append so-far
656                                     (substring "abcdefghijklmnopqrstuvwxyz"
657                                                pos
658                                                (1+ pos)))
659                    an
660                    (1- pow)))))
661   (let* ((number-and-power (get-number-and-power 0 0))
662          (begin-measure (= 0 (ly:moment-main-numerator measure-pos)))
663          (maybe-open-parenthesis (if begin-measure "" "("))
664          (maybe-close-parenthesis (if begin-measure "" ")")))
665     (markup (string-append maybe-open-parenthesis
666                            (number->string barnum)
667                            (make-letter ""
668                                         (car number-and-power)
669                                         (cdr number-and-power))
670                            maybe-close-parenthesis))))
671
672 (define-public (all-bar-numbers-visible barnum mp) #t)
673
674
675 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
676 ;; percent repeat counters
677
678 (define-public ((every-nth-repeat-count-visible n) count context)
679   (= 0 (modulo count n)))
680
681 (define-public (all-repeat-counts-visible count context) #t)
682
683 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
684 ;; make-engraver helper macro
685
686 (defmacro-public make-engraver forms
687   "Helper macro for creating Scheme engravers.
688
689 The usual form for an engraver is an association list (or alist)
690 mapping symbols to either anonymous functions or to another such
691 alist.
692
693 @code{make-engraver} accepts forms where the first element is either
694 an argument list starting with the respective symbol, followed by the
695 function body (comparable to the way @code{define} is used for
696 defining functions), or a single symbol followed by subordinate forms
697 in the same manner.  You can also just make an alist pair
698 literally (the @samp{car} is quoted automatically) as long as the
699 unevaluated @samp{cdr} is not a pair.  This is useful if you already
700 have defined your engraver functions separately.
701
702 Symbols mapping to a function would be @code{initialize},
703 @code{start-translation-timestep}, @code{process-music},
704 @code{process-acknowledged}, @code{stop-translation-timestep}, and
705 @code{finalize}.  Symbols mapping to another alist specified in the
706 same manner are @code{listeners} with the subordinate symbols being
707 event classes, and @code{acknowledgers} and @code{end-acknowledgers}
708 with the subordinate symbols being interfaces."
709   (let loop ((forms forms))
710     (if (cheap-list? forms)
711         `(list
712           ,@(map (lambda (form)
713                    (if (pair? (car form))
714                        `(cons ',(caar form) (lambda ,(cdar form) ,@(cdr form)))
715                        `(cons ',(car form) ,(loop (cdr form)))))
716                  forms))
717         forms)))