63 Commits

Author SHA1 Message Date
tux
36f1e8c0fd new engraver for spacing in chordlyrics 2025-06-12 22:29:58 +02:00
tux
53c41bf5dc add transliteration to toc 2025-06-10 07:33:09 +02:00
c49dba89f7 include adaptions/editings in author system 2025-06-07 16:27:32 +02:00
tux
195110923a include another song without outputting anything 2025-06-06 18:13:22 +02:00
tux
8d0ddcee06 introduce TEXT_PAGES 2025-05-19 22:14:08 +02:00
tux
c47ceb443e fix some chord spacing 2025-05-11 17:59:56 +02:00
tux
377beeab57 refactor and fix tagging bug 2025-05-11 11:39:19 +02:00
tux
cb4b74d4f7 indents for verse lines 2025-05-10 15:13:47 +02:00
616034baca Remove Metronome_mark_engraver 2025-05-08 23:28:08 +04:00
tux
34c9c5a932 capoTranspose eingebaut 2025-04-30 17:31:01 +02:00
57bcf94167 add default chordName exceptions 2025-04-27 14:37:07 +02:00
9a9ef8e8df fix transpositions for verses 2025-04-27 12:15:32 +04:00
tux
531e459af1 fix files-in-directory 2025-04-27 01:47:53 +02:00
tux
fc1bc74b10 fix altChord transposition again 2025-04-27 01:21:13 +02:00
tux
d47ae1a66d interpret songinfo with markups 2025-04-26 21:29:06 +02:00
37ee497f89 make cues configurable 2025-04-26 23:26:12 +04:00
41bd881014 remove Jump_engraver 2025-04-26 21:40:05 +04:00
tux
00a7b0ebc8 pseudoIndents for custom layouting 2025-04-21 19:48:30 +02:00
tux
760ca71ba7 melisOff and melisOn 2025-04-21 19:11:05 +02:00
tux
17074e2db6 secondVoiceStyle for Accidentals 2025-04-19 22:33:54 +02:00
tux
654f619fba sort contributions 2025-04-19 19:08:13 +02:00
tux
cc4fc9f297 fix altChord for SVG Output 2025-04-19 17:03:20 +02:00
d2dbf3448a change accidentalStyle to modern-voice-cautionary 2025-04-17 22:52:36 +04:00
tux
140bc611d1 fix altChord transposition 2025-04-17 20:22:21 +02:00
tux
eccd9b8730 fill-midi 2025-04-05 14:31:19 +02:00
900f4e1d06 workaround popen trouble on windows 2025-03-28 11:01:55 +01:00
7d01bd769c use authorId if not empty and not found 2025-03-27 17:00:05 +01:00
a0e70b4c4b add dottedExtender 2025-03-24 20:23:21 +01:00
04a01d3b3f Workaround für komische Python-Pfade 2025-03-13 20:14:43 +01:00
07b598db06 Fix handling of 'alttitle' variable to support both strings and lists in csv 2025-03-03 20:18:45 +04:00
a61becaf5e Support multiple alternative titles in headerToTOC function
This update allows the alttitle header field to accept a list of alternative titles instead of a single value. Each title in the list is processed and added to the table of contents as an alternative title, enhancing flexibility and usability.
2025-03-03 16:57:05 +04:00
f0f4b0c180 change default font 2025-02-25 08:21:31 +01:00
b6bead747d change midi settings 2025-02-25 08:19:07 +01:00
4dbdb72d30 fix layout size problem 2025-02-18 08:57:07 +01:00
aa8b9526fa implementing swingOff 2025-02-10 12:21:31 +04:00
tux
3cb0e00e8f updates for newest lilypond versions 2025-02-09 13:06:12 +01:00
152d56fe3f Autorenverzeichnis sortiert 2025-02-06 23:38:00 +01:00
883c32701c work if authors not specified 2025-02-02 16:12:04 +01:00
7b97e04fab optimize link area in toc 2025-01-28 00:26:18 +01:00
e4b65aafcb ref stanzas without dot 2025-01-27 22:16:34 +01:00
bf7e959100 appendix and footer improvements 2025-01-27 01:35:48 +01:00
tux
ddd4888041 \ref can take a list of stanza numbers now 2025-01-26 21:51:14 +01:00
tux
a368a3c589 Refactor author-format function 2025-01-23 23:08:15 +01:00
tux
e6725b84d4 scroll to left upper corner with pdf bookmarks 2025-01-23 22:12:09 +01:00
b41e2a62a4 windows includes angepasst 2024-12-31 16:13:21 +01:00
tux
15c27c271f toc for authors 2024-12-22 23:25:49 +01:00
tux
eca352b6d0 use \write-toc-csv in a markup to generate a toc.csv 2024-12-22 21:06:35 +01:00
ed30b34df2 swingMusic for midi that works with partials 2024-12-08 16:05:25 +01:00
a558a0b02f tagged case in lyricmode 2024-12-04 20:37:59 +01:00
tux
c4f3a3c196 use starttext and better PDF bookmarks 2024-12-01 15:48:25 +01:00
tux
db26306b5b use python3 for yml parsing 2024-11-28 09:19:52 +01:00
tux
487bf457fb load AUTHOR_DATA via path convention 2024-11-27 20:28:57 +01:00
tux
47bb7991b8 use yml files as default 2024-11-26 18:04:13 +01:00
tux
d94703547c allow break paged output
* compatibilty to Liederbuchgenerator
* apply standalone output directly to top level not wrapped in a book
2024-11-26 18:02:09 +01:00
tux
f0ffd3f630 use yml data structures with python parser 2024-11-24 15:23:32 +01:00
8dfbc5ef25 referencable appendix 2024-11-23 23:51:01 +01:00
tux
8800341e18 Make it possible to override a stanza number 2024-11-20 17:14:04 +01:00
tux
d56c11c5ff force printing first chord on newline 2024-11-16 23:21:37 +01:00
tux
754682afcf multiVerseSkips and alt functions 2024-11-03 17:35:45 +01:00
tux
88f0dc9f8f allow inherits in json file 2024-11-02 19:58:15 +01:00
tux
e9b904c32c more robust scm file include 2024-11-02 19:54:15 +01:00
tux
fd14138d0b no chord repeating by default in chordlyrics after linebreak 2024-11-02 19:51:10 +01:00
tux
a1bc48b824 right and left hyphen 2024-10-23 22:21:46 +02:00
21 changed files with 1053 additions and 171 deletions

View File

@ -1,7 +1,24 @@
#(define noStandaloneOutput (if (defined? 'noStandaloneOutput) noStandaloneOutput #f))
#(if (defined? 'LAYOUT) #f (load "json_parser.scm"))
#(use-modules (json parser))
#(define windows? (string-prefix-ci? "windows" (utsname:sysname (uname))))
#(if (defined? 'LAYOUT) #f
(let ((scm-load (lambda (filename) (load (
string-append
; on windows the detection of absolute pathes is broken (cause they start with a drive letter and not with a /)
; so we have to use relative pathes for load. That works in frescobaldi, but not if you call lilypond from command line,
; with a relative path to the .ly file, so we use absolute pathes on posix systems, where it works.
(if windows?
""
(string-append (dirname (current-filename)) file-name-separator-string))
"scm" file-name-separator-string filename
)))))
(scm-load "json_parser.scm")
(scm-load "resolve_inherits.scm")
(scm-load "yaml_parser.scm")))
#(define AUTHOR_DATA (if (defined? 'AUTHOR_DATA) AUTHOR_DATA (parse-yml-file "../../lilypond-song-includes/data/authors.yml")))
#(define SONG_DATA (if (defined? 'SONG_DATA) SONG_DATA (parse-yml-file "../../lilypond-song-includes/data/songs.yml")))
\include "basic_format_and_style_settings.ly"
\include "eps_file_from_song_dir.ly"
\include "title_with_category_images.ly"
@ -11,12 +28,15 @@
\include "arrows_in_scores.ly"
\include "swing_style.ly"
\include "inline_score.ly"
\include "custom_indentation.ily"
\include "include_from_song.ily"
% reset important variables
LAYOUT = \layout { \generalLayout }
HEADER = {}
MUSIC = {}
TEXT = \markuplist {""}
TEXT = \markuplist {}
TEXT_PAGES = #f
verseChords = {}
firstVoice = {}

44
appendix.ly Normal file
View File

@ -0,0 +1,44 @@
appendix =
#(define-void-function (parser location title) (markup?)
(define (appendix-item->markup layout props appendix-item)
(interpret-markup layout props
(markup
#:override (cons 'appendixItem:heading (assoc-ref appendix-item "heading"))
#:override (cons 'appendixItem:text (assoc-ref appendix-item "text"))
(ly:output-def-lookup layout 'appendixItemMarkup))))
(ly:book-add-bookpart! (ly:parser-lookup '$current-book)
#{
\bookpart {
\markup { #title }
#(for-each
(lambda (item)
(add-score (ly:make-page-label-marker (string->symbol (car item))))
(add-text
(make-on-the-fly-markup
(lambda (layout props arg) (appendix-item->markup layout props (cdr item)))
(make-null-markup)))
)
(reverse APPENDIX_DATA))
}
#}))
#(define-markup-command (appendix-ref layout props label) (symbol?)
"call page-ref to appendix-item"
(interpret-markup layout props
(markup #:with-link label
#:override (cons 'appendixPage (make-page-ref-markup label "888" "?"))
(ly:output-def-lookup layout 'appendixReferenceMarkup))))
\paper {
appendixItemMarkup = \markup {
\left-column {
\line { \large \bold \fromproperty #'appendixItem:heading }
\vspace #0.2
\sans \wordwrap-field #'appendixItem:text
\vspace #0.7
}
}
appendixReferenceMarkup = \markup {
\fromproperty #'appendixPage
}
}

View File

@ -24,6 +24,11 @@
markup-system-spacing = #'((basic-distance . 1))
score-markup-spacing = #'((padding . 2))
top-markup-spacing = #'((basic-distance . 0) (minimum-distance . 0) (padding . 0))
refMarkupFormatter = #(lambda (layout props stanzanumbers)
(interpret-markup layout props
(if (null? stanzanumbers)
refString
(ly:format refStringWithNumbers (string-join (map (lambda (stanzanumber) (ly:format "~a" stanzanumber)) stanzanumbers) ", ")))))
}
generalLayout = \layout {
@ -38,7 +43,7 @@ generalLayout = \layout {
}
\context {
\Staff
\accidentalStyle modern
\accidentalStyle modern-voice-cautionary
\consists Merge_rests_engraver
}
\context {
@ -66,6 +71,7 @@ generalLayout = \layout {
\override Stem.layer = 2
\override Flag.layer = 2
\override Beam.layer = 2
\override Accidental.layer = 2
}
}
@ -81,11 +87,13 @@ secondVoiceStyle = {
\override Stem.color = #grey
\override Flag.color = #grey
\override Beam.color = #grey
\override Accidental.color = #grey
\override NoteHead.layer = 1
\override Dots.layer = 1
\override Stem.layer = 1
\override Flag.layer = 1
\override Beam.layer = 1
\override Accidental.layer = 1
}
firstVoiceStyle = {
@ -94,6 +102,7 @@ firstVoiceStyle = {
\override Stem.color = #black
\override Flag.color = #black
\override Beam.color = #black
\override Accidental.color = #black
}
% Deprecated: einzelne Noten innerhalb von \secondVoiceStyle mit schwarzem statt grauem Kopf
@ -105,22 +114,35 @@ romanStanza =
#(define-music-function (parser location) ()
#{ \override StanzaNumber.style = #'roman #})
override-stanza =
#(define-music-function (parser location stanzanumber) (number?)
#{
\once \override StanzaNumber.forced-spacing = #stanzanumber % misuse property "forced-spacing" to override the stanzanumber
#}
)
stanza =
#(define-music-function (parser location stanzanumber) (number?)
#{
\once \override StanzaNumber.layer = 23 % set this to signal that there is a real stanza and no repeat signs
\applyContext
#(lambda (context)
(let* ((stanzastyle (ly:assoc-get 'style (ly:context-grob-definition context 'StanzaNumber)))
(let* ((stanzanumber (ly:assoc-get 'forced-spacing (ly:context-grob-definition context 'StanzaNumber) stanzanumber))
(stanzastyle (ly:assoc-get 'style (ly:context-grob-definition context 'StanzaNumber)))
(formattedStanzaNumber (format #f (if (eq? stanzastyle 'roman) romanStanzaFormat stanzaFormat) stanzanumber)))
(ly:context-set-property! context 'stanza formattedStanzaNumber)))
#}
)
ref = {
\once \override StanzaNumber.layer = 23 % set this to signal that there is a real stanza and no repeat signs
\set stanza = \refString
}
ref =
#(define-music-function (stanzanumbers lyrics) ((number-list? (list)) ly:music?)
#{ \lyricmode {
\once \override StanzaNumber.layer = 23 % set this to signal that there is a real stanza and no repeat signs
\set stanza = #(make-on-the-fly-markup (lambda (layout props m) ((ly:output-def-lookup layout 'refMarkupFormatter) layout props stanzanumbers)) (make-null-markup))
#lyrics
}
#}
)
% prints a repStart Sign as stanza if the tag 'repeats is kept.
% if there was a stanza already set by the stanza function with StanzaNumber.layer = 23 we set that also as stanza.
@ -147,4 +169,65 @@ repStopWithTag = \lyricmode {
\once \override StanzaNumber.direction = 1
\set stanza = \markup { \repStop }
}
}
}
rightHyphen = \lyricmode {
\once \override StanzaNumber.font-series = #'normal
\once \override StanzaNumber.direction = 1
\set stanza = "-"
}
leftHyphen = \lyricmode {
\once \override StanzaNumber.font-series = #'normal
\set stanza = "-"
}
multiVerseSkips =
#(define-music-function (parser location skips) (number?)
#{ \tag #'multiVerse { \repeat unfold #skips { \skip4 } } #})
alt =
#(define-music-function (parser location a b) (ly:music? ly:music?)
#{ \tag #'firstVerse { #a } \tag #'multiVerse { #b } #})
updown =
#(define-music-function (parser location word) (string?)
#{
\lyricmode {
\tag #'up { \markup { #(string-capitalize word) } }
\tag #'down { \markup { #(string-downcase word) } }
}
#})
dottedExtender = {
\override LyricExtender.style = #'dotted-line
\override LyricExtender.thickness = 2
\override LyricExtender.Y-offset = 0.1
\override LyricExtender.stencil =
#(lambda (grob)
(let* ((stil (ly:lyric-extender::print grob))
(nostil (null? stil))
(x-ext (if nostil 0 (ly:stencil-extent stil X))))
(if nostil
stil
(make-connected-line
(list
(cons (car x-ext) 0)
(cons (cdr x-ext) 0))
grob))))
}
melisOff = \set ignoreMelismata = ##t
melisOn = \unset ignoreMelismata
cue =
#(define-music-function (zahlen) (number-list?)
#{
\tag #'cues {
\tweak self-alignment-X #LEFT
\mark
#(make-on-the-fly-markup
(lambda (layout props m) (interpret-markup layout (prepend-alist-chain 'cues zahlen props) (ly:output-def-lookup layout 'cueMarkup)))
(make-null-markup))
}
#})

View File

@ -9,7 +9,7 @@
(closedir dir)
files)
(else
(next (readdir dir) (if (string-match "^(0|\\.)" f) files (cons f files))))))))
(next (readdir dir) (if (ly:regex-match? (ly:regex-exec (ly:make-regex "^(0|\\.)") f)) files (cons f files))))))))
#(define (file-to-stats filename)
(set! song-list (cons filename song-list)))
@ -114,7 +114,7 @@ width may require additional tweaking.)"
includeSong =
#(define-void-function (parser location textproc filename) ((procedure?) string?)
#(define-void-function (parser location filename) (string?)
#{
\bookOutputName #filename
#}
@ -125,7 +125,15 @@ includeSong =
(acons label additional-page-numbers additional-page-switch-label-list))
(set! song-list
(acons (string->symbol filename)
(acons 'label label (acons 'header HEADER (acons 'music MUSIC (acons 'layout LAYOUT (acons 'text #{ \markuplist \setsongfilename $filename $(if textproc (textproc TEXT) TEXT) #} '())))))
(acons 'label label
(acons 'header HEADER
(acons 'music MUSIC
(acons 'layout LAYOUT
(acons 'text-pages
(map (lambda (text)
#{ \markuplist \setsongfilename $filename $text #})
TEXT_PAGES)
'())))))
song-list))
))
@ -184,14 +192,14 @@ songs =
;(header (assq-ref songvars 'header))
(music (assq-ref songvars 'music))
(layout (assq-ref songvars 'layout))
(text (assq-ref songvars 'text))
(text-pages (assq-ref songvars 'text-pages))
(label (assq-ref songvars 'label)))
#{
\bookpart {
$header
\headerToTOC #header #label
\score { $music \layout { $layout } }
$text
$(add-text-pages text-pages)
}
#}))))))
(reverse song-list)

2
break_paged_output.ily Normal file
View File

@ -0,0 +1,2 @@
\include "lilypond-book-preamble.ly"
#(ly:set-option 'separate-page-formats "pdf")

View File

@ -38,31 +38,47 @@ shiftChords = #(define-music-function (parser location xshift chords) (number? l
$chords
#})
altChord = #(define-music-function (parser location mainchord altchord) (ly:music? ly:music?)
(define (chord-namer in-pitches bass inversion context)
#{
\markup {
\translate #'(-0.5 . 0)
\score {
\chords { #mainchord \klamm #altchord }
\layout {
\LAYOUT
\context {
\ChordNames
\override ChordName.extra-spacing-width = #'(0 . 0.3)
}
\context {
\Score
\override SpacingSpanner.spacing-increment = 0
}
}
}
}
#})
#{
\once \set chordNameFunction = #chord-namer
#mainchord
#})
altChord =
#(define-music-function (parser location mainchord altchord) (ly:music? ly:music?)
(let* ((remove-point-and-click
(lambda (grob)
(ly:grob-set-property! grob 'cause #f)
(ly:text-interface::print grob)))
(chord-name (lambda (in-pitches bass inversion context) #{
\markup {
\translate #'(-0.5 . 0)
\score {
\chords { \transposable #(cons (car (music-pitches mainchord)) (car in-pitches)) { #(music-clone mainchord) \klamm #(music-clone altchord) } }
\layout {
\LAYOUT
\context {
\ChordNames
\override ChordName.extra-spacing-width = #'(0 . 0.3)
\override ChordName.stencil = #remove-point-and-click
}
\context {
\Score
\override SpacingSpanner.spacing-increment = 0
}
}
}
}#})))
#{
\once \set chordNameFunction = #chord-name
#mainchord
#}))
% Akkorde werden so transponiert, dass sie passen, wenn man mit Kapo im angegebenen Bund spielt
capoTranspose =
#(define-music-function (fret chords) (number? ly:music?)
(define semi->pitch
(make-semitone->pitch
(music-pitches
#{ h b a gis g fis f e es d cis c #})))
(transpose
(ly:pitch-transpose (semi->pitch fret) (ly:make-pitch 0 0))
(ly:make-pitch 0 0)
chords))
% kleine Mollakkorde und Alteration ausgeschrieben
#(define (note-name->german-markup-nosym pitch lowercase?)
@ -100,11 +116,42 @@ altChord = #(define-music-function (parser location mainchord altchord) (ly:musi
% additional bass notes should get uppercased
#(define (bassnote-name->german-markup-nosym pitch lowercase?)(note-name->german-markup-nosym pitch #f))
defaultChordPrintings = {
<c g>-\markup { \super "5" }
%% Dur
<c e g a>-\markup { \super "6" } %Standardverhalten
<c e a >-\markup { \super "6(no5)" }
<c e g a d'>-\markup { \super "6/9" }
<c e g b d'>-\markup { \super "9" } %Standardverhalten
<c e g b d' f'>-\markup { \super "11" } %Standardverhalten
<c e g b d' a'>-\markup { \super "13" }
%add chords
<c e g d'>-\markup { \super "add9" }
<c e g f'>-\markup { \super "add11" }
<c e g a'>-\markup { \super "add13" }
%major chords
<c e g h d'>-\markup { \super "maj9" }
%% Moll
<c es g a>-\markup { \super "6" } %Standardverhalten
<c es a >-\markup { \super "6(no5)" }
<c es g a d'>-\markup { \super "6/9" }
<c es g b d'>-\markup { \super "9" } %Standardverhalten
<c es g b d' f'>-\markup { \super "11" } %Standardverhalten
<c es g b d' a'>-\markup { \super "13" }
%add chords
<c es g d'>-\markup { \super "add9" }
<c es g f'>-\markup { \super "add11" }
<c es g a'>-\markup { \super "add13" }
%major chords
<c es g h d'>-\markup { \super "maj9" }
}
#(define chordNameExceptions
(if (defined? 'customChordPrintings)
(sequential-music-to-chord-exceptions customChordPrintings #t)
'()))
(append
(if (defined? 'customChordPrintings)
(sequential-music-to-chord-exceptions customChordPrintings #t)
'())
(sequential-music-to-chord-exceptions defaultChordPrintings #t)))
generalLayout = \layout {
\generalLayout
@ -138,3 +185,33 @@ generalLayout = \layout {
}
}
#}))
% If you add this engraver to ChordNames Context chords get only printed on chordchanges and if its the first chord after line break.
Ensure_first_chord_after_line_break_printed_engraver =
#(lambda (ctx)
(define last-system-column-rank 0)
(make-engraver
(acknowledgers
((chord-name-interface this-engraver grob source-engraver)
(ly:grob-set-property! grob 'after-line-breaking
(lambda (grob)
(let ((current-system-column-rank (car (ly:grob-spanned-column-rank-interval (ly:grob-system grob)))))
(if (and
(ly:context-property ctx 'chordChanges #f)
(ly:grob-property grob 'begin-of-line-visible #f)
(not (= last-system-column-rank current-system-column-rank)))
; the current chord handling implementation in lilypond uses 'begin-of-line-visible to mark repeated chords
(ly:grob-set-property! grob 'begin-of-line-visible #f))
(set! last-system-column-rank current-system-column-rank)
(ly:chord-name::after-line-breaking grob)
)))))))
% If you add this engraver to ChordNames Context chords get only printed on chordchanges and not at newline.
Ignoring_newline_chord_changes_engraver =
#(lambda (ctx)
(make-engraver
(acknowledgers
((chord-name-interface this-engraver grob source-engraver)
(when (and (ly:context-property ctx 'chordChanges #f) (ly:grob-property grob 'begin-of-line-visible #f))
(ly:grob-suicide! grob)
)))))

135
custom_indentation.ily Normal file
View File

@ -0,0 +1,135 @@
% https://lsr.di.unimi.it/LSR/Snippet?id=1098
%%%%%%%% HEADER %%%%%%%%
%
% this code was prompted by
% https://lists.gnu.org/archive/html/lilypond-user/2019-07/msg00139.html
% and offers a pseudoIndent hack suitable for general use
% keywords:
% indent short-indent indentation system line
% mid-score temporarily arbitrary individual single just only once
% coda margin
% mouse's tale acrostic mesostic spine
%%%%%%%% PSEUDOINDENT FUNCTIONS %%%%%%%%
% these two functions are for indenting individual systems
% - to left-indent a system, apply \pseudoIndent before the music continues
% - \pseudoIndents is similar, but lets you also indent on the right
% - both provide an option for changing that system's instrument names
% N.B. these functions
% - assume application to non-ragged lines (generally the default)
% - include a manual \break to ensure application at line start
% - misbehave if called more than once at the same line start
% the parameters of the (full) pseudoIndents function are:
% 1: name-tweaks
% usually omitted; accepts replacement \markup for instrument names
% as an ordered list; starred elements leave their i-names unchanged.
% 2: left-indent
% additional left-indentation, in staff-space units; can be negative,
% but avoid a total indentation which implies (unsupported) stretching.
% 3: right-indent
% amount of right-indentation, in staff-space units; can be negative.
% - not offered by the (reduced) pseudoIndent function
pseudoIndents = % inline alternative to a new \score, also with right-indent
#(define-music-function (name-tweaks left-indent right-indent)
((markup-list? '()) number? number?)
(define (warn-stretched p1 p2) (ly:input-warning (*location*) (G_
" pseudoIndents ~s ~s is stretching staff; expect distorted layout") p1 p2))
(let* (
(narrowing (+ left-indent right-indent)) ; of staff implied by args
(set-staffsymbol! (lambda (staffsymbol-grob) ; change staff to new width
(let* (
(left-bound (ly:spanner-bound staffsymbol-grob LEFT))
(left-moment (ly:grob-property left-bound 'when))
(capo? (moment<=? left-moment ZERO-MOMENT)) ; in first system of score
(layout (ly:grob-layout staffsymbol-grob))
(lw (ly:output-def-lookup layout 'line-width)) ; debugging info
(indent (ly:output-def-lookup layout (if capo? 'indent 'short-indent)))
(old-stil (ly:staff-symbol::print staffsymbol-grob))
(staffsymbol-x-ext (ly:stencil-extent old-stil X))
;; >=2.19.16's first system has old-stil already narrowed [2]
;; compensate for this (ie being not pristine) when calculating
;; - old leftmost-x (its value is needed when setting so-called 'width)
;; - the new width and position (via local variable narrowing_)
(ss-t (ly:staff-symbol-line-thickness staffsymbol-grob))
(pristine? (<= 0 (car staffsymbol-x-ext) ss-t)) ; would expect half
(leftmost-x (+ indent (if pristine? 0 narrowing)))
(narrowing_ (if pristine? narrowing 0)) ; uses 0 if already narrowed
(old-width (+ (interval-length staffsymbol-x-ext) ss-t))
(new-width (- old-width narrowing_))
(new-rightmost-x (+ leftmost-x new-width)) ; and set! this immediately
(junk (ly:grob-set-property! staffsymbol-grob 'width new-rightmost-x))
(in-situ-stil (ly:staff-symbol::print staffsymbol-grob))
(new-stil (ly:stencil-translate-axis in-situ-stil narrowing_ X))
;(new-stil (stencil-with-color new-stil red)) ; for when debugging
(new-x-ext (ly:stencil-extent new-stil X)))
(ly:grob-set-property! staffsymbol-grob 'stencil new-stil)
(ly:grob-set-property! staffsymbol-grob 'X-extent new-x-ext)
)))
(set-X-offset! (lambda (margin-grob) ; move grob across to line start
(let* (
(old (ly:grob-property-data margin-grob 'X-offset))
(new (lambda (grob) (+ (if (procedure? old) (old grob) old) narrowing))))
(ly:grob-set-property! margin-grob 'X-offset new))))
(tweak-text! (lambda (i-name-grob mkup) ; tweak both instrumentname texts
(if (and (markup? mkup) (not (string=? (markup->string mkup) "*")))
(begin
(ly:grob-set-property! i-name-grob 'long-text mkup)
(ly:grob-set-property! i-name-grob 'text mkup)
)))) ; else retain existing text
(install-narrowing (lambda (leftedge-grob) ; on staves, + adapt left margin
(let* (
(sys (ly:grob-system leftedge-grob))
(all-grobs (ly:grob-array->list (ly:grob-object sys 'all-elements)))
(grobs-named (lambda (name)
(filter (lambda (x) (eq? name (grob::name x))) all-grobs)))
(first-leftedge-grob (list-ref (grobs-named 'LeftEdge) 0))
(relsys-x-of (lambda (g) (ly:grob-relative-coordinate g sys X)))
(leftedge-x (relsys-x-of first-leftedge-grob))
(leftedged? (lambda (g) (= (relsys-x-of g) leftedge-x)))
(leftedged-ss (filter leftedged? (grobs-named 'StaffSymbol))))
(if (eq? leftedge-grob first-leftedge-grob) ; ignore other leftedges [1]
(begin
(for-each set-staffsymbol! leftedged-ss)
(for-each set-X-offset! (grobs-named 'SystemStartBar))
(for-each set-X-offset! (grobs-named 'InstrumentName))
(for-each tweak-text! (grobs-named 'InstrumentName) name-tweaks)
))))))
(if (negative? narrowing) (warn-stretched left-indent right-indent))
#{ % and continue anyway
% ensure that these overrides are applied only at begin-of-line
\break % (but this does not exclude unsupported multiple application)
% give the spacing engine notice regarding the loss of width for music
\once \override Score.LeftEdge.X-extent = #(cons narrowing narrowing)
% discard line start region of staff and reassemble left-margin elements
\once \override Score.LeftEdge.after-line-breaking = #install-narrowing
% shift the system to partition the narrowing between left and right
\overrideProperty Score.NonMusicalPaperColumn.line-break-system-details
.X-offset #(- right-indent)
% prevent a leftmost barnumber entering a stretched staff
\once \override Score.BarNumber.horizon-padding = #(max 1 (- 1 narrowing))
#}))
pseudoIndent = % for changing just left-indent
#(define-music-function (name-tweaks left-indent)
((markup-list? '()) number?)
#{
\pseudoIndents $name-tweaks $left-indent 0
#})
% [1] versions <2.19.1 can have end-of-line leftedges too
% - these were eliminated in issue 3761
% [2] versions >=2.19.16: the first system behaves differently from the rest
% - a side effect of issue 660 ?
% [3] versions >=2.23.0: LeftEdge's position may well differ in Y (but not in X)
% - a side effect of issue 6084 ?

View File

@ -2,6 +2,7 @@
poetPrefix = "Worte:"
composerPrefix = "Weise:"
compositionPrefix = "Satz:"
adaptionPrefix = "Bearbeitung:"
poetAndComposerEqualPrefix = "Worte und Weise:"
voicePrefix = "Stimme:"
versePrefix = "Strophe:"
@ -42,7 +43,16 @@
(poet-with-year (if (and poet-maybe-with-composer year_text) (string-append poet-maybe-with-composer ", " year_text) poet-maybe-with-composer))
(composer-with-year (if (and composer year_melody) (string-append composer ", " year_melody) composer))
(poet-and-composer-oneliner (if (and poet-with-year composer-with-year) (markup poet-with-year between-poet-and-composer-markup composer-with-year) #f))
(current-line-width (chain-assoc-get 'line-width props (ly:output-def-lookup layout 'line-width))))
(current-line-width (chain-assoc-get 'line-width props (ly:output-def-lookup layout 'line-width)))
(string-with-paragraphs->markuplist (lambda (prefix text)
(if text
(apply append
(map
(lambda (paragraph)
(make-wordwrap-internal-markup-list #t
#{ \markuplist { $(ly:parser-include-string paragraph) } #}))
(ly:regex-split (ly:make-regex "\n[ \t\n]*\n[ \t\n]*") text)))
'()))))
(stack-lines DOWN 0.0 (chain-assoc-get 'baseline-skip props)
(interpret-markup-list layout props
(append
@ -53,10 +63,11 @@
(if composer-with-year (string-append "\n\n" composer-with-year) "")
)))
(make-wordwrap-string-internal-markup-list #t (string-append
(if copyright (string-append "\n\n© " copyright) "")
(if infotext (string-append "\n\n" infotext) "")
(if translation (string-append "\n\nÜbersetzung: " translation) "")
(if pronunciation (string-append "\n\nAussprache: " pronunciation) ""))))))))
(if copyright (string-append "\n\n© " copyright) "")))
(string-with-paragraphs->markuplist "" infotext)
(string-with-paragraphs->markuplist "Übersetzung: " translation)
(string-with-paragraphs->markuplist "Aussprache: " pronunciation)
)))))
(make-null-markup)
)
}

View File

@ -3,9 +3,9 @@ songMargin = 5
songInfoFontSize = 0
songInfoLineWidthFraction = 0.9
songTitleSize = 6
songTitleFont = "LilyPond Sans"
songChordFont = "LilyPond Sans"
songLyricFont = "LilyPond Sans"
songTitleFont = "Liberation Sans"
songChordFont = "Liberation Sans"
songLyricFont = "Liberation Sans"
songChordFontSeries = #'bold
songTextChordAlignment = #'left
songScoreChordFontSize = 2
@ -18,6 +18,18 @@ lyricSize = 1.6
stanzaFormat = "~a."
romanStanzaFormat = "~@r."
refString = "Ref.:"
refStringWithNumbers = "Ref. ~a:"
% hübsche Wiederholungszeichen für den Liedtext
repStart = "𝄆"
repStop = "𝄇"
customChordPrintings = {}
\paper {
cueMarkup = \markup {
\italic
#(make-on-the-fly-markup (lambda (layout props m)
(interpret-markup layout props
(string-join (map (lambda (n) (format #f "~@r." n)) (chain-assoc-get 'cues props)) ", ")))
(make-null-markup))
}
}

View File

@ -1,4 +1,35 @@
#(use-modules (ice-9 receive))
#(define (format-author author-format-function authorId noDetails)
(let ((author (if (defined? 'AUTHOR_DATA) (assoc-ref AUTHOR_DATA authorId) #f)))
(if author
(author-format-function
noDetails
(assoc-ref author "name")
(assoc-ref author "trail_name")
(assoc-ref author "birth_year")
(assoc-ref author "death_year")
(assoc-ref author "organization")
)
(if (string-null? authorId)
"unbekannt"
authorId))))
#(define (find-author-ids-by contributionType authors)
(if authors
(filter-map (lambda (authordata) (if (member contributionType (cdr authordata)) (car authordata) #f)) authors)
(list)))
#(define (find-author-id-with-part-numbers contributionType authors)
(if authors
(sort-list
(filter-map (lambda (authordata)
(let ((contributionNumbers (filter-map (lambda (contribution) (if (and (list? contribution) (equal? contributionType (car contribution))) (cadr contribution) #f)) (cdr authordata)))
(authorId (car authordata)))
(if (null? contributionNumbers) #f (cons authorId (sort-list contributionNumbers <)))
)) authors)
(lambda (a b) (< (cadr a) (cadr b))))
(list)))
#(define-markup-command (print-songinfo layout props) ()
(define (songinfo-from songId key)
(let ((song (if (defined? 'SONG_DATA) (assoc-ref SONG_DATA songId) #f)))
@ -6,39 +37,17 @@
(assoc-ref song key)
(ly:warning (ly:format "song with id ~a not found" songId)))))
(define (format-author authorId noDetails)
(let ((author (if (defined? 'AUTHOR_DATA) (assoc-ref AUTHOR_DATA authorId) #f)))
(if author
((ly:output-def-lookup layout 'authorFormat)
noDetails
(assoc-ref author "name")
(assoc-ref author "trail_name")
(assoc-ref author "birth_year")
(assoc-ref author "death_year")
(assoc-ref author "organization")
)
"unbekannt")))
(define* (default-author-format authorId #:optional (noDetails #f))
(format-author (ly:output-def-lookup layout 'authorFormat) authorId noDetails))
(define (format-poet poetId)
(string-append (ly:output-def-lookup layout 'poetPrefix) " " (format-author poetId #f)))
(string-append (ly:output-def-lookup layout 'poetPrefix) " " (default-author-format poetId)))
(define (format-composer composerId)
(string-append (ly:output-def-lookup layout 'composerPrefix) " " (format-author composerId #f)))
(string-append (ly:output-def-lookup layout 'composerPrefix) " " (default-author-format composerId)))
(define (format-poet-and-composer authorId)
(string-append (ly:output-def-lookup layout 'poetAndComposerEqualPrefix) " " (format-author authorId #f)))
(define (find-author-ids-by contributionType authors)
(filter-map (lambda (authordata) (if (member contributionType (cdr authordata)) (car authordata) #f)) authors)
)
(define (find-author-id-with-part-numbers contributionType authors)
(filter-map (lambda (authordata)
(let ((contributionNumbers (filter-map (lambda (contribution) (if (and (list? contribution) (equal? contributionType (car contribution))) (cadr contribution) #f)) (cdr authordata)))
(authorId (car authordata)))
(if (null? contributionNumbers) #f (cons authorId contributionNumbers))
)) authors)
)
(string-append (ly:output-def-lookup layout 'poetAndComposerEqualPrefix) " " (default-author-format authorId)))
(define (numbered-contribution-prefix contributionNumbers prefixLookup)
(string-append
@ -52,7 +61,7 @@
(define (format-authors authorIds)
(map (lambda (authorId)
(format-author
(default-author-format
authorId
(if (member authorId referencedAuthors)
#t
@ -68,22 +77,20 @@
(string-append contributionPrefix " " (string-join (format-authors authorIds) ", ")))
)
(define (join-present items joiner)
(string-join (filter (lambda (item) (and (string? item) (not (string-null? (string-trim-both item))))) items) joiner)
)
(define (render-partial-contribution-group prefixLookup authorData)
(if (null? authorData)
""
(let ((firstAuthorContributions (cdar authorData)))
(receive (authorDataSame authorDataOther)
(partition (lambda (authorEntry) (equal? (cdr authorEntry) firstAuthorContributions)) authorData)
(string-append
(join-present (list
(render-contribution-group (numbered-contribution-prefix firstAuthorContributions prefixLookup) (map car authorDataSame))
" "
(render-partial-contribution-group prefixLookup authorDataOther)
))))
)
(define (join-present items joiner)
(string-join (filter (lambda (item) (and (string? item) (not (string-null? (string-trim-both item))))) items) joiner)
)
) " ")))))
(define (poet-and-composer-from-authors authors)
(if authors
@ -95,14 +102,16 @@
(verseComposerData (find-author-id-with-part-numbers 'meloverse authors))
(voiceComposerData (find-author-id-with-part-numbers 'voice authors))
(compositionIds (find-author-ids-by 'composition authors))
(adaptionIds (find-author-ids-by 'adaption authors))
(bridgeIds (find-author-ids-by 'bridge authors))
(interludeIds (find-author-ids-by 'interlude authors))
(year_text (chain-assoc-get 'header:year_text props #f))
(year_translation (chain-assoc-get 'header:year_translation props #f))
(year_melody (chain-assoc-get 'header:year_melody props #f))
(year_composition (chain-assoc-get 'header:year_composition props #f))
(year_adaption (chain-assoc-get 'header:year_adaption props #f))
)
(if (and (equal? poetIds composerIds) (null? translatorIds) (null? versePoetData) (null? verseComposerData) (null? voiceComposerData) (null? compositionIds) (null? bridgeIds) (null? interludeIds))
(if (and (equal? poetIds composerIds) (null? translatorIds) (null? versePoetData) (null? verseComposerData) (null? voiceComposerData) (null? compositionIds) (null? adaptionIds) (null? bridgeIds) (null? interludeIds))
(list
(join-present (list
(render-contribution-group (ly:output-def-lookup layout 'poetAndComposerEqualPrefix) poetIds)
@ -126,7 +135,7 @@
) ", ")
) "; ")
))
(if (and (null? composerIds) (null? compositionIds) (null? verseComposerData) (null? voiceComposerData) (null? bridgeIds) (null? interludeIds)) #f
(if (and (null? composerIds) (null? compositionIds) (null? adaptionIds) (null? verseComposerData) (null? voiceComposerData) (null? bridgeIds) (null? interludeIds)) #f
(string-append
(ly:output-def-lookup layout 'composerPrefix)
" "
@ -141,6 +150,10 @@
(render-contribution-group (ly:output-def-lookup layout 'compositionPrefix) compositionIds)
year_composition
) ", ")
(join-present (list
(render-contribution-group (ly:output-def-lookup layout 'adaptionPrefix) adaptionIds)
year_adaption
) ", ")
(render-contribution-group (ly:output-def-lookup layout 'bridgePrefix) bridgeIds)
(render-contribution-group (ly:output-def-lookup layout 'interludePrefix) interludeIds)
) "; ")
@ -213,14 +226,14 @@
oddFooterMarkup = \markup {
\fill-line {
\line { \null }
\line { \general-align #Y #DOWN \fractional-line-width \print-songinfo }
\line { \if \on-last-page-of-part \general-align #Y #DOWN \fractional-line-width \print-songinfo }
\line { \if \should-print-page-number \print-pagenumber }
}
}
evenFooterMarkup = \markup {
\fill-line {
\line { \if \should-print-page-number \print-pagenumber }
\line { \general-align #Y #DOWN \fractional-line-width \print-songinfo }
\line { \if \on-last-page-of-part \general-align #Y #DOWN \fractional-line-width \print-songinfo }
\line { \null }
}
}

7
include_from_song.ily Normal file
View File

@ -0,0 +1,7 @@
includeFromSong =
#(define-void-function (filename) (string?)
(let ((noDefaultOutputBackup noDefaultOutput))
(set! noDefaultOutput #t)
(ly:parser-parse-string (ly:parser-clone)
(ly:format "\\include \"~a\"" filename))
(set! noDefaultOutput noDefaultOutputBackup)))

View File

@ -6,3 +6,8 @@ inline-score =
#{
\transposable #TRANSPOSITION #music
#})
fill-midi =
#(define-void-function (music) (ly:music?)
(set! INLINESCOREMUSIC #{ \INLINESCOREMUSIC #music #})
)

22
scm/resolve_inherits.scm Normal file
View File

@ -0,0 +1,22 @@
(define (resolve-inherit-entry-in-list alist entry)
(let* ((key (car entry))
(attributes (cdr entry))
(inherits (assoc-ref attributes "inherits")))
(if inherits
(let* ((alist-without-entry (alist-delete key alist))
(inherit-entry (assoc inherits alist-without-entry))
(inherits-attributes (if inherit-entry (alist-copy (cdr (resolve-inherit-entry-in-list alist-without-entry inherit-entry)))))
(override-attributes (alist-delete "inherits" attributes)))
(if inherit-entry
(begin
(for-each (lambda (attribute) (assoc-set! inherits-attributes (car attribute) (cdr attribute))) override-attributes)
(cons key inherits-attributes)
)
(ly:error "~a can not inherit from ~a" key inherits))
)
entry
)))
(define (resolve-inherits alist)
(map (lambda (entry) (resolve-inherit-entry-in-list alist entry)) alist)
)

25
scm/yaml_parser.scm Normal file
View File

@ -0,0 +1,25 @@
(if (not windows?) (use-modules (ice-9 popen)))
(use-modules (ice-9 textual-ports) (json parser))
; We use Python to convert the data yamls like the authors.yml to json, that we can parse in scheme.
; Windows does not like Pipes, so we use a tmpfile instead.
; Be sure you have PyYAML installed. On Windows that could be done for example like "py -m pip install PyYAML"
(define (yml-file->scm filename)
(if windows?
(let* ((port (make-tmpfile #f))
(tmpfilepath (port-filename port))
(ignore (close-port port))
(python_code (string-append "import sys, yaml, json; f = open(r'" tmpfilepath "', 'w'); f.write(json.dumps(yaml.safe_load(open(r'" filename "')))); f.close()"))
(status (system (string-append (search-executable '("python3" "python" "py")) " -X utf8 -c \"" python_code "\"")))
(readport (open-file tmpfilepath "r" #:encoding "UTF-8"))
(json (get-string-all readport)))
(close-port readport)
(delete-file tmpfilepath)
(json-string->scm (if (status:exit-val status) json "{}")))
(let* ((python_code (string-append "import sys, yaml, json; print(json.dumps(yaml.safe_load(open(r'" filename "'))))"))
(pipe (open-pipe (string-append "PYTHONHOME='' " (search-executable '("python3" "python" "py")) " -X utf8 -c \"" python_code "\"") OPEN_READ))
(json (get-string-all pipe)))
(close-pipe pipe)
(json-string->scm json))))
(define (parse-yml-file filename) (resolve-inherits (yml-file->scm filename)))

View File

@ -3,6 +3,3 @@
#(define noDefaultOutput (if (defined? 'noDefaultOutput) noDefaultOutput #f))
\include #(if noDefaultOutput "void.ly" "all_base_includes.ly")
#(define AUTHOR_DATA (if (defined? 'AUTHOR_DATA) AUTHOR_DATA (call-with-input-file "../data/authors.json" json->scm)))
#(define SONG_DATA (if (defined? 'SONG_DATA) SONG_DATA (call-with-input-file "../data/songs.json" json->scm)))

View File

@ -10,6 +10,14 @@ verselayout = \layout {
\override ChordName.font-size = \songTextChordFontSize
}
}
LAYOUT = \layout {
\LAYOUT
#(let
((custom-size (ly:output-def-lookup LAYOUT 'size #f)))
(if custom-size (layout-set-staff-size custom-size)))
}
TEXT = \markuplist {
\override #`(transposition . ,TRANSPOSITION)
\override #`(verselayout . ,verselayout)
@ -18,37 +26,63 @@ TEXT = \markuplist {
\TEXT
}
% nur Output wenn noStandaloneOutput auf false steht
output = #(if (not noStandaloneOutput)
#{
\bookpart {
\HEADER
#(define TEXT_PAGES
(map
(lambda (text) #{
\markuplist {
\override #`(transposition . ,TRANSPOSITION)
\override #`(verselayout . ,verselayout)
\override #`(verse-chords . ,#{ \chords { \verseChords } #})
\override #`(verse-reference-voice . ,#{ \global \firstVoice #})
#text
}
#})
(if
(and
(defined? 'TEXT_PAGES)
(pair? TEXT_PAGES))
TEXT_PAGES
(list TEXT))))
#(define (add-text-pages text-pages)
(if (pair? text-pages)
(begin
(add-score (car text-pages))
(for-each
(lambda (text)
(add-music (pageBreak))
(add-score text))
(cdr text-pages)))))
#(if (not noStandaloneOutput)
(begin
(let ((header (ly:book-header HEADER)) (paper (ly:book-paper HEADER)))
(if header (set! $defaultheader header))
(if paper (set! $defaultpaper paper))
)
(add-score #{
\score {
\MUSIC
\layout { \LAYOUT }
}
\TEXT
}#})
(add-text-pages TEXT_PAGES)
(add-score #{
\score {
\unfoldRepeats { \MUSIC \INLINESCOREMUSIC }
\midi {
\context {
\Score
% Tempo des midi files
tempoWholesPerMinute = #(ly:make-moment midiQuarterNoteSpeed 4)
tempoWholesPerMinute = #(/ midiQuarterNoteSpeed 4)
}
\context {
\Staff
\remove "Staff_performer"
}
\context {
\Voice
\consists "Staff_performer"
}
}
}
}
#}
)
% if we don't want a standalone output, cause we compile a book, we just have an empty output here,
% so lilypond does not generate output for this song
\book {
\bookpart { \output }
}
}#})
))

View File

@ -45,4 +45,77 @@ swing = \mark \markup {
}
}
}
}
}
swingOff = \mark \markup {
\line \general-align #Y #DOWN {
\score {
\new Staff \with {
fontSize = #-2
\override StaffSymbol.line-count = #0
% \override VerticalAxisGroup.Y-extent = #'(0 . 0)
}
\relative {
\stemUp
\override Score.SpacingSpanner.common-shortest-duration = #(ly:make-moment 3 16)
\override Beam.positions = #'(2 . 2)
h'8[ h8]
}
\layout {
ragged-right= ##t
indent = 0
\context {
\Staff \remove "Clef_engraver"
\remove "Time_signature_engraver"
}
}
}
" ="
\score {
\new Staff \with {
fontSize = #-2
\override StaffSymbol.line-count = #0
% \override VerticalAxisGroup.Y-extent = #'(0 . 0)
}
\relative {
\stemUp
\override Score.SpacingSpanner.common-shortest-duration = #(ly:make-moment 3 16)
\override Beam.positions = #'(2 . 2)
h'8 [h8]
}
\layout {
ragged-right= ##t
indent = 0
\context {
\Staff
\remove "Clef_engraver"
\remove "Time_signature_engraver"
}
}
}
}
}
\include "swing.ly"
swingMusic =
#(define-music-function (parser location music) (ly:music?)
(define (partial-duration-length m)
(let ((name (ly:music-property m 'name))
(es (ly:music-property m 'elements))
(e (ly:music-property m 'element)))
(if (pair? es)
(partial-duration-length (car es))
(if (ly:music? e)
(if (and (eq? name 'ContextSpeccedMusic) (eq? (ly:music-property e 'name) 'PartialSet))
(ly:duration->moment (ly:music-property e 'duration))
(if (eq? name 'NoteEvent)
ZERO-MOMENT
(partial-duration-length e)
)
)
ZERO-MOMENT))))
#{
\swing
\applySwingWithOffset 8 #'(2 1) #(partial-duration-length music) #music
#})

View File

@ -41,22 +41,20 @@
(ly:make-stencil
(list 'embedded-ps
(ly:format
"[/Action /GoTo /View [/Fit] /Title (~a) /OUT pdfmark" (pdf-encode title)))
"[/Action /GoTo /View [/XYZ -4 currentpagedevice /PageSize get 1 get 4 add null] /Title (~a) /OUT pdfmark" (pdf-encode title)))
empty-interval empty-interval
;'(0 . 0) '(0 . 0)
))
#(define-markup-command (title-with-category-images layout props right)(boolean?)
(interpret-markup layout props
(let* ((title (chain-assoc-get 'header:title props #f))
(pdfbookmark (chain-assoc-get 'header:songfilename props title)))
(let* ((title (chain-assoc-get 'header:title props ""))
(starttext (chain-assoc-get 'header:starttext props #f))
(pdfbookmark (if starttext (string-append starttext " | " title) title)))
(if title
;(if (chain-assoc-get 'header:categories props #f)
(if right
#{\markup { \title-to-pdf-toc #pdfbookmark \fill-line \general-align #Y #UP { \null \bookTitleMarkupCustom \category-images } } #}
#{\markup { \title-to-pdf-toc #pdfbookmark \fill-line \general-align #Y #UP { \category-images \bookTitleMarkupCustom \null } } #})
;#{\markup \fill-line \general-align #Y #UP { \null \bookTitleMarkupCustom \null } #})
;(make-null-markup))
#{ \markup { " " } #})
)))

View File

@ -67,13 +67,14 @@
(ly:string-substitute "…" ""
(ly:string-substitute "Č" "C"
(ly:string-substitute "Đ" "D"
(ly:string-substitute "Š" "S"
(ly:string-substitute "Т" "T"
(ly:string-substitute "Ä" "Ae"
(ly:string-substitute "ä" "ae"
(ly:string-substitute "Ö" "O"
(ly:string-substitute "ö" "oe"
(ly:string-substitute "Ü" "U"
(ly:string-substitute "ü" "ue" sorttext))))))))))))
(ly:string-substitute "ü" "ue" sorttext)))))))))))))
index-item-list))
(make-music 'EventChord
'page-marker #t
@ -114,7 +115,33 @@
(set! category-index-items (lambda ()
(append-map (lambda (kv) (reverse (hashq-ref category-index-hash (car kv) (list)))) category-names))))
% code for author index
#(define*-public (add-author-index-item! authorIDs markup-symbol text #:optional label) #f)
#(define-public (author-index-items) #f)
#(let ((author-index-hash (make-hash-table)))
(set! add-author-index-item!
(lambda* (authorIDs markup-symbol text #:optional (label (gensym "index")))
(for-each (lambda (authorID)
(let* ((authorsym (string->symbol authorID))
(authorlist (hashq-ref author-index-hash authorsym
(list (list label 'indexAuthorMarkup authorID)))))
(hashq-set! author-index-hash authorsym
(cons (list label markup-symbol text) authorlist))
))
authorIDs)
(make-music 'EventChord
'page-marker #t
'page-label label
'elements (list (make-music 'LabelEvent
'page-label label)))))
(set! author-index-items (lambda ()
(append-map cdr
(sort-list
(hash-map->list
(lambda (authorsym authorlist) (cons (string-downcase (symbol->string authorsym)) (reverse authorlist)))
author-index-hash)
(lambda (a b) (string-ci<? (car a) (car b))))))))
#(define-markup-command (with-link-symbol-ref layout props symbol arg)
@ -140,16 +167,13 @@
(markup #:override (cons 'baseline-skip 3.5) (if catname (make-left-column-markup (string-split (cadr catname) #\newline)) category)))))
#(define-markup-command (index-item-with-pattern layout props)()
(let (
(let* (
(text (chain-assoc-get 'index:text props))
(page (chain-assoc-get 'index:page props))
(width (-
(chain-assoc-get 'line-width props)
(interval-length (ly:stencil-extent (interpret-markup layout props "XXXX") X))))
)
(interpret-markup layout props
(make-column-markup
(let ((revlist
(lines-reversed
(if (markup? text)
(list text)
(reverse (map (lambda (stil) (markup #:stencil stil))
@ -158,17 +182,25 @@
(list (cons 'line-width width) (cons 'font-shape 'italic))
(list (cons 'line-width width))) props) #f
(chain-assoc-get 'rawtext text))))))
(target-size-markup
(last-line-with-dots (make-fill-with-pattern-markup 1 RIGHT "." (car lines-reversed) page))
(lines-without-dots (cdr lines-reversed))
(target-line-size-markup
(make-column-markup
(list
(make-simple-markup "Agj")
(make-vspace-markup 0.2))))
)
(reverse (map (lambda (m)
(make-size-box-to-box-markup #f #t m target-size-markup))
(cons
(make-with-link-symbol-ref-markup 'index:label (make-fill-with-pattern-markup 1 RIGHT "." (car revlist) page))
(cdr revlist)))))))))
)
(interpret-markup layout props
(make-size-box-to-box-markup #f #t
(make-with-link-symbol-ref-markup 'index:label
(make-column-markup
(reverse (cons
last-line-with-dots
(map (lambda (m) (make-size-box-to-box-markup #f #t m target-line-size-markup)) lines-without-dots)))))
; this column is just to have a reference height for resizing
(make-column-markup
(reverse (map (lambda (m) (make-size-box-to-box-markup #f #t m target-line-size-markup)) (cons last-line-with-dots lines-without-dots))))
))))
\paper {
indexItemMarkup = \markup {
@ -182,7 +214,16 @@
\fill-line { \line { \vcenter \category-image-symbol-ref #7 #'index:text \hspace #3 \vcenter \sans \bold \fontsize #3 \category-name-symbol-ref #'index:text } \null }
\vspace #.4
}
indexAuthorMarkup = \markup \override #'(baseline-skip . 1.5) \left-column {
\vspace #1
\sans \bold \fontsize #3
#(make-on-the-fly-markup
(lambda (layout props m)
(interpret-markup layout props
(make-justify-string-markup (format-author (ly:output-def-lookup layout 'authorFormat) (chain-assoc-get 'index:text props #f) #t))))
(make-null-markup))
\vspace #.4
}
}
#(define (prepare-item-markup items layout)
@ -200,7 +241,8 @@
( _i "Outputs index alphabetical sorted or in categories" )
(let ((items (case index-type
((alphabetical) index-items)
((categories) category-index-items)))
((categories) category-index-items)
((authors) author-index-items)))
(title (interpret-markup layout props title-markup)))
(cons title
(interpret-markup-list layout props
@ -219,29 +261,176 @@ indexSection =
"Add a section line to the alphabetical index, using @code{indexSectionMarkup} paper variable markup. This can be used to divide the alphabetical index into different sections, for example one section for each first letter."
(add-index-item! 'indexSectionMarkup text sorttext))
#(define (extract-var-from-module module sym)
(let ((variableref (assoc-ref module sym)))
(if variableref (variable-ref variableref) #f))
)
#(define (extract-and-check-vars-from-header bookheader varlist)
(let* ((headervars (hash-map->list cons (struct-ref (ly:book-header bookheader) 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)))
headerToTOC = #(define-music-function (parser location header label) (ly:book? symbol?)
(define (all-author-ids authors)
(let ((poetIds (find-author-ids-by 'text authors))
(translatorIds (find-author-ids-by 'translation authors))
(versePoetData (find-author-id-with-part-numbers 'verse authors))
(composerIds (find-author-ids-by 'melody authors))
(verseComposerData (find-author-id-with-part-numbers 'meloverse authors))
(voiceComposerData (find-author-id-with-part-numbers 'voice authors))
(compositionIds (find-author-ids-by 'composition authors))
(adaptionIds (find-author-ids-by 'adaption authors))
(bridgeIds (find-author-ids-by 'bridge authors))
(interludeIds (find-author-ids-by 'interlude authors)))
(delete-duplicates
(append poetIds translatorIds (map car versePoetData) composerIds (map car verseComposerData) (map car voiceComposerData) compositionIds adaptionIds bridgeIds interludeIds))
))
(let*
(
(headervars (hash-map->list cons (struct-ref (ly:book-header header) 0)))
(extract-var-and-check (lambda (headervar) (let
((extracted (extract-var-from-module headervars headervar)))
(if (and extracted (not (string-null? extracted))) extracted #f)
)))
(title (extract-var-and-check 'title))
(alttitle (extract-var-and-check 'alttitle))
(altalttitle (extract-var-and-check 'altalttitle))
(categorytitle (extract-var-and-check 'categorytitle))
(categories (extract-var-and-check 'categories))
(extractedheadervars (extract-and-check-vars-from-header header '(title starttext alttitle categorytitle categories authors)))
(title (assq-ref extractedheadervars 'title))
(starttext (assq-ref extractedheadervars 'starttext))
(alttitle (assq-ref extractedheadervars 'alttitle))
(categorytitle (assq-ref extractedheadervars 'categorytitle))
(categories (assq-ref extractedheadervars 'categories))
(authors (assq-ref extractedheadervars 'authors))
(add-to-toc! (lambda (toctitle tocmarkup)
(add-index-item! 'indexItemMarkup tocmarkup toctitle label)))
)
(if categories (add-category-index-item! (string-tokenize categories) 'indexItemMarkup (cons (list (cons 'rawtext (if categorytitle categorytitle title))) '()) label))
(if alttitle (add-to-toc! alttitle (cons (list (cons 'rawtext alttitle) (cons 'alternative #t)) '())))
(if altalttitle (add-to-toc! altalttitle (cons (list (cons 'rawtext altalttitle) (cons 'alternative #t)) '())))
(if authors (add-author-index-item! (all-author-ids authors) 'indexItemMarkup (cons (list (cons 'rawtext (if categorytitle categorytitle title))) '()) label))
(if starttext (add-to-toc! starttext (cons (list (cons 'rawtext starttext) (cons 'alternative #t)) '())))
(if alttitle
(if (list? alttitle)
(for-each (lambda (alt)
(add-to-toc! alt (cons (list (cons 'rawtext alt) (cons 'alternative #t)) '())))
alttitle)
(add-to-toc! alttitle (cons (list (cons 'rawtext alttitle) (cons 'alternative #t)) '()))))
(if title (add-to-toc! title (cons (list (cons 'rawtext title)) '())) #{ #})
))
%% https://github.com/NalaGinrut/guile-csv/blob/master/csv/csv.scm
#(define* (sxml->csv sxml port #:key (delimiter #\,))
(let* ((d (string delimiter))
(csv (map (lambda (l) (string-join l d)) sxml)))
(for-each (lambda (l)
(format port "~a~%" l))
csv)))
#(define csv-write sxml->csv)
#(define-markup-command (write-toc-csv layout props) ()
(define (csv-escape field)
(if (string-null? field)
field
(string-append
"\""
(ly:string-substitute "\n" "\\n"
(ly:string-substitute "\"" "\\\"" field))
"\"")))
(define (format-authors authorIds)
(string-join (map (lambda (authorId) (format-author (ly:output-def-lookup layout 'authorFormat) authorId #f)) authorIds) ", "))
(define cr-regex (ly:make-regex "\r"))
(define crlf-regex (ly:make-regex "\r\n"))
(define para-sep-regex (ly:make-regex "\n[ \t\n]*\n[ \t\n]*"))
(define whitespace-regex (ly:make-regex "[ \t\n]+"))
(define leading-whitespace-regex (ly:make-regex "^[ \t\n]+"))
(define trailing-whitespace-regex (ly:make-regex "[ \t\n]+$"))
(define (cleanup-whitespaces str)
(ly:regex-replace leading-whitespace-regex
(ly:regex-replace trailing-whitespace-regex
(ly:regex-replace whitespace-regex str " ")
"")
""))
(define (format-info-paragraphs text)
(let* ((para-strings (ly:regex-split
para-sep-regex
(ly:regex-replace
cr-regex
(ly:regex-replace crlf-regex text "\n")
"\n")))
(para-lines (map cleanup-whitespaces para-strings)))
(string-join para-lines "\n")))
(define (generate-toc-csv labelPageTable)
(let ((song-lines (map (lambda (song)
(let* ((filename (symbol->string (car song)))
(songvars (cdr song))
(page-number (number->string (assoc-get (assq-ref songvars 'label) labelPageTable)))
(extractedheadervars (extract-and-check-vars-from-header (assq-ref songvars 'header)
'(title starttext alttitle categorytitle categories authors year_text year_melody year_translation year_composition year_adaption infotext translation pronunciation copyright source)))
(headervar-or-empty (lambda (varsym)
(let ((extracted (assq-ref extractedheadervars varsym)))
(if extracted extracted ""))))
(authors (assq-ref extractedheadervars 'authors))
(poetIds (find-author-ids-by 'text authors))
(translatorIds (find-author-ids-by 'translation authors))
(versePoetData (find-author-id-with-part-numbers 'verse authors))
(composerIds (find-author-ids-by 'melody authors))
(verseComposerData (find-author-id-with-part-numbers 'meloverse authors))
(voiceComposerData (find-author-id-with-part-numbers 'voice authors))
(compositionIds (find-author-ids-by 'composition authors))
(adaptionIds (find-author-ids-by 'adaption authors))
(bridgeIds (find-author-ids-by 'bridge authors))
(interludeIds (find-author-ids-by 'interlude authors)))
(map csv-escape
(list
filename
page-number
(headervar-or-empty 'title)
(headervar-or-empty 'starttext)
(let ((alttitle-value (headervar-or-empty 'alttitle)))
(if (list? alttitle-value)
(string-join alttitle-value ", ") ; Wenn eine Liste, dann zusammenfügen
alttitle-value)) ; Wenn kein Liste, den originalen Wert verwenden
(headervar-or-empty 'categorytitle)
(headervar-or-empty 'categories)
(format-authors (append poetIds (map car versePoetData)))
(format-authors translatorIds)
(format-authors (append composerIds compositionIds adaptionIds bridgeIds interludeIds (map car voiceComposerData) (map car verseComposerData)))
(headervar-or-empty 'year_text)
(headervar-or-empty 'year_melody)
(headervar-or-empty 'year_translation)
(headervar-or-empty 'year_composition)
(headervar-or-empty 'year_adaption)
(headervar-or-empty 'copyright)
(headervar-or-empty 'source)
(format-info-paragraphs (headervar-or-empty 'infotext))
(format-info-paragraphs (headervar-or-empty 'translation))
(format-info-paragraphs (headervar-or-empty 'pronunciation))
))))
(alist-delete 'imagePage (alist-delete 'emptyPage song-list)))))
(call-with-output-file "toc.csv"
(lambda (port)
(csv-write (cons '(
"filename"
"page-number"
"title"
"starttext"
"alttitle"
"categorytitle"
"categories"
"poets"
"translators"
"composers"
"year_text"
"year_melody"
"year_translation"
"year_composition"
"year_adaption"
"copyright"
"source"
"infotext"
"translation"
"pronunciation"
) song-lines) port))
)))
; we use a delayed stencil to have all the page references available
(ly:make-stencil
`(delay-stencil-evaluation
,(delay (let* ((table (ly:output-def-lookup layout 'label-page-table)))
(generate-toc-csv (if (list? table) table '()))
empty-stencil)))))

View File

@ -221,14 +221,28 @@
(cons (car x) (+ (cdr x) amount))
y)))
#(define-markup-command (score-equal-height layout props reference-height lines)
(number? markup-list?)
#(define-markup-command (score-equal-height-with-indents layout props lines)
(markup-list?)
#:category music
#:properties ((baseline-skip))
(stack-stencils Y DOWN baseline-skip
(map
(lambda (line) (ly:make-stencil (ly:stencil-expr line) (ly:stencil-extent line X) `(,(/ reference-height -2.0) . ,(/ reference-height 2.0))))
(interpret-markup-list layout props lines))))
#:properties ((intraverse-vspace 0)
(verse-line-height songTextLineHeigth)
(line-indents '()))
(let ((indents-max-index (- (length line-indents) 1)))
(stack-stencils Y DOWN intraverse-vspace
(index-map
(lambda (index line)
(let ((stil
(ly:make-stencil
(ly:stencil-expr line)
(ly:stencil-extent line X)
`(,(/ verse-line-height -2.0) . ,(/ verse-line-height 2.0)))))
(if (<= index indents-max-index)
(ly:stencil-translate-axis
stil
(list-ref line-indents index)
X)
stil)))
(interpret-markup-list layout props lines)))))
#(define-public (custom-lyric-text::print grob)
"Allow interpretation of tildes as lyric tieing marks."
@ -239,30 +253,136 @@
(make-pad-right-markup -0.1 (make-tied-lyric-markup text))
text))))
Chord_lyrics_spacing_engraver =
#(lambda (ctx)
(let ((last-note-head #f)
(note-head-extended #f)
(last-lyric-syllable-width 0)
(lyric-width-since-last-chord 0)
(notes-on-syllable-count 0)
(last-chord-name #f)
(remaining-chord-width 0)
(last-rest #f)
(rest-count 0)
(multi-measure-rest-count 0)
(stanza-shift 0))
(make-engraver
(listeners
((multi-measure-rest-event engraver event)
(set! multi-measure-rest-count (+ multi-measure-rest-count 1))
)
((break-event engraver event)
(set! last-note-head #f)
(set! note-head-extended #f)
(set! last-lyric-syllable-width 0)
(set! lyric-width-since-last-chord 0)
(set! notes-on-syllable-count 0)
(set! last-chord-name #f)
(set! remaining-chord-width 0)
(set! last-rest #f)
(set! rest-count 0)
(set! multi-measure-rest-count 0)
(set! stanza-shift 0)
))
(acknowledgers
((note-head-interface this-engraver grob source-engraver)
(if (and (> rest-count 0) (not last-note-head))
(let ((rest-spacing-on-line-start 1.2))
(ly:grob-set-property! grob 'minimum-X-extent (cons (- rest-spacing-on-line-start) 0))
(set! stanza-shift rest-spacing-on-line-start)
))
(set! notes-on-syllable-count (+ 1 notes-on-syllable-count))
(set! last-note-head grob)
(set! note-head-extended #f)
(set! last-rest #f)
(set! rest-count 0)
(set! multi-measure-rest-count 0)
)
((lyric-syllable-interface this-engraver grob source-engraver)
(set! remaining-chord-width (max 0 (- remaining-chord-width lyric-width-since-last-chord)))
(set! last-lyric-syllable-width (- (cdr (ly:grob-extent grob grob X)) 0.2))
(set! lyric-width-since-last-chord (+ lyric-width-since-last-chord last-lyric-syllable-width))
(if last-note-head (set! notes-on-syllable-count 1))
)
((chord-name-interface this-engraver grob source-engraver)
(if (not (and
(boolean? (ly:grob-property grob 'begin-of-line-visible))
(ly:grob-property grob 'begin-of-line-visible)))
(let ((on-a-rest (> rest-count 0)))
(if (not on-a-rest)
(set! notes-on-syllable-count (- notes-on-syllable-count 1)))
(if (and last-chord-name (= multi-measure-rest-count 1) (> lyric-width-since-last-chord remaining-chord-width))
(ly:grob-set-property! last-chord-name 'extra-spacing-width (cons -0.1 (+ 0.1 (- lyric-width-since-last-chord remaining-chord-width)))))
(if last-note-head
(let* ((last-note-min-x-extent (ly:grob-property last-note-head 'minimum-X-extent))
(last-note-min-x-lower (if (pair? last-note-min-x-extent) (car last-note-min-x-extent) 0))
(last-note-min-x-upper (if (pair? last-note-min-x-extent) (cdr last-note-min-x-extent) 0)))
(if on-a-rest
(begin
(if (not note-head-extended)
(begin
(ly:grob-set-property! last-note-head 'minimum-X-extent
(cons last-note-min-x-lower (- last-lyric-syllable-width -2 (* 2.2 rest-count))))
(set! note-head-extended #t)
))
(ly:grob-set-property! last-rest 'minimum-X-extent (cons 0 2))
)
(if (and (> lyric-width-since-last-chord 0)
(> remaining-chord-width lyric-width-since-last-chord))
(ly:grob-set-property! last-note-head 'minimum-X-extent
(cons (- -1.2 (- remaining-chord-width lyric-width-since-last-chord)) last-note-min-x-upper))
(let* ((width-per-note-head 0.5)
(note-width-since-last-chord (* width-per-note-head notes-on-syllable-count)))
(if (> remaining-chord-width note-width-since-last-chord)
(ly:grob-set-property! last-note-head 'minimum-X-extent
(cons (- note-width-since-last-chord remaining-chord-width) last-note-min-x-upper))
)
)
)
)))
(set! last-chord-name grob)
(set! remaining-chord-width
(if (and on-a-rest (equal? (ly:prob-property (ly:grob-property grob 'cause) 'duration) (ly:prob-property (ly:grob-property last-rest 'cause) 'duration)))
0
(cdr (ly:grob-extent grob grob X))))
(set! lyric-width-since-last-chord 0)
(set! notes-on-syllable-count (if on-a-rest 0 1))
))
)
((rest-interface this-engraver grob source-engraver)
(set! rest-count (+ 1 rest-count))
(set! last-rest grob)
(set! multi-measure-rest-count 0)
)
((stanza-number-interface this-engraver grob source-engraver)
(ly:grob-set-property! grob 'padding (+ 1 stanza-shift)))
))))
%#(ly:set-option 'debug-skylines #t)
#(define-markup-command (chordlyrics layout props lyrics) (ly:music?)
#:properties ((verse-chords #{#})
(verse-reference-voice #{#})
(verse-break-voice #{#})
(verse-line-height songTextLineHeigth)
(verse-text-chord-distance songTextChordDistance)
(intraverse-vspace 0)
(transposition (cons #f #f))
(verselayout generalLayout))
"Vers mit Akkorden"
(interpret-markup layout props
#{
\markup {
\override #`(baseline-skip . ,intraverse-vspace)
\score-equal-height #verse-line-height \score-lines {
\transposable #transposition
\score-equal-height-with-indents \score-lines {
<<
\new Devnull { #verse-break-voice }
\new NullVoice = "dummyvoice" { #verse-reference-voice }
#(music-clone verse-chords)
\new Devnull { #(music-clone verse-break-voice) }
\new NullVoice = "dummyvoice" { #(music-clone verse-reference-voice) }
\transposable #transposition #(music-clone verse-chords)
\new Lyrics \lyricsto "dummyvoice" { #lyrics }
>>
\layout {
\verselayout
#(let
((custom-size (ly:output-def-lookup verselayout 'size #f)))
(if custom-size (layout-set-staff-size custom-size)))
ragged-right = ##t
\context {
\Lyrics
@ -277,6 +397,7 @@
\ChordNames
\override VerticalAxisGroup.staff-affinity = ##f
\override ChordName.extra-spacing-width = #'(-0.1 . 0.1)
\consists \Ensure_first_chord_after_line_break_printed_engraver
}
\context {
\Score
@ -284,10 +405,14 @@
% \override SpacingSpanner.strict-note-spacing = ##t
\override SpacingSpanner.uniform-stretching = ##t
\override SpacingSpanner.spacing-increment = 0
%\override SpacingSpanner.packed-spacing = ##t
\consists \Chord_lyrics_spacing_engraver
\remove Bar_number_engraver
\remove Mark_engraver
\remove Jump_engraver
\remove Volta_engraver
\remove Parenthesis_engraver
\remove Metronome_mark_engraver
}
\context {
\Staff
@ -314,8 +439,10 @@
\NullVoice
\consists Rest_engraver
\omit Rest
% \undo \omit NoteHead
% \hide NoteHead
\override Rest.X-extent = #'(0 . 0)
\undo \omit NoteHead
\hide NoteHead
\override NoteHead.X-extent = #'(0 . 0.5)
}
}
}