extract meta data from lilypond compilation
* write that as yaml file next to the other outputs * extract lyrics * makes chordpro export work in songbooks * more metadata for chordpro export
This commit is contained in:
@@ -65,6 +65,26 @@ additionalPageNumbers =
|
||||
display-pages-list
|
||||
)
|
||||
|
||||
% Fuer Zusatzseiten: Label der ersten Seite des zusammenhaengenden
|
||||
% Zusatzseiten-Blocks, zu dem label gehoert (bestimmt den Buchstaben-Suffix a, b, ...).
|
||||
#(define (earliest-additional-label label)
|
||||
(let find-earliest-additional-label
|
||||
((rest-additional-page-switch-label-list (member (cons label #t) additional-page-switch-label-list)))
|
||||
(if (cdadr rest-additional-page-switch-label-list)
|
||||
(find-earliest-additional-label (cdr rest-additional-page-switch-label-list))
|
||||
(caar rest-additional-page-switch-label-list))))
|
||||
|
||||
% Sichtbare Seitenzahl der Seite mit diesem Label: normalerweise eine Zahl,
|
||||
% auf Zusatzseiten ein String mit Buchstaben-Suffix (z.B. "12a"); gleiche
|
||||
% Logik wie custom-page-number unten.
|
||||
#(define (visible-page-value layout label)
|
||||
(let ((display-page (assq-ref (build-display-pages-list layout) label)))
|
||||
(if (assq-ref additional-page-switch-label-list label)
|
||||
(ly:format "~a~a" display-page
|
||||
(string (integer->char (+ 97 (- (real-page-number layout label)
|
||||
(real-page-number layout (earliest-additional-label label)))))))
|
||||
display-page)))
|
||||
|
||||
% TODO:
|
||||
% Eigentlich können wir das direkt in oddFooderMarkup und evenFooterMarkup aufrufen
|
||||
% vermutlich sogar ohne den delay kram. Wir sollten außerdem einfach nur die property
|
||||
@@ -97,12 +117,7 @@ width may require additional tweaking.)"
|
||||
(if (assq-ref additional-page-switch-label-list label)
|
||||
(make-concat-markup (list (number-format number-type display-page)
|
||||
(make-char-markup (+ 97 (- real-current-page (real-page-number layout
|
||||
(let find-earliest-additional-label
|
||||
((rest-additional-page-switch-label-list (member (cons label #t) additional-page-switch-label-list)))
|
||||
(if (cdadr rest-additional-page-switch-label-list)
|
||||
(find-earliest-additional-label (cdr rest-additional-page-switch-label-list))
|
||||
(caar rest-additional-page-switch-label-list)))
|
||||
))))))
|
||||
(earliest-additional-label label)))))))
|
||||
(number-format number-type (+ display-page (- real-current-page (real-page-number layout label))))
|
||||
))
|
||||
(page-stencil (interpret-markup layout props page-markup))
|
||||
@@ -118,6 +133,79 @@ width may require additional tweaking.)"
|
||||
(make-filled-box-stencil x-ext y-ext))))
|
||||
|
||||
|
||||
%% Schreibt die Daten aller Lieder des Buchs (extract-song-data, siehe
|
||||
%% data_extractor.ily) als YAML-Datei: oberster Key contents, darunter eine
|
||||
%% Liste mit den Lied-Daten, jeweils angereichert um den Key page mit der
|
||||
%% sichtbaren Seitenzahl des Liedanfangs. Einbinden wie \write-toc-csv in
|
||||
%% einem Markup des Buchs; wie dort passiert das Schreiben ueber ein
|
||||
%% delayed stencil, weil die Label->Seiten-Tabelle erst beim Ausgeben des
|
||||
%% Buchs feststeht. Der Dateiname ist der Ausgabename des Buchs
|
||||
%% (\bookOutputName, sonst der .ly-Basisname) mit angehaengtem .yml --
|
||||
%% landet also wie das PDF relativ zum Arbeitsverzeichnis.
|
||||
#(define-markup-command (write-book-yaml layout props) ()
|
||||
(let ((filename (string-append (ly:parser-output-name) ".yml")))
|
||||
(ly:make-stencil
|
||||
`(delay-stencil-evaluation
|
||||
,(delay
|
||||
(begin
|
||||
(scm->yml-file filename
|
||||
(list (cons 'contents
|
||||
(map (lambda (song)
|
||||
(let ((songvars (cdr song)))
|
||||
(cons (cons 'page (visible-page-value layout (assq-ref songvars 'label)))
|
||||
(extract-song-data (assq-ref songvars 'header)
|
||||
(assq-ref songvars 'music)
|
||||
;; Textseiten-Strophen aus dem Render-
|
||||
;; Collector-Store (Key = Liedname); beim
|
||||
;; Ausgeben des Buchs sind alle Textseiten
|
||||
;; bereits interpretiert.
|
||||
(hash-ref chordpro-song-store
|
||||
(symbol->string (car song)))
|
||||
(symbol->string (car song))))))
|
||||
(reverse (alist-delete 'markupPage
|
||||
(alist-delete 'imagePage
|
||||
(alist-delete 'emptyPage song-list))))))))
|
||||
empty-stencil))))))
|
||||
|
||||
%% Schreibt den ChordPro-Export aller Lieder des Buchs (in Dokument-
|
||||
%% reihenfolge) als EINE Datei statt als Einzeldateien pro Lied. Lieder
|
||||
%% werden mit der ChordPro-Direktive {new_song} getrennt; vor dem ersten
|
||||
%% Lied ist sie nicht noetig, sie gilt am Dateianfang implizit (siehe
|
||||
%% https://www.chordpro.org/chordpro/directives-new_song/). Einbinden wie
|
||||
%% \write-book-yaml an beliebiger Stelle im Buch -- unabhaengig von
|
||||
%% chordpro-export-enabled (das steuert nur den Pro-Song-Export), beide
|
||||
%% Befehle koennen auch gleichzeitig genutzt werden. Wie bei
|
||||
%% \write-book-yaml passiert das Schreiben ueber ein delayed stencil,
|
||||
%% weil der Render-Collector-Store erst beim Ausgeben des Buchs
|
||||
%% vollstaendig gefuellt ist. Der Dateiname ist wie bei \write-book-yaml
|
||||
%% der Ausgabename des Buchs mit angehaengtem .cho.
|
||||
#(define-markup-command (write-book-chordpro layout props) ()
|
||||
(let ((filename (string-append (ly:parser-output-name) ".cho")))
|
||||
(ly:make-stencil
|
||||
`(delay-stencil-evaluation
|
||||
,(delay
|
||||
(begin
|
||||
(with-output-to-file filename
|
||||
(lambda ()
|
||||
(let loop ((songs (filter
|
||||
;; \chordlyrics-Strophen ODER (bei
|
||||
;; chordpro-include-music-lyrics) aus der
|
||||
;; Musik nachgeholte Kandidaten.
|
||||
(lambda (song) (or (> (chordpro-song-verse-index song) 0)
|
||||
(pair? (chordpro-song-music-candidates song))))
|
||||
(filter-map
|
||||
(lambda (song) (hash-ref chordpro-song-store
|
||||
(symbol->string (car song))))
|
||||
(reverse (alist-delete 'markupPage
|
||||
(alist-delete 'imagePage
|
||||
(alist-delete 'emptyPage song-list)))))))
|
||||
(first? #t))
|
||||
(when (pair? songs)
|
||||
(unless first? (display "{new_song}\n"))
|
||||
(display (chordpro-format-song (car songs)))
|
||||
(loop (cdr songs) #f)))))
|
||||
empty-stencil))))))
|
||||
|
||||
includeSong =
|
||||
#(define-void-function (filename) (string?)
|
||||
#{
|
||||
@@ -125,6 +213,15 @@ includeSong =
|
||||
#}
|
||||
(ly:parser-parse-string (ly:parser-clone)
|
||||
(ly:format "\\include \"~a/~a/~a.ly\"" songPath filename filename))
|
||||
;; ChordPro: Titel/Autoren dieses Lieds im Render-Collector-Store
|
||||
;; registrieren -- basicSongInfo haelt direkt nach dem Include genau
|
||||
;; dieses Lied. Der Key ist der Liedname; die Collector-Daten (Verse,
|
||||
;; Akkorde) kommen spaeter beim Rendern ueber \setsongfilename unter
|
||||
;; demselben Key dazu. Unabhaengig von chordpro-export-enabled, damit
|
||||
;; sowohl der Pro-Song-Export (der die Flag braucht) als auch
|
||||
;; \write-book-chordpro (das nur explizit aufgerufen werden muss) auf
|
||||
;; vollstaendige Metadaten zugreifen koennen.
|
||||
(chordpro-register-song-metadata filename HEADER)
|
||||
(let ((label (gensym "index")))
|
||||
(set! additional-page-switch-label-list
|
||||
(acons label additional-page-numbers additional-page-switch-label-list))
|
||||
@@ -302,14 +399,79 @@ songs =
|
||||
(text-pages (assq-ref songvars 'text-pages))
|
||||
(label (assq-ref songvars 'label)))
|
||||
(set! song-number newnumber)
|
||||
#{
|
||||
\bookpart {
|
||||
$header
|
||||
\headerToTOC #header #label
|
||||
\score { $music \layout { $layout songfilename = $(symbol->string filename) } }
|
||||
$(add-text-pages text-pages)
|
||||
}
|
||||
#}))))))))
|
||||
;; ChordPro_music_collector (Strophen unter den Noten, siehe
|
||||
;; chordpro.ily) braucht den Song-Key beim tatsaechlichen
|
||||
;; Interpretieren dieses Songs' Hauptscore. Im Buch ist DAS
|
||||
;; NICHT synchron zu dieser for-each-Schleife -- LilyPond
|
||||
;; interpretiert die \score-Bloecke aller Lieder gesammelt fuer
|
||||
;; das Seitenlayout, oft NACHDEM die Schleife schon beim
|
||||
;; naechsten (oder letzten) Lied angelangt ist. Ein globaler
|
||||
;; chordpro-active-song-key waere zu diesem Zeitpunkt also auf
|
||||
;; ein ANDERES Lied gesetzt -- die gesammelten Strophen landen
|
||||
;; dann fälschlich im Store-Eintrag des zuletzt verarbeiteten
|
||||
;; Lieds (beobachtet: mehrere Lieder ohne eigene \chordlyrics
|
||||
;; wurden alle in den letzten Song der Test-Bibliothek
|
||||
;; geschrieben). Deshalb setzen wir den Key per \applyContext
|
||||
;; DIREKT IN DER MUSIK in chordpro-context-song-keys (siehe
|
||||
;; chordpro.ily) -- das laeuft synchron zur tatsaechlichen
|
||||
;; Interpretation dieses Scores und ist deshalb robust gegen die
|
||||
;; verzoegerte/gesammelte Verarbeitung (eine LilyPond-Context-
|
||||
;; Property ueber den \layout-Block waere hier keine Alternative:
|
||||
;; LilyPond verwirft \applyContext-\ly:context-set-property!-
|
||||
;; Aufrufe fuer nicht deklarierte Properties stillschweigend).
|
||||
;; Der Music_collector liest bevorzugt diese Tabelle,
|
||||
;; chordpro-active-song-key bleibt nur als Fallback
|
||||
;; (Einzellied-Standalone-Pfad, dort synchron genug).
|
||||
;; \consists nur bei chordpro-include-music-lyrics -- sonst
|
||||
;; keine Kosten (Grob-Acknowledger fuer JEDEN Akkord/jede
|
||||
;; Silbe/Strophenmarker des Hauptscores) fuer Buecher, die das
|
||||
;; Feature nicht nutzen. Bewusst NICHT zusaetzlich an
|
||||
;; chordpro-export-enabled gekoppelt -- die steuert nur, ob
|
||||
;; \write-book-chordpro/\chordpro-delayed-write am Ende etwas
|
||||
;; schreiben, nicht ob hier gesammelt wird.
|
||||
(set! chordpro-active-song-key (symbol->string filename))
|
||||
(let* ((collect-music-lyrics? (and (defined? 'chordpro-include-music-lyrics) chordpro-include-music-lyrics))
|
||||
(song-layout
|
||||
(if collect-music-lyrics?
|
||||
#{
|
||||
\layout {
|
||||
$layout
|
||||
songfilename = $(symbol->string filename)
|
||||
\context {
|
||||
\Score
|
||||
\consists \ChordPro_music_collector
|
||||
}
|
||||
}
|
||||
#}
|
||||
#{
|
||||
\layout {
|
||||
$layout
|
||||
songfilename = $(symbol->string filename)
|
||||
}
|
||||
#}))
|
||||
(score-music
|
||||
(if collect-music-lyrics?
|
||||
#{
|
||||
<<
|
||||
\applyContext
|
||||
#(let ((key (symbol->string filename)))
|
||||
(lambda (ctx)
|
||||
(hashq-set! chordpro-context-song-keys ctx key)))
|
||||
$music
|
||||
>>
|
||||
#}
|
||||
music)))
|
||||
#{
|
||||
\bookpart {
|
||||
$header
|
||||
\headerToTOC #header #label
|
||||
\score {
|
||||
$score-music
|
||||
$song-layout
|
||||
}
|
||||
$(add-text-pages text-pages)
|
||||
}
|
||||
#})))))))))
|
||||
ordered
|
||||
)))
|
||||
|
||||
|
||||
@@ -370,6 +370,9 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
|
||||
#(define csv-write sxml->csv)
|
||||
|
||||
#(define-markup-command (write-toc-csv layout props) ()
|
||||
;; Dateiname wie bei \write-book-yaml/\write-book-chordpro: der
|
||||
;; Ausgabename des Buchs mit angehaengtem .csv statt fest "toc.csv".
|
||||
(define csv-filename (string-append (ly:parser-output-name) ".csv"))
|
||||
(define (csv-escape field)
|
||||
(if (string-null? field)
|
||||
field
|
||||
@@ -453,7 +456,7 @@ headerToTOC = #(define-music-function (parser location header label) (ly:book? s
|
||||
(format-info-paragraphs (headervar-or-empty 'pronunciation))
|
||||
))))
|
||||
(alist-delete 'markupPage (alist-delete 'imagePage (alist-delete 'emptyPage song-list))))))
|
||||
(call-with-output-file "toc.csv"
|
||||
(call-with-output-file csv-filename
|
||||
(lambda (port)
|
||||
(csv-write (cons '(
|
||||
"filename"
|
||||
|
||||
Reference in New Issue
Block a user