Difference between revisions of "LilyPond"

From Wiki
Jump to navigation Jump to search
(add link to t-filter docs)
Line 153: Line 153:
  
 
== Example ==
 
== Example ==
 
(TODO: This is old and might contain deprecated or non-working code. Unchecked.)
 
  
 
Here's an example of placing score snippets in the body of the text, with fonts in the score & body matching:
 
Here's an example of placing score snippets in the body of the text, with fonts in the score & body matching:
  
 
<texcode>
 
<texcode>
\unprotect
+
\def\LILYTEMP{lilytemp} % name of folder for LilyPond/buffer files
  
\usemodule[filter]
+
\def\ParseLilypondFile#1% #1 is the name of the output file
 +
  {\ctxlua{thirddata.parselilypondfile("#1")}}
  
\traceexternalfilters
+
\startluacode
 +
thirddata = thirddata or {}
  
\defineexternalfilter
+
-- create temp folder if missing
  [lilypond]
+
if not lfs.isdir("\LILYTEMP") then
  [\c!output=\externalfilterbasefile.pdf,
+
   lfs.mkdir("\LILYTEMP")
  \c!filtercommand=\lilypondcommand,
+
end
   \c!continue=\v!yes,
 
  \c!readcommand=\readlilypondoutput,
 
  %\c!directory=output,
 
  ]
 
  
% frame=on is for testing
+
function thirddata.parselilypondfile(name)
\def\readlilypondoutput#1{\setupfloats[location=right,frame=off]\placefigure[]{From \sc{http://lsr.dsi.unimi.it/LSR/Search?q=piano}}{\externalfigure[#1]}}
+
  -- include all systems (pages)
 +
  -- name is like \LILYTEMP/mainfile-temp-lilypond-21.pdf
 +
  logs.report("LILYPOND","name='" .. name .. "'")
 +
  local scname = string.gsub(name, '%.pdf$', '-systems.count')
 +
  local syco = tonumber(io.loaddata(scname)) or 0
  
\def\lilypondcommand%
+
  for nr = 1, syco do
{lilypond -dbackend=eps -dno-gs-load-fonts -dinclude-eps-fonts \externalfilterinputfile}
+
    logs.report("LILYPOND","including system no." .. nr)
\protect
+
    context("\\setupfloats[location=right,frame=off]\\placefigure[none]{}{\\externalfigure[" .. string.gsub(name, '%.pdf$', '-' .. nr) .. "]}")
 +
  end
 +
end
 +
\stopluacode
  
\setuplayout[textwidth=6in] % matches line-width below
+
\usemodule[filter]
\usetypescript[palatino]
+
\defineexternalfilter[lilypond]
\setupbodyfont[palatino,13pt]
+
  [continue=yes,
 +
        cache=yes,
 +
  readcommand=\ParseLilypondFile,
 +
  directory=\LILYTEMP/,
 +
  output={\externalfilterbasefile.pdf},
 +
  filtercommand={lilypond -dbackend=eps -dinclude-eps-fonts -dno-gs-load-fonts -o"\LILYTEMP/\externalfilterbasefile" "\externalfilterinputfile"}]
 +
 
 +
\setuplayout[textwidth=6in] % matches line-width below
 +
\definefontfeature[main][protrusion=quality, expansion=quality]
 +
\definefontfamily[mainface][rm][Adobe Jenson Pro][features=main]
 +
\setupbodyfont[mainface,13pt]
 +
\setupalign[hz,hanging]
  
 
\starttext
 
\starttext
  
\input zapf  
+
\input zapf
  
 
\startlilypond
 
\startlilypond
 +
 
\layout{
 
\layout{
 
   indent=0\mm
 
   indent=0\mm
Line 195: Line 210:
 
}
 
}
 
\paper  {
 
\paper  {
myStaffSize = #20
+
myStaffSize = #20
#(define fonts
+
  #(define fonts
  (make-pango-font-tree "palatino"
+
    (make-pango-font-tree "Adobe Jenson Pro"
"palatino"
+
                          "Myriad Pro"
"palatino"
+
                          "Myriad Pro"
              (/ myStaffSize 20)))  
+
(/ myStaffSize 20)))
  line-width=6\in
+
line-width=6\in
 
   oddFooterMarkup=##f
 
   oddFooterMarkup=##f
 
   oddHeaderMarkup=##f
 
   oddHeaderMarkup=##f
Line 207: Line 222:
 
   scoreTitleMarkup = ##f
 
   scoreTitleMarkup = ##f
 
  }
 
  }
global = {
+
melody = \relative c'' {
 +
  \clef treble
 
   \key c \major
 
   \key c \major
 
   \time 4/4
 
   \time 4/4
 +
 +
  a b c d
 
}
 
}
  
sopMusic = \relative c'' {
+
text = \lyricmode {
  c4 c c8[( b)] c4
+
Aaa Bee Cee Dee
}
 
sopWords = \lyricmode {
 
  hi hi hi hi
 
 
}
 
}
  
altoMusic = \relative c' {
+
upper = \relative c'' {
   e4 f d e
+
   \clef treble
}
+
  \key c \major
altoWords =\lyricmode {
+
  \time 2/4
   ha ha ha ha
+
 
 +
   a4 b c d
 
}
 
}
  
tenorMusic = \relative c' {
+
lower = \relative c {
   g4 a f g
+
   \clef bass
}
+
  \key c \major
tenorWords = \lyricmode {
+
   \time 2/4
   hu hu hu hu
 
}
 
  
bassMusic = \relative c {
+
   a2 c
   c4 c g c
 
}
 
bassWords = \lyricmode {
 
  ho ho ho hø
 
 
}
 
}
  
 
\score {
 
\score {
 
   <<
 
   <<
     \new ChoirStaff <<
+
     \new Voice = "mel" { \autoBeamOff \melody }
      \new Lyrics = "sopranos"
+
    \new Lyrics \lyricsto mel \text
      \new Staff = "women" <<
 
        \new Voice = "sopranos" { \voiceOne << \global \sopMusic >> }
 
        \new Voice = "altos" { \voiceTwo << \global \altoMusic >> }
 
      >>
 
      \new Lyrics = "altos"
 
      \new Lyrics = "tenors"
 
      \new Staff = "men" <<
 
        \clef bass
 
        \new Voice = "tenors" { \voiceOne << \global \tenorMusic >> }
 
        \new Voice = "basses" { \voiceTwo << \global \bassMusic >> }
 
      >>
 
      \new Lyrics = "basses"
 
      \context Lyrics = "sopranos" \lyricsto "sopranos" \sopWords
 
      \context Lyrics = "altos" \lyricsto "altos" \altoWords
 
      \context Lyrics = "tenors" \lyricsto "tenors" \tenorWords
 
      \context Lyrics = "basses" \lyricsto "basses" \bassWords
 
    >>
 
 
     \new PianoStaff <<
 
     \new PianoStaff <<
       \new Staff <<
+
       \new Staff = "upper" \upper
        \set Staff.printPartCombineTexts = ##f
+
       \new Staff = "lower" \lower
        \partcombine
 
        << \global \sopMusic >>
 
        << \global \altoMusic >>
 
      >>
 
       \new Staff <<
 
        \clef bass
 
        \set Staff.printPartCombineTexts = ##f
 
        \partcombine
 
        << \global \tenorMusic >>
 
        << \global \bassMusic >>
 
      >>
 
 
     >>
 
     >>
 
   >>
 
   >>
 +
  \layout {
 +
    \context { \Staff \RemoveEmptyStaves }
 +
  }
 
}
 
}
 +
 
\stoplilypond
 
\stoplilypond
\input zapf
+
\input tufte
 +
 
 
\stoptext
 
\stoptext
 
</texcode>
 
</texcode>

Revision as of 22:21, 15 June 2014

< Graphics | Using Graphics >

LilyPond is a great music engraver, and you can include LilyPond in ConTeXt source using the filter module with some setup.

Simple Filter Setup

This works with ConTeXt MkII and MkIV, but takes only the first page of multi-pages scores, and you must create the folder "lilytemp" manually:

\def\readPDFfile#1{\externalfigure[#1]}

\usemodule[filter]
\defineexternalfilter[lilypond]
	[continue=yes,
	readcommand=\readPDFfile,
	directory=lilytemp/, % directory for LilyPond's files
	output={\externalfilterbasefile.pdf},
	filtercommand={lilypond -dbackend=eps -dno-gs-load-fonts -dinclude-eps-fonts -ddelete-intermediate-files -o"lilytemp/\externalfilterbasefile" "\externalfilterinputfile"}]

Multi Page Filter Setup

This uses Lua and therefore only works with ConTeXt MkIV. It includes all pages of multi-page scores. It doesn’t look into the complete (multi-page) PDF, but reads a "-system.count" auxiliary file written by LilyPond that contains the number of systems (pages) and includes the single-system PDFs.

\def\LILYTEMP{lilytemp} % name of folder for LilyPond/buffer files

\def\ParseLilypondFile#1% #1 is the name of the output file
  {\ctxlua{thirddata.parselilypondfile("#1")}}

\startluacode
 thirddata = thirddata or {}
 
 -- create temp folder if missing
 if not lfs.isdir("\LILYTEMP") then
   lfs.mkdir("\LILYTEMP")
 end
 
 function thirddata.parselilypondfile(name)
   -- include all systems (pages)
   -- name is like \LILYTEMP/mainfile-temp-lilypond-21.pdf
   logs.report("LILYPOND","name='" .. name .. "'")
   local scname = string.gsub(name, '%.pdf$', '-systems.count')
   local syco = tonumber(io.loaddata(scname)) or 0
   
   for nr = 1, syco do
     logs.report("LILYPOND","including system no." .. nr)
     context("\\externalfigure[" .. string.gsub(name, '%.pdf$', '-' .. nr) .. "]")
   end
 end
\stopluacode

\usemodule[filter]
\defineexternalfilter[lilypond]
	[continue=yes,
        cache=yes,
	readcommand=\ParseLilypondFile,
	directory=\LILYTEMP/,
	output={\externalfilterbasefile.pdf},
	filtercommand={lilypond -dbackend=eps -dinclude-eps-fonts -dno-gs-load-fonts -o"\LILYTEMP/\externalfilterbasefile" "\externalfilterinputfile"}]


LilyPond Settings

Collect your LilyPond settings in a .ly file, put it in your lilytemp directory and include it from within your lilypond block like this:

\startlilypond
\include "mysettings.ly"
...
\stoplilypond

You can avoid this \include line following filter module documentation, section "Prepend and append text".

Sample Include File

"mysettings.ly" could look like this:

\version "2.16.0"
#(ly:set-option (quote no-point-and-click))
#(set-global-staff-size 14)

% --- start of setup for single-line output files ---
#(define default-toplevel-book-handler
  print-book-with-defaults-as-systems )

#(define toplevel-book-handler
  (lambda ( . rest)
  (set! output-empty-score-list #f)
  (apply print-book-with-defaults rest)))

#(define toplevel-music-handler
  (lambda ( . rest)
   (apply collect-music-for-book rest)))

#(define toplevel-score-handler
  (lambda ( . rest)
   (apply collect-scores-for-book rest)))

#(define toplevel-text-handler
  (lambda ( . rest)
   (apply collect-scores-for-book rest)))

#(set! output-empty-score-list #t)

% --- stop single-line setup ---

\paper {
	#(define dump-extents #t)
	indent = 0\mm
	ragged-bottom = ##t
	ragged-last-bottom = ##t
	print-page-number = ##f
	line-width = 120\mm
	oddFooterMarkup  = ##f
	oddHeaderMarkup  = ##f
	bookTitleMarkup  = ##f
	scoreTitleMarkup = ##f
}

\layout {
	#(layout-set-staff-size 14) % beware, this resets fonts!
	% set fonts for rm / ss / tt 
	#(define fonts (make-pango-font-tree "TeX Gyre Schola" "LMSans10" "LMTypewriter10 Regular" (/ 14 20)))
 	\context { \Score
		\remove "Bar_number_engraver"
		\override PaperColumn #'keep-inside-line = ##t
 	}
	\context { \Staff
  		\override TimeSignature #'style = #'numbered
 	}
	\context { \ChordNames
		chordChanges = ##t
		majorSevenSymbol = \markup{ 7+ }
	}
}

Please look up the meaning of settings in LilyPond’s great documentation!

Unfortunately it’s not possible to exchange measures like text width between ConTeXt and LilyPond (at least I’ve no idea how), so you must define sizes manually. And you can’t define LilyPond’s text (lyrics) size with an absolute value, but only relative to staff size...

Named Buffers

Normally, your LilyPond snippets just get a running number. If you re-order your scores, each one gets re-rendered.

You might want to add [name=\currentcomponent] (or name=mymusic) to \startlilypond to avoid unnecessary re-rendering.

Example

Here's an example of placing score snippets in the body of the text, with fonts in the score & body matching:

\def\LILYTEMP{lilytemp} % name of folder for LilyPond/buffer files

\def\ParseLilypondFile#1% #1 is the name of the output file
  {\ctxlua{thirddata.parselilypondfile("#1")}}

\startluacode
 thirddata = thirddata or {}

 -- create temp folder if missing
 if not lfs.isdir("\LILYTEMP") then
   lfs.mkdir("\LILYTEMP")
 end

 function thirddata.parselilypondfile(name)
   -- include all systems (pages)
   -- name is like \LILYTEMP/mainfile-temp-lilypond-21.pdf
   logs.report("LILYPOND","name='" .. name .. "'")
   local scname = string.gsub(name, '%.pdf$', '-systems.count')
   local syco = tonumber(io.loaddata(scname)) or 0

   for nr = 1, syco do
     logs.report("LILYPOND","including system no." .. nr)
     context("\\setupfloats[location=right,frame=off]\\placefigure[none]{}{\\externalfigure[" .. string.gsub(name, '%.pdf$', '-' .. nr) .. "]}")
   end
 end
\stopluacode

\usemodule[filter]
\defineexternalfilter[lilypond]
  [continue=yes,
        cache=yes,
  readcommand=\ParseLilypondFile,
  directory=\LILYTEMP/,
  output={\externalfilterbasefile.pdf},
  filtercommand={lilypond -dbackend=eps -dinclude-eps-fonts -dno-gs-load-fonts -o"\LILYTEMP/\externalfilterbasefile" "\externalfilterinputfile"}]

 \setuplayout[textwidth=6in] % matches line-width below
\definefontfeature[main][protrusion=quality, expansion=quality]
\definefontfamily[mainface][rm][Adobe Jenson Pro][features=main]
\setupbodyfont[mainface,13pt]
\setupalign[hz,hanging]

\starttext

\input zapf

\startlilypond

\layout{
  indent=0\mm
  ragged-right = ##f
}
\paper  {
myStaffSize = #20
  #(define fonts
    (make-pango-font-tree "Adobe Jenson Pro"
                          "Myriad Pro"
                          "Myriad Pro"
(/ myStaffSize 20)))
line-width=6\in
  oddFooterMarkup=##f
  oddHeaderMarkup=##f
  bookTitleMarkup = ##f
  scoreTitleMarkup = ##f
 }
melody = \relative c'' {
  \clef treble
  \key c \major
  \time 4/4

  a b c d
}

text = \lyricmode {
Aaa Bee Cee Dee
}

upper = \relative c'' {
  \clef treble
  \key c \major
  \time 2/4

  a4 b c d
}

lower = \relative c {
  \clef bass
  \key c \major
  \time 2/4

  a2 c
}

\score {
  <<
    \new Voice = "mel" { \autoBeamOff \melody }
    \new Lyrics \lyricsto mel \text
    \new PianoStaff <<
      \new Staff = "upper" \upper
      \new Staff = "lower" \lower
    >>
  >>
  \layout {
    \context { \Staff \RemoveEmptyStaves }
  }
}

\stoplilypond
\input tufte

\stoptext

Too many open files

With bigger projects and the above setup you might run into "too many open files" errors, at least in Linux, OSX and other Unix-like OSes. Ask your shell about the open files maximum and set it higher:

 $ ulimit -n
 256
 ulimit -S -n 2048

Deprecation

The old LilyPond module (t-lilypond) doesn't work any more with recent versions of ConTeXt, therefore we removed its documentation here.