]> git.donarmstrong.com Git - lilypond.git/blob - scm/translation-functions.scm
67653bc49a8da4709cd2ad5219c77b43eeca9b4c
[lilypond.git] / scm / translation-functions.scm
1 ;;;; This file is part of LilyPond, the GNU music typesetter.
2 ;;;;
3 ;;;; (c) 1998--2010 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 ;; metronome marks
22
23 (define-public (format-metronome-markup text dur count context)
24   (let* ((hide-note (eq? #t (ly:context-property context 'tempoHideNote))))
25     (metronome-markup text dur count hide-note)))
26
27 (define-public (metronome-markup text dur count hide-note)
28   (let* ((note-mark (if (and (not hide-note) (ly:duration? dur))
29                         (make-smaller-markup
30                          (make-note-by-number-markup (ly:duration-log dur)
31                                                      (ly:duration-dot-count dur)
32                                                      1))
33                         #f))
34          (count-markup (cond ((number? count)
35                               (if (> count 0)
36                                   (make-simple-markup (number->string count))
37                                   #f))
38                              ((pair? count)
39                               (make-concat-markup
40                                (list
41                                 (make-simple-markup (number->string (car count)))
42                                 (make-simple-markup " ")
43                                 (make-simple-markup "–")
44                                 (make-simple-markup " ")
45                                 (make-simple-markup (number->string (cdr count))))))
46                              (else #f)))
47          (note-markup (if (and (not hide-note) count-markup)
48                           (make-concat-markup
49                            (list
50                             (make-general-align-markup Y DOWN note-mark)
51                             (make-simple-markup " ")
52                             (make-simple-markup "=")
53                             (make-simple-markup " ")
54                             count-markup))
55                           #f))
56          (text-markup (if (not (null? text))
57                           (make-bold-markup text)
58                           #f)))
59     (if text-markup
60         (if (and note-markup (not hide-note))
61             (make-line-markup (list text-markup
62                                     (make-concat-markup
63                                      (list (make-simple-markup "(")
64                                            note-markup
65                                            (make-simple-markup ")")))))
66             (make-line-markup (list text-markup)))
67         (if note-markup
68             (make-line-markup (list note-markup))
69             (make-null-markup)))))
70
71 (define-public (format-mark-alphabet mark context)
72   (make-bold-markup (make-markalphabet-markup (1- mark))))
73
74 (define-public (format-mark-box-alphabet mark context)
75   (make-bold-markup (make-box-markup (make-markalphabet-markup (1- mark)))))
76
77 (define-public (format-mark-circle-alphabet mark context)
78   (make-bold-markup (make-circle-markup (make-markalphabet-markup (1- mark)))))
79
80 (define-public (format-mark-letters mark context)
81   (make-bold-markup (make-markletter-markup (1- mark))))
82
83 (define-public (format-mark-numbers mark context)
84   (make-bold-markup (number->string mark)))
85
86 (define-public (format-mark-barnumbers mark context)
87   (make-bold-markup (number->string (ly:context-property context
88                                                          'currentBarNumber))))
89
90 (define-public (format-mark-box-letters mark context)
91   (make-bold-markup (make-box-markup (make-markletter-markup (1- mark)))))
92
93 (define-public (format-mark-circle-letters mark context)
94   (make-bold-markup (make-circle-markup (make-markletter-markup (1- mark)))))
95
96 (define-public (format-mark-box-numbers mark context)
97   (make-bold-markup (make-box-markup (number->string mark))))
98
99 (define-public (format-mark-circle-numbers mark context)
100   (make-bold-markup (make-circle-markup (number->string mark))))
101
102 (define-public (format-mark-box-barnumbers mark context)
103   (make-bold-markup (make-box-markup
104                      (number->string (ly:context-property context
105                                                           'currentBarNumber)))))
106
107 (define-public (format-mark-circle-barnumbers mark context)
108   (make-bold-markup (make-circle-markup
109                      (number->string (ly:context-property context
110                                                           'currentBarNumber)))))
111
112
113 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
114 ;; Bass figures.
115
116 (define-public (format-bass-figure figure event context)
117   (let* ((fig (ly:event-property event 'figure))
118          (fig-markup (if (number? figure)
119
120                          ;; this is not very elegant, but center-aligning
121                          ;; all digits is problematic with other markups,
122                          ;; and shows problems in the (lack of) overshoot
123                          ;; of feta-alphabet glyphs.
124                          ((if (<= 10 figure)
125                               (lambda (y) (make-translate-scaled-markup
126                                            (cons -0.7 0) y))
127                               identity)
128
129                           (cond
130                            ((eq? #t (ly:event-property event 'diminished))
131                             (markup #:slashed-digit figure))
132                            ((eq? #t (ly:event-property event 'augmented-slash))
133                             (markup #:backslashed-digit figure))
134                            (else (markup #:number (number->string figure 10)))))
135                          #f))
136
137          (alt (ly:event-property event 'alteration))
138          (alt-markup
139           (if (number? alt)
140               (markup
141                #:general-align Y DOWN #:fontsize
142                (if (not (= alt DOUBLE-SHARP))
143                    -2 2)
144                (alteration->text-accidental-markup alt))
145               #f))
146
147          (plus-markup (if (eq? #t (ly:event-property event 'augmented))
148                           (markup #:number "+")
149                           #f))
150
151          (alt-dir (ly:context-property context 'figuredBassAlterationDirection))
152          (plus-dir (ly:context-property context 'figuredBassPlusDirection)))
153
154     (if (and (not fig-markup) alt-markup)
155         (begin
156           (set! fig-markup (markup #:left-align #:pad-around 0.3 alt-markup))
157           (set! alt-markup #f)))
158
159
160     ;; hmm, how to get figures centered between note, and
161     ;; lone accidentals too?
162
163     ;;    (if (markup? fig-markup)
164     ;;  (set!
165     ;;   fig-markup (markup #:translate (cons 1.0 0)
166     ;;                      #:center-align fig-markup)))
167
168     (if alt-markup
169         (set! fig-markup
170               (markup #:put-adjacent
171                       X (if (number? alt-dir)
172                             alt-dir
173                             LEFT)
174                       fig-markup
175                       #:pad-x 0.2 alt-markup)))
176
177     (if plus-markup
178         (set! fig-markup
179               (if fig-markup
180                   (markup #:put-adjacent
181                           X (if (number? plus-dir)
182                                 plus-dir
183                                 LEFT)
184                           fig-markup
185                           #:pad-x 0.2 plus-markup)
186                   plus-markup)))
187
188     (if (markup? fig-markup)
189         (markup #:fontsize -2 fig-markup)
190         empty-markup)))
191
192
193 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
194 ;; fret diagrams
195
196 (define (create-fretboard context grob placement-list)
197   "Convert @var{placement-list} into a fretboard @var{grob}."
198
199   (let* ((tunings (ly:context-property context 'stringTunings))
200          (my-string-count (length tunings))
201          (details (ly:grob-property grob 'fret-diagram-details)))
202
203     ;; Add string-count from string-tunings to fret-diagram-details.
204     (set! (ly:grob-property grob 'fret-diagram-details)
205             (acons 'string-count my-string-count details))
206     ;; Create the dot-placement list for the grob
207     (set! (ly:grob-property grob 'dot-placement-list) placement-list)))
208
209 (define-public
210   (determine-frets context notes defined-strings . rest)
211   "Determine string numbers and frets for playing @var{notes}
212 as a chord, given specified string numbers @var{defined-strings}.
213 Will look for predefined fretboards if @code{predefinedFretboardTable}
214 is not @code {#f}.  If @var{rest} is present, it contains the
215 FretBoard grob, and a fretboard will be
216 created.  Otherwise, a list of (string fret finger) lists will
217 be returned)."
218
219   ;;  helper functions
220
221   (define (string-frets->placement-list string-frets string-count)
222     "Convert @var{string-frets} to @code{fret-diagram-verbose}
223 dot placement entries."
224     (let* ((placements (list->vector
225                         (map (lambda (x) (list 'mute  (1+ x)))
226                              (iota string-count)))))
227
228       (for-each (lambda (sf)
229                   (let* ((string (car sf))
230                          (fret (cadr sf))
231                          (finger (caddr sf)))
232                     (vector-set!
233                      placements (1- string)
234                      (if (= 0 fret)
235                          (list 'open string)
236                          (if finger
237                              (list 'place-fret string fret finger)
238                              (list 'place-fret string fret))))))
239                 string-frets)
240       (vector->list placements)))
241
242   (define (placement-list->string-frets placement-list)
243     "Convert @var{placement-list} to string-fret list."
244     (map (lambda (x) (if (eq? (car x) 'place-fret)
245                          (cdr x)
246                          (list (cadr x) 0)))
247          (filter (lambda (l) (or (eq? (car l) 'place-fret)
248                                  (eq? (car l) 'open)))
249                  placement-list)))
250
251
252   (define (get-predefined-fretboard predefined-fret-table tuning pitches)
253     "Search through @var{predefined-fret-table} looking for a predefined
254 fretboard with a key of @var{(tuning . pitches)}.  The search will check
255 both up and down an octave in order to accomodate transposition of the
256 chords.  Returns a placement-list."
257
258     (define (get-fretboard key)
259       (let ((hash-handle
260              (hash-get-handle predefined-fret-table key)))
261         (if hash-handle
262             (cdr hash-handle)  ; return table entry
263             '())))
264
265     ;; body of get-predefined-fretboard
266     (let ((test-fretboard (get-fretboard (cons tuning pitches))))
267       (if (not (null? test-fretboard))
268           test-fretboard
269           (let ((test-fretboard
270                  (get-fretboard
271                   (cons tuning (map (lambda (x) (shift-octave x 1)) pitches)))))
272             (if (not (null? test-fretboard))
273                 test-fretboard
274                 (get-fretboard
275                  (cons tuning (map (lambda (x) (shift-octave x -1))
276                                    pitches))))))))
277
278   ;; body of determine-frets
279   (let* ((predefined-fret-table
280           (ly:context-property context 'predefinedDiagramTable))
281          (tunings (ly:context-property context 'stringTunings))
282          (string-count (length tunings))
283          (grob (if (null? rest) '() (car rest)))
284          (pitches (map (lambda (x) (ly:event-property x 'pitch)) notes))
285          (predefined-fretboard
286           (if predefined-fret-table
287               (get-predefined-fretboard
288                predefined-fret-table
289                tunings
290                pitches)
291               '())))
292
293      (if (null? predefined-fretboard)
294          (let ((string-frets
295                 (determine-frets-and-strings
296                  notes
297                  defined-strings
298                  (ly:context-property context 'minimumFret 0)
299                  (ly:context-property context 'maximumFretStretch 4)
300                  tunings)))
301             (if (null? grob)
302                 string-frets
303                 (create-fretboard
304                  context grob (string-frets->placement-list
305                                 string-frets string-count))))
306          (if (null? grob)
307              (placement-list->string-frets predefined-fretboard)
308              (create-fretboard context grob predefined-fretboard)))))
309
310
311 (define (determine-frets-and-strings
312           notes defined-strings minimum-fret maximum-stretch tuning)
313
314   (define (calc-fret pitch string tuning)
315     (- (ly:pitch-semitones pitch) (list-ref tuning (1- string))))
316
317   (define (note-pitch a)
318     (ly:event-property a 'pitch))
319
320   (define (note-pitch>? a b)
321     (ly:pitch<? (note-pitch b)
322                 (note-pitch a)))
323
324   (define (note-finger ev)
325     (let* ((articulations (ly:event-property ev 'articulations))
326            (finger-found #f))
327
328       (map (lambda (art)
329              (let* ((num (ly:event-property art 'digit)))
330
331                (if (and (eq? 'fingering-event (ly:event-property art 'class))
332                         (number? num))
333                    (set! finger-found num))))
334            articulations)
335
336       finger-found))
337
338   (define (string-number event)
339     (let ((num (ly:event-property event 'string-number)))
340       (if (number? num)
341           num
342           #f)))
343
344
345   (define (delete-free-string string)
346     (if (number? string)
347         (set! free-strings
348               (delete string free-strings))))
349
350   (define free-strings '())
351   (define unassigned-notes '())
352   (define specified-frets '())
353
354   (define (close-enough fret)
355     (if (null? specified-frets)
356         #t
357         (reduce
358           (lambda (x y)
359             (and x y))
360           #t
361           (map (lambda (specced-fret)
362                  (> maximum-stretch (abs (- fret specced-fret))))
363                specified-frets))))
364
365   (define (string-qualifies string pitch)
366     (let* ((fret (calc-fret pitch string tuning)))
367       (and (>= fret minimum-fret)
368            (close-enough fret))))
369
370   (define string-fret-fingering-tuples '())
371
372   (define (set-fret note string)
373     (let ((this-fret (calc-fret (ly:event-property note 'pitch)
374                                 string
375                                 tuning)))
376        (set! string-fret-fingering-tuples
377              (cons (list string
378                          this-fret
379                          (note-finger note))
380                    string-fret-fingering-tuples))
381        (delete-free-string string)
382        (set! specified-frets (cons this-fret specified-frets))))
383
384   ;;; body of determine-frets-and-strings
385   (set! free-strings (map 1+ (iota (length tuning))))
386
387   ;; get defined-strings same length as notes
388   (while (< (length defined-strings) (length notes))
389          (set! defined-strings (append defined-strings '(()))))
390
391   ;; handle notes with strings assigned
392   (for-each
393     (lambda (note string)
394       (if (null? string)
395           (set! unassigned-notes (cons note unassigned-notes))
396           (let ((this-string (string-number string)))
397             (delete-free-string this-string)
398             (set-fret note this-string))))
399     notes defined-strings)
400
401   ;; handle notes without strings assigned
402   (for-each
403    (lambda (note)
404      (let* ((fit-string
405               (find (lambda (string)
406                       (string-qualifies string (note-pitch note)))
407                     free-strings)))
408         (if fit-string
409             (set-fret note fit-string)
410             (ly:warning "No string for pitch ~a (given frets ~a)"
411                         (note-pitch note)
412                         specified-frets))))
413    (sort unassigned-notes note-pitch>?))
414
415    string-fret-fingering-tuples)
416
417 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
418 ;; tablature
419
420 ;; The TabNoteHead tablatureFormat callbacks.
421
422 ;; Calculate the fret from pitch and string number as letter
423 ;; The fret letter is taken from 'fretLabels if present
424 (define-public (fret-letter-tablature-format
425                 context string-number fret-number)
426  (let ((labels (ly:context-property context 'fretLabels)))
427   (make-vcenter-markup
428    (cond
429     ((= 0 (length labels))
430      (string (integer->char (+ fret-number (char->integer #\a)))))
431     ((and (<= 0 fret-number) (< fret-number (length labels)))
432      (list-ref labels fret-number))
433     (else
434      (ly:warning "No label for fret ~a (on string ~a);
435 only ~a fret labels provided"
436                  fret-number string-number (length labels))
437        ".")))))
438
439 ;; Display the fret number as a number
440 (define-public (fret-number-tablature-format
441                 context string-number fret-number)
442   (make-vcenter-markup
443     (format "~a" fret-number)))
444
445 ;; The 5-string banjo has got a extra string, the fifth (duh), which
446 ;; starts at the fifth fret on the neck.  Frets on the fifth string
447 ;; are referred to relative to the other frets:
448 ;;   the "first fret" on the fifth string is really the sixth fret
449 ;;   on the banjo neck.
450 ;; We solve this by defining a new fret-number-tablature function:
451 (define-public (fret-number-tablature-format-banjo
452                 context string-number fret-number)
453  (make-vcenter-markup
454   (number->string (cond
455                    ((and (> fret-number 0) (= string-number 5))
456                     (+ fret-number 5))
457                    (else fret-number)))))
458
459 ;;  Tab note head staff position functions
460 ;;
461 ;;  Define where in the staff to display a given string.  Some forms of
462 ;;  tablature put the tab note heads in the spaces, rather than on the
463 ;;  lines
464
465 (define-public (tablature-position-on-lines context string-number)
466  (let* ((string-tunings (ly:context-property context 'stringTunings))
467         (string-count (length string-tunings))
468         (string-one-topmost (ly:context-property context 'stringOneTopmost))
469         (staff-line (- (* 2 string-number) string-count 1)))
470   (if string-one-topmost
471       (- staff-line)
472       staff-line)))
473
474 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
475 ;; bar numbers
476
477 (define-public ((every-nth-bar-number-visible n) barnum)
478   (= 0 (modulo barnum n)))
479
480 (define-public ((modulo-bar-number-visible n m) barnum)
481   (and (> barnum 1) (= m (modulo barnum n))))
482
483 (define-public ((set-bar-number-visibility n) tr)
484   (let ((bn (ly:context-property tr 'currentBarNumber)))
485     (ly:context-set-property! tr 'barNumberVisibility
486                               (modulo-bar-number-visible n (modulo bn n)))))
487
488 (define-public (first-bar-number-invisible barnum) (> barnum 1))
489
490 (define-public (all-bar-numbers-visible barnum) #t)
491
492
493 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
494 ;; percent repeat counters
495
496 (define-public ((every-nth-repeat-count-visible n) count context)
497   (= 0 (modulo count n)))
498
499 (define-public (all-repeat-counts-visible count context) #t)