158 lines
6.7 KiB
LilyPond
158 lines
6.7 KiB
LilyPond
%% Mehrere Lieder in einer Liederdatei (z.B. zwei Lieder auf einer Seite)
|
|
%%
|
|
%% Das erste Lied ist das Hauptlied (HEADER), alle weiteren werden als
|
|
%% eingebettete Lieder angemeldet. Eingebettete Lieder bekommen eigene
|
|
%% Eintraege im Inhaltsverzeichnis, in den Kategorie-, Autoren- und
|
|
%% Kapitelverzeichnissen, im PDF-Inhaltsverzeichnis, in der toc.csv und
|
|
%% eine eigene Liednummer. Im Footer steht die Songinfo des letzten
|
|
%% eingebetteten Lieds (abschaltbar mit footersong = ##f im HEADER bzw. mit
|
|
%% footersong = \andereslied umstellbar), die Songinfo des Hauptlieds wird
|
|
%% mit \songinfo-of selbst platziert.
|
|
%%
|
|
%% Beispiel:
|
|
%%
|
|
%% \include "../../lilypond-song-includes/liedbausteine/lied_a.ly"
|
|
%% liedA = \captureSong
|
|
%% \include "../../lilypond-song-includes/liedbausteine/lied_b.ly"
|
|
%% liedB = \captureSong
|
|
%%
|
|
%% TEXT = \markuplist {
|
|
%% \songinfo-of \liedA
|
|
%% \vspace #2
|
|
%% \embedded-song-title \liedB
|
|
%% \vspace #.5
|
|
%% \score { \songMusic \liedB \layout { \LAYOUT } }
|
|
%% \with-song \liedB {
|
|
%% \chordlyrics \secondVerse
|
|
%% }
|
|
%% }
|
|
%%
|
|
%% \restoreSong \liedA
|
|
%% \embedSong \liedB
|
|
%% \include "public_includes/layout_bottom.ily"
|
|
%%
|
|
%% \captureSong sichert den Header (basicSongInfo) und die Variablen aus
|
|
%% captured-song-variable-names, damit das naechste \include sie nicht
|
|
%% ueberschreibt. Das Ergebnis ist ein normales Header-Modul, es kann also
|
|
%% auch in \header { \liedA } verwendet werden.
|
|
%%
|
|
%% \restoreSong setzt basicSongInfo, HEADER und diese Variablen zurueck,
|
|
%% TEXT aber nicht. TEXT also vor \restoreSong schreiben, solange darin noch
|
|
%% Variablen des zuletzt eingebundenen Lieds (\secondVerse usw.) stehen.
|
|
%%
|
|
%% Ohne \with-song beziehen sich Strophen mit Akkorden auf das Hauptlied.
|
|
%%
|
|
%% Alle Verzeichniseintraege zeigen auf die erste Seite des Hauptlieds.
|
|
|
|
#(define captured-song-variable-names '(MUSIC global firstVoice verseChords))
|
|
|
|
#(define (song-variable song name)
|
|
(assq-ref (module-ref song 'captured-song-variables '()) name))
|
|
|
|
#(define (extract-and-check-vars-from-module headermodule varlist)
|
|
(let* ((headervars (if (module? headermodule) (hash-map->list cons (struct-ref headermodule 0)) '()))
|
|
(extract-var-and-check (lambda (headervar)
|
|
(let* ((variableref (assoc-ref headervars headervar))
|
|
(extracted (if variableref (variable-ref variableref) #f)))
|
|
(if (and extracted (not (and (string? extracted) (string-null? extracted)))) extracted #f)))))
|
|
(map (lambda (varsymbol)
|
|
(cons varsymbol (extract-var-and-check varsymbol))
|
|
) varlist)))
|
|
|
|
% Text des PDF-Lesezeichens eines Lieds, #f wenn es keins bekommt.
|
|
% Gleiche Regel wie in build-full-title.
|
|
#(define (song-pdf-bookmark-title headermodule)
|
|
(let* ((vars (extract-and-check-vars-from-module headermodule '(title starttext)))
|
|
(title (assq-ref vars 'title))
|
|
(starttext (assq-ref vars 'starttext))
|
|
(as-string (lambda (m) (if (markup? m) (markup->string m) ""))))
|
|
(cond ((and title starttext) (string-append (as-string starttext) " | " (as-string title)))
|
|
(title (as-string title))
|
|
(starttext (string-append (as-string starttext) " | "))
|
|
(else #f))))
|
|
|
|
#(define (embedded-song-list songs)
|
|
(if (list? songs) (filter module? songs) '()))
|
|
|
|
#(define (embedded-songs-of-header headermodule)
|
|
(if (module? headermodule)
|
|
(embedded-song-list (module-ref headermodule 'embeddedsongs '()))
|
|
'()))
|
|
|
|
% Ersetzt alle header:*-Properties durch die Felder aus headermodule, damit
|
|
% keine Felder des Hauptlieds in das andere Lied durchsickern.
|
|
#(define (header-module->props headermodule props)
|
|
(let ((header-key? (lambda (entry)
|
|
(and (pair? entry) (symbol? (car entry))
|
|
(string-prefix? "header:" (symbol->string (car entry)))))))
|
|
(cons
|
|
(map (lambda (kv) (cons (symbol-append 'header: (car kv)) (cdr kv)))
|
|
(ly:module->alist headermodule))
|
|
(map (lambda (alist) (remove header-key? alist)) props))))
|
|
|
|
captureSong =
|
|
#(define-scheme-function () ()
|
|
(let ((song (make-module)))
|
|
(ly:module-copy song basicSongInfo)
|
|
(module-define! song 'captured-song-variables
|
|
(filter-map
|
|
(lambda (name)
|
|
(let ((value (ly:parser-lookup name)))
|
|
(and (not (null? value)) (cons name value))))
|
|
captured-song-variable-names))
|
|
song))
|
|
|
|
% Stellt basicSongInfo, HEADER und die gesicherten Variablen eines Lieds
|
|
% wieder her, als waere es das zuletzt eingebundene.
|
|
restoreSong =
|
|
#(define-void-function (song) (module?)
|
|
(for-each
|
|
(lambda (kv) (ly:parser-define! (car kv) (cdr kv)))
|
|
(module-ref song 'captured-song-variables '()))
|
|
(ly:parser-define! 'basicSongInfo song)
|
|
(ly:parser-define! 'HEADER #{ \bookpart { \header { $song } } #}))
|
|
|
|
embedSong =
|
|
#(define-void-function (song) (module?)
|
|
(set! EMBEDDED_SONGS (append EMBEDDED_SONGS (list song))))
|
|
|
|
songMusic =
|
|
#(define-music-function (song) (module?)
|
|
(let ((music (song-variable song 'MUSIC)))
|
|
(if (ly:music? music) music (make-music 'SequentialMusic))))
|
|
|
|
% Strophen, \fromproperty #'header:... usw. beziehen sich innerhalb auf das
|
|
% angegebene Lied. Die Transposition des Hauptlieds wird nicht uebernommen.
|
|
#(define-markup-list-command (with-song layout props song args) (module? markup-list?)
|
|
(let* ((music-or-empty (lambda (name)
|
|
(let ((music (song-variable song name)))
|
|
(if (ly:music? music) music (make-music 'SequentialMusic)))))
|
|
(verseChords (music-or-empty 'verseChords))
|
|
(global (music-or-empty 'global))
|
|
(firstVoice (music-or-empty 'firstVoice)))
|
|
(interpret-markup-list layout
|
|
(prepend-alist-chain 'verse-chords #{ \chords { $verseChords } #}
|
|
(prepend-alist-chain 'verse-reference-voice #{ $global $firstVoice #}
|
|
(prepend-alist-chain 'transposition (cons #f #f)
|
|
(header-module->props song props))))
|
|
args)))
|
|
|
|
#(define-markup-command (songinfo-of layout props song) (module?)
|
|
(interpret-markup layout (header-module->props song props)
|
|
(make-fractional-line-width-markup (make-songinfo-content-markup))))
|
|
|
|
#(define-markup-command (embedded-song-title layout props song) (module?)
|
|
(interpret-markup layout (header-module->props song props)
|
|
(make-fill-line-markup (list (ly:output-def-lookup layout 'defaultTitleMarkup)))))
|
|
|
|
% PDF-Lesezeichen der eingebetteten Lieder, wird von build-full-title direkt
|
|
% nach dem Lesezeichen des Hauptlieds ausgegeben.
|
|
#(define-markup-command (embedded-songs-to-pdf-toc layout props) ()
|
|
(apply ly:stencil-add
|
|
(cons empty-stencil
|
|
(filter-map
|
|
(lambda (song)
|
|
(let ((bookmark (song-pdf-bookmark-title song)))
|
|
(and bookmark (make-pdf-toc-stencil bookmark 0))))
|
|
(embedded-song-list (chain-assoc-get 'header:embeddedsongs props '()))))))
|