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 *system-count* (make-parameter #f))
162 #(define *score-indent* (make-parameter #f))
163 #(define *score-extra-music* (make-parameter #f))
164 #(define *score-extra-music2* (make-parameter #f))
165 #(define *tag-global* (make-parameter #f))
166 #(define *tag-notes* (make-parameter #f))
167 #(define *figures* (make-parameter #f))
168 #(define *clef* (make-parameter #f))
170 #(define*-public (include-part-markup parser
177 'page-label (string->symbol (or label name))))
178 (add-text parser markp))
180 #(define*-public (include-part-music parser
187 'page-label (string->symbol (or label name))))
188 (add-music parser music))
190 #(define*-public (include-part-score parser
198 'page-label (string->symbol (or label name))))
199 (parameterize ((*piece* name))
200 (ly:parser-parse-string
201 (ly:parser-clone parser)
202 (format #f "\\include \"~a\""
204 (string-append "templates/" score-filename ".ily")
205 (include-pathname score-filename))))))
207 #(define* (make-piece piece-spec
210 (score-template "score")
219 "Return an associative list defining a part piece, with the following keys:
220 - name the piece name.
221 - score the part piece filename (without directory, nor extension)
223 - from-template should the score filename be found in templates directory?
224 Is #t when #:score has been explicitely specified, #f otherwise.
225 - ragged the value of the ragged-last layout variable
227 - system-count the value of the system-count layout variable
229 - indent the value of the indent layout variable
230 Default: #f (which means that the globally defined indent is used)
231 - tag-global the tag to be used when including the 'global.ily' file:
232 \\keepWithTag #tag \\global
233 Default: #f (do not use a tag)
234 - tag-notes the tag to be used when including the note file:
235 Default: #f (do not use a tag)
236 - notes the note filename (without directory, nor extension)
237 Default: default-note-filename
238 - instrument the instrument name to be printed before the first staff
239 Default: #f (do not print instrument name)
241 `piece-spec' should be a list, which first-element is the piece name,
242 then consisting of alterning keywords and values, the keywords being any
243 combination from the following list:
244 #:score #:score-template #:ragged #:system-count #:indent #:tag-global
245 #:tag-notes #:notes #:instrument #:music
246 #:music allows to include some extra music
247 The keyword arguments give default values to be used when non-specified in `piece-spec'."
248 (let ((score (or score score-template))
249 (from-templates (not score))
251 (system-count system-count)
253 (tag-global tag-global)
254 (tag-notes tag-notes)
257 (instrument instrument)
260 (on-the-fly-music #f)
261 (on-the-fly-markup #f))
262 (if clef (*clef* clef)) ;; hack: set *clef* for silence scores
263 (let parse-props ((props piece-spec))
264 (if (not (or (null? props) (null? (cdr props))))
267 ((#:notes) (set! notes (cadr props)))
268 ((#:clef) (set! clef (cadr props)))
269 ((#:ragged) (set! ragged (cadr props)))
270 ((#:system-count) (set! system-count (cadr props)))
271 ((#:indent) (set! indent (cadr props)))
272 ((#:tag-global) (set! tag-global (cadr props)))
273 ((#:tag-notes) (set! tag-notes (cadr props)))
275 (set! score (cadr props))
276 (set! from-templates #f))
278 (set! score (cadr props))
279 (set! from-templates #t))
280 ((#:on-the-fly-music) (set! on-the-fly-music (cadr props)))
281 ((#:on-the-fly-markup) (set! on-the-fly-markup (cadr props)))
282 ((#:instrument) (set! instrument (cadr props)))
283 ((#:music) (set! music (cadr props)))
284 ((#:music2) (set! music2 (cadr props))))
285 (parse-props (cddr props)))))
287 (from-templates . ,from-templates)
289 (system-count . ,system-count)
291 (tag-global . ,tag-global)
292 (tag-notes . ,tag-notes)
295 (instrument . ,instrument)
299 (on-the-fly-markup . ,on-the-fly-markup)
300 (on-the-fly-music . ,on-the-fly-music))))
303 #(define-music-function (parser location piece-specs) (list?)
304 "Define the part spec for a piece, by setting the *piece-description* special variable"
305 (define (get-part-opus-spec part)
306 (let ((spec (assoc part (*opus-part-specs*))))
307 (and spec (cdr spec))))
308 (define (get-fallbacks part)
309 (let ((part-opus-spec (get-part-opus-spec part)))
310 (or (and part-opus-spec (cadr part-opus-spec)) (list))))
311 (define (get-defaults part)
312 (let ((part-opus-spec (get-part-opus-spec part)))
313 (or (and part-opus-spec (caddr part-opus-spec)) (list))))
314 (define (get-default-clef part)
315 (define* (get-clef-helper #:key (clef #f) #:allow-other-keys)
317 (let ((part-opus-spec (get-part-opus-spec part)))
318 (and part-opus-spec (apply get-clef-helper (caddr part-opus-spec)))))
320 (define (get-part-piece parts piece-specs)
322 ;; default silent piece
323 (make-piece (list #:ragged #t
325 #:score-template "score-silence")
327 (let* ((part (caar parts))
328 (part-name (cadar parts))
329 (forced-clef (if (null? (cddar parts)) #f (caddar parts)))
330 (spec-result (assoc part piece-specs))
331 (spec (and spec-result (cdr spec-result))))
333 (let* ((default-spec (append (get-defaults part)
335 (list #:clef forced-clef)
337 (piece (apply make-piece spec default-spec)))
338 (if (and part-name (not (assoc-ref piece 'instrument)))
339 (assoc-set! piece 'instrument part-name))
341 (get-part-piece (cdr parts) piece-specs)))))
343 (let* ((part-opus-spec (get-part-opus-spec (*part*)))
344 (parts (append (list (list (*part*) #f))
345 (get-fallbacks (*part*))
346 (list (list 'silence #f (get-default-clef (*part*)))))))
347 (*piece-description* (get-part-piece parts piece-specs)))
348 (make-music 'Music 'void #t))
351 #(define-music-function (parser location opus-specs) (list?)
352 (let* ((silence-specs '(silence "" ()
353 (#:ragged #t #:notes "silence"
354 #:score-template "score-silence")))
355 (full-opus-specs (if (not (assoc 'silence opus-specs))
356 (cons silence-specs opus-specs)
358 (*opus-part-specs* full-opus-specs))
359 (let* ((name (ly:get-option 'part))
360 (spec (assoc name (*opus-part-specs*))))
363 (*part-name* (cadr spec)))
365 (ly:warning "No `~a' part defined for this opus" name))))
366 (make-music 'Music 'void #t))
373 #(define-music-function (parser this-location) ()
375 (let* ((global-symbol
376 (string->symbol (format "global~a~a" (*opus*) (*piece*))))
377 (global-music (ly:parser-lookup parser global-symbol)))
378 (if (not (ly:music? global-music))
379 (let* ((global-file (include-pathname "global")))
381 #{ \notemode { \staffStart \include $global-file } #})
382 (ly:parser-define! parser global-symbol global-music)))
383 (ly:music-deep-copy global-music)))
386 #(define-music-function (parser this-location pathname) (string?)
387 ;; use locations from the included file,
388 ;; and not from where \includeNotes is called
390 (let ((include-file (include-pathname pathname)))
391 #{ \notemode { \include $include-file } #}))
394 #(define-music-function (parser this-location pathname) (string?)
395 ;; use locations from the included file,
396 ;; and not from where \includeNotes is called
398 (let ((include-file (include-pathname pathname)))
399 #{ \lyricmode { \include $include-file } #}))
402 #(define-music-function (parser this-location pathname) (string?)
403 ;; use locations from the included file,
404 ;; and not from where \includeNotes is called
406 (let ((include-file (include-pathname pathname)))
407 #{ \new FiguredBass \figuremode { \include $include-file } #}))
410 #(define-music-function (parser location name) (string?)
412 (make-music 'Music 'void #t))
415 #(define-music-function (parser location name) (string?)
416 (let ((match (string-match "^(.*)/(.*)$" name)))
418 (begin ;; composer/category
419 (*composer* (match:substring match 1))
420 (*category* (match:substring match 2)))
423 (make-music 'Music 'void #t))
426 #(define-music-function (parser location name) (string?)
427 (let ((match (string-match "^(.*)/(.*)/(.*)$" name)))
429 (begin ;; composet/category/opus
430 (*composer* (match:substring match 1))
431 (*category* (match:substring match 2))
432 (*opus* (match:substring match 3)))
433 (let ((match (string-match "^(.*)/(.*)$" name)))
435 (begin ;; category/opus
436 (*category* (match:substring match 1))
437 (*opus* (match:substring match 2)))
440 (make-music 'Music 'void #t))
442 #(define (include-score-helper parser name label allow-page-turn)
443 ;;(format #t "Including score `~a'~%" name)
444 (if (eqv? #t (ly:get-option 'non-score-print))
445 (let ((label (string->symbol (or label name))))
448 (make-music 'EventChord
449 'elements (list (make-music
454 (add-toplevel-markup parser name))
455 (parameterize ((*piece* name))
456 ;;(format #t "Reading ~a~%" name)
458 (begin ;; a part score
459 ;; Include the parts.ily file, describing
460 ;; the parts defined for this piece.
461 ;; It should contain a call to \piecePartSpec
462 ;; which sets *piece-description*
463 (ly:parser-parse-string (ly:parser-clone parser)
464 (format #f "\\include \"~a\""
465 (include-pathname "parts")))
466 (let ((piece (*piece-description*)))
467 ;; special cases: if on-the-fly-markup or
468 ;; on-the-fly-music are set,
469 ;; just include the markup/music
470 (cond ((assoc-ref piece 'on-the-fly-markup)
471 (include-part-markup parser
473 (assoc-ref piece 'on-the-fly-markup)
475 ((assoc-ref piece 'on-the-fly-music)
476 (include-part-music parser
478 (assoc-ref piece 'on-the-fly-music)
481 (parameterize ((*score-ragged* (assoc-ref piece 'ragged))
482 (*system-count* (assoc-ref piece 'system-count))
483 (*note-filename* (assoc-ref piece 'notes))
484 (*instrument-name* (assoc-ref piece 'instrument))
485 (*score-indent* (assoc-ref piece 'indent))
486 (*tag-global* (assoc-ref piece 'tag-global))
487 (*tag-notes* (assoc-ref piece 'tag-notes))
488 (*figures* (assoc-ref piece 'figures))
489 (*clef* (or (assoc-ref piece 'clef) (*clef*) "treble"))
490 (*score-extra-music* (assoc-ref piece 'music))
491 (*score-extra-music2* (assoc-ref piece 'music2)))
492 (include-part-score parser
494 (assoc-ref piece 'score)
495 (assoc-ref piece 'from-templates)
498 (add-allow-page-turn parser)))
500 (include-score parser name label))))
501 (make-music 'Music 'void #t))
504 #(define-music-function (parser location name) (string?)
505 (include-score-helper parser name #f #t))
508 #(define-music-function (parser location condition name)
511 (include-score-helper parser name #f #t)
512 (make-music 'Music 'void #t)))
514 includeScoreNoPageTurn =
515 #(define-music-function (parser location name) (string?)
516 (include-score-helper parser name #f #f))
519 #(define-music-function (parser location name label) (string? string?)
520 (include-score-helper parser name label #t))
523 #(define-music-function (parser location condition name label)
524 (boolean? string? string?)
526 (include-score-helper parser name label #t)
527 (make-music 'Music 'void #t)))
532 #(define (toplevel-score-handler parser score)
533 (cond ((ly:parser-lookup parser '$current-bookpart)
534 ((ly:parser-lookup parser 'bookpart-score-handler)
535 (ly:parser-lookup parser '$current-bookpart) score))
536 ((ly:parser-lookup parser '$current-book)
537 ((ly:parser-lookup parser 'book-score-handler)
538 (ly:parser-lookup parser '$current-book) score))
540 (collect-scores-for-book parser score))))