1 %%% includes.ily -- commands for including files from a project hierarchy
3 %%% Author: Nicolas Sceaux <nicolas.sceaux@free.fr>
5 %%% Directory hierarchy:
6 %%% Composer > Category > Opus > Piece
10 %%% Lully/Ballets/AmourMalade/AAouverture
12 %%% LilyPond should be invoked from the hierarchy root,
13 %%% or the hierarchy root should be in LilyPond include path.
18 %%% the composer identifier in the project herarchy (a string)
21 %%% the category identifier in the project herarchy (a string)
24 %%% the opus identifier in the project herarchy (a string)
27 %%% the piece identifier in the project herarchy (a string)
32 %%% (include-pathname name)
33 %%% Composer, category, opus and piece special variables being set,
34 %%% possibly to an empty string, return the complete pathname of
37 %%% (include-score parser name)
38 %%% Set the piece special variable to `name', then parse the
39 %%% following LilyPond code:
40 %%% \include "<complete piece pathname>/score.ily"
44 %%% Functions setting the current composer, category and opus:
46 %%% \setComposer "composer"
47 %%% define the current composer
49 %%% \setCategory "category"
50 %%% \setCategory "composer/category"
51 %%% define the current category, and possibly the current composer
54 %%% \setOpus "category/opus"
55 %%% \setOpus "composer/category/opus"
56 %%% define the current opus, and possibly the current composer and
59 %%% Functions for parsing a piece score:
61 %%% \includeScore "piece"
62 %%% set the current piece to `piece', and parse the file
63 %%% "<piece pathname>/score.ily"
65 %%% Functions parsing a file and returning its music:
68 %%% Return the music of the current piece "global.ily" file,
69 %%% parsing it if that has not been done yet.
71 %%% \includeNotes "name"
72 %%% == \notemode { \include "<piece pathname>/name.ily" }
74 %%% \includeLyrics "name"
75 %%% == \lyricmode { \include "<piece pathname>/name.ily" }
77 %%% \includeFigures "name"
78 %%% == \figuremode { \include "<piece pathname>/name.ily" }
87 #(use-modules (srfi srfi-39)
91 %% Define the *target* and *target-full* variables
92 #(define-public *target*
93 (make-parameter (cond ((symbol? (ly:get-option 'target)) (ly:get-option 'target))
94 ((symbol? (ly:get-option 'part)) (ly:get-option 'part))
95 ((eqv? #t (ly:get-option 'letter)) 'full-letter)
96 ((eqv? #t (ly:get-option 'urtext)) 'full-urtext)
97 ((eqv? #t (ly:get-option 'use-rehearsal-numbers)) 'full-rehearsal)
99 #(define-public *target-full* (make-parameter (not (symbol? (ly:get-option 'part)))))
101 %% The newBookPart command uses the *target* variable value to decide
102 %% whether it should actually start a new book part
104 #(define-music-function (parser location targets) (list?)
105 "To be used at toplevel, to start a new implicit bookpart, if
106 *target* is a member of `targets'. If there is some toplevel music or
107 text, add it to a bookpart, and add that bookpart to the list of
109 (if (and (or (null? targets)
110 (memq (*target*) targets)
111 (and (*target-full*) (memq 'full targets)))
112 (pair? (ly:parser-lookup parser 'toplevel-scores)))
114 (ly:parser-define! parser 'toplevel-bookparts
115 (cons (ly:make-book-part (ly:parser-lookup parser 'toplevel-scores))
116 (ly:parser-lookup parser 'toplevel-bookparts)))
117 (ly:parser-define! parser 'toplevel-scores (list))))
118 (make-music 'Music 'void #t))
120 #(define *composer* (make-parameter ""))
121 #(define *category* (make-parameter ""))
122 #(define *opus* (make-parameter ""))
123 #(define *piece* (make-parameter ""))
125 #(define-public (include-pathname name)
126 (let ((hierarchy (list (*composer*)
133 (if (string-null? dir)
135 (string-append dir "/")))
140 #(define*-public (include-score parser name #:optional label)
144 'page-label (string->symbol (or label name))))
145 (parameterize ((*piece* name))
146 (ly:parser-parse-string
147 (ly:parser-clone parser)
148 (format #f "\\include \"~a\""
149 (include-pathname "score")))))
154 #(define *opus-part-specs* (make-parameter #f)) % all part global specs
155 #(define *part* (make-parameter #f)) % The chosen part identifer
156 #(define *part-name* (make-parameter "")) % The chosen part name
157 #(define *piece-description* (make-parameter #f)) % the current piece description
158 #(define *note-filename* (make-parameter #f))
159 #(define *instrument-name* (make-parameter #f))
160 #(define *score-ragged* (make-parameter #f))
161 #(define *score-indent* (make-parameter #f))
162 #(define *score-extra-music* (make-parameter #f))
163 #(define *score-extra-music2* (make-parameter #f))
164 #(define *tag-global* (make-parameter #f))
165 #(define *tag-notes* (make-parameter #f))
166 #(define *figures* (make-parameter #f))
167 #(define *clef* (make-parameter #f))
169 #(define*-public (include-part-markup parser
176 'page-label (string->symbol (or label name))))
177 (add-text parser markp))
179 #(define*-public (include-part-score parser
187 'page-label (string->symbol (or label name))))
188 (parameterize ((*piece* name))
189 (ly:parser-parse-string
190 (ly:parser-clone parser)
191 (format #f "\\include \"~a\""
193 (string-append "templates/" score-filename ".ily")
194 (include-pathname score-filename))))))
196 #(define* (make-piece piece-spec
199 (score-template "score")
207 "Return an associative list defining a part piece, with the following keys:
208 - name the piece name.
209 - score the part piece filename (without directory, nor extension)
211 - from-template should the score filename be found in templates directory?
212 Is #t when #:score has been explicitely specified, #f otherwise.
213 - ragged the value of the layout ragged-last variable
215 - indent the value of the layout indent variable
216 Default: #f (which means that the globally defined indent is used)
217 - tag-global the tag to be used when including the 'global.ily' file:
218 \\keepWithTag #tag \\global
219 Default: #f (do not use a tag)
220 - tag-notes the tag to be used when including the note file:
221 Default: #f (do not use a tag)
222 - notes the note filename (without directory, nor extension)
223 Default: default-note-filename
224 - instrument the instrument name to be printed before the first staff
225 Default: #f (do not print instrument name)
227 `piece-spec' should be a list, which first-element is the piece name,
228 then consisting of alterning keywords and values, the keywords being any
229 combination from the following list:
230 #:score #:score-template #:ragged #:indent #:tag-global #:tag-notes
231 #:notes #:instrument #:music
232 #:music allows to include some extra music
233 The keyword arguments give default values to be used when non-specified in `piece-spec'."
234 (let ((score (or score score-template))
235 (from-templates (not score))
238 (tag-global tag-global)
239 (tag-notes tag-notes)
242 (instrument instrument)
245 (on-the-fly-markup #f))
246 (if clef (*clef* clef)) ;; hack: set *clef* for silence scores
247 (let parse-props ((props piece-spec))
248 (if (not (or (null? props) (null? (cdr props))))
251 ((#:notes) (set! notes (cadr props)))
252 ((#:clef) (set! clef (cadr props)))
253 ((#:ragged) (set! ragged (cadr props)))
254 ((#:indent) (set! indent (cadr props)))
255 ((#:tag-global) (set! tag-global (cadr props)))
256 ((#:tag-notes) (set! tag-notes (cadr props)))
258 (set! score (cadr props))
259 (set! from-templates #f))
261 (set! score (cadr props))
262 (set! from-templates #t))
263 ((#:on-the-fly-markup) (set! on-the-fly-markup (cadr props)))
264 ((#:instrument) (set! instrument (cadr props)))
265 ((#:music) (set! music (cadr props)))
266 ((#:music2) (set! music2 (cadr props))))
267 (parse-props (cddr props)))))
269 (from-templates . ,from-templates)
272 (tag-global . ,tag-global)
273 (tag-notes . ,tag-notes)
276 (instrument . ,instrument)
280 (on-the-fly-markup . ,on-the-fly-markup))))
283 #(define-music-function (parser location piece-specs) (list?)
284 "Define the part spec for a piece, by setting the *piece-description* special variable"
285 (define (get-part-opus-spec part)
286 (let ((spec (assoc part (*opus-part-specs*))))
287 (and spec (cdr spec))))
288 (define (get-fallbacks part)
289 (let ((part-opus-spec (get-part-opus-spec part)))
290 (or (and part-opus-spec (cadr part-opus-spec)) (list))))
291 (define (get-defaults part)
292 (let ((part-opus-spec (get-part-opus-spec part)))
293 (or (and part-opus-spec (caddr part-opus-spec)) (list))))
294 (define (get-default-clef part)
295 (define* (get-clef-helper #:key (clef #f) #:allow-other-keys)
297 (let ((part-opus-spec (get-part-opus-spec part)))
298 (and part-opus-spec (apply get-clef-helper (caddr part-opus-spec)))))
300 (define (get-part-piece parts piece-specs)
302 ;; default silent piece
303 (make-piece (list #:ragged #t
305 #:score-template "score-silence")
307 (let* ((part (caar parts))
308 (part-name (cadar parts))
309 (forced-clef (if (null? (cddar parts)) #f (caddar parts)))
310 (spec-result (assoc part piece-specs))
311 (spec (and spec-result (cdr spec-result))))
313 (let* ((default-spec (append (get-defaults part)
315 (list #:clef forced-clef)
317 (piece (apply make-piece spec default-spec)))
318 (if (and part-name (not (assoc-ref piece 'instrument)))
319 (assoc-set! piece 'instrument part-name))
321 (get-part-piece (cdr parts) piece-specs)))))
323 (let* ((part-opus-spec (get-part-opus-spec (*part*)))
324 (parts (append (list (list (*part*) #f))
325 (get-fallbacks (*part*))
326 (list (list 'silence #f (get-default-clef (*part*)))))))
327 (*piece-description* (get-part-piece parts piece-specs)))
328 (make-music 'Music 'void #t))
331 #(define-music-function (parser location opus-specs) (list?)
332 (let* ((silence-specs '(silence "" ()
333 (#:ragged #t #:notes "silence"
334 #:score-template "score-silence")))
335 (full-opus-specs (if (not (assoc 'silence opus-specs))
336 (cons silence-specs opus-specs)
338 (*opus-part-specs* full-opus-specs))
339 (let* ((name (ly:get-option 'part))
340 (spec (assoc name (*opus-part-specs*))))
343 (*part-name* (cadr spec)))
345 (ly:warning "No `~a' part defined for this opus" name))))
346 (make-music 'Music 'void #t))
353 #(define-music-function (parser this-location) ()
355 (let* ((global-symbol
356 (string->symbol (format "global~a~a" (*opus*) (*piece*))))
357 (global-music (ly:parser-lookup parser global-symbol)))
358 (if (not (ly:music? global-music))
359 (let* ((global-file (include-pathname "global")))
361 #{ \notemode { \staffStart \include $global-file } #})
362 (ly:parser-define! parser global-symbol global-music)))
363 (ly:music-deep-copy global-music)))
366 #(define-music-function (parser this-location pathname) (string?)
367 ;; use locations from the included file,
368 ;; and not from where \includeNotes is called
370 (let ((include-file (include-pathname pathname)))
371 #{ \notemode { \include $include-file } #}))
374 #(define-music-function (parser this-location pathname) (string?)
375 ;; use locations from the included file,
376 ;; and not from where \includeNotes is called
378 (let ((include-file (include-pathname pathname)))
379 #{ \lyricmode { \include $include-file } #}))
382 #(define-music-function (parser this-location pathname) (string?)
383 ;; use locations from the included file,
384 ;; and not from where \includeNotes is called
386 (let ((include-file (include-pathname pathname)))
387 #{ \new FiguredBass \figuremode { \include $include-file } #}))
390 #(define-music-function (parser location name) (string?)
392 (make-music 'Music 'void #t))
395 #(define-music-function (parser location name) (string?)
396 (let ((match (string-match "^(.*)/(.*)$" name)))
398 (begin ;; composer/category
399 (*composer* (match:substring match 1))
400 (*category* (match:substring match 2)))
403 (make-music 'Music 'void #t))
406 #(define-music-function (parser location name) (string?)
407 (let ((match (string-match "^(.*)/(.*)/(.*)$" name)))
409 (begin ;; composet/category/opus
410 (*composer* (match:substring match 1))
411 (*category* (match:substring match 2))
412 (*opus* (match:substring match 3)))
413 (let ((match (string-match "^(.*)/(.*)$" name)))
415 (begin ;; category/opus
416 (*category* (match:substring match 1))
417 (*opus* (match:substring match 2)))
420 (make-music 'Music 'void #t))
422 #(define (include-score-helper parser name label allow-page-turn)
423 ;;(format #t "Including score `~a'~%" name)
424 (if (eqv? #t (ly:get-option 'non-score-print))
425 (let ((label (string->symbol (or label name))))
428 (make-music 'EventChord
429 'elements (list (make-music
434 (add-toplevel-markup parser name))
435 (parameterize ((*piece* name))
436 ;;(format #t "Reading ~a~%" name)
438 (begin ;; a part score
439 ;; Include the parts.ily file, describing
440 ;; the parts defined for this piece.
441 ;; It should contain a call to \piecePartSpec
442 ;; which sets *piece-description*
443 (ly:parser-parse-string (ly:parser-clone parser)
444 (format #f "\\include \"~a\""
445 (include-pathname "parts")))
446 (let ((piece (*piece-description*)))
447 ;; special case: if on-the-fly-markup is set,
448 ;; just include the markup
449 (if (assoc-ref piece 'on-the-fly-markup)
450 (include-part-markup parser
452 (assoc-ref piece 'on-the-fly-markup)
454 (parameterize ((*score-ragged* (assoc-ref piece 'ragged))
455 (*note-filename* (assoc-ref piece 'notes))
456 (*instrument-name* (assoc-ref piece 'instrument))
457 (*score-indent* (assoc-ref piece 'indent))
458 (*tag-global* (assoc-ref piece 'tag-global))
459 (*tag-notes* (assoc-ref piece 'tag-notes))
460 (*figures* (assoc-ref piece 'figures))
461 (*clef* (or (assoc-ref piece 'clef) (*clef*) "treble"))
462 (*score-extra-music* (assoc-ref piece 'music))
463 (*score-extra-music2* (assoc-ref piece 'music2)))
464 (include-part-score parser
466 (assoc-ref piece 'score)
467 (assoc-ref piece 'from-templates)
470 (add-allow-page-turn parser)))
472 (include-score parser name label))))
473 (make-music 'Music 'void #t))
476 #(define-music-function (parser location name) (string?)
477 (include-score-helper parser name #f #t))
480 #(define-music-function (parser location condition name)
483 (include-score-helper parser name #f #t)
484 (make-music 'Music 'void #t)))
486 includeScoreNoPageTurn =
487 #(define-music-function (parser location name) (string?)
488 (include-score-helper parser name #f #f))
491 #(define-music-function (parser location name label) (string? string?)
492 (include-score-helper parser name label #t))
495 #(define-music-function (parser location condition name label)
496 (boolean? string? string?)
498 (include-score-helper parser name label #t)
499 (make-music 'Music 'void #t)))
504 #(define (toplevel-score-handler parser score)
505 (cond ((ly:parser-lookup parser '$current-bookpart)
506 ((ly:parser-lookup parser 'bookpart-score-handler)
507 (ly:parser-lookup parser '$current-bookpart) score))
508 ((ly:parser-lookup parser '$current-book)
509 ((ly:parser-lookup parser 'book-score-handler)
510 (ly:parser-lookup parser '$current-book) score))
512 (collect-scores-for-book parser score))))