This is where we talk about how to setup a large score project for getting real-world work done, synthesizing all of our previous information about how Python, Abjad and LilyPond work together.
Key points to keep in mind during this discussion.
LilyPond can concatenate like-named contexts in adjacent music expressions. That's a mouthful. Let's look at an example:
\score {
{
\new Staff = "My Staff" { c'4 d'4 e'4 f'4 }
\new Staff = "My Staff" { g'4 a'4 b'4 c''4 }
}
}
The above - as far as LilyPond is concerned - is the equivalent of:
\score {
{
\new Staff = "My Staff" { c'4 d'4 e'4 f'4 g'4 a'4 b'4 c''4 }
}
}
Why? Because the two adjacent Staff
expressions both have the same name. Context concatenation is the main reason why we'll be using a ScoreTemplate
class to generate the empty score we later fill in with music. By using the same class to generate the score skeleton, we guarantee that every context in the score is named correctly.
When you make settings in a LilyPond file (including any files included at any point in that file) those settings override previous settings. This is somewhat analogous to how CSS works. For example, if you set the title variable in a \header
block at the beginning of the file, you can override that title later without losing the other settings in the original header block.
Consider the following:
\header {
composer = "Best Composer"
title = "My Great Piece"
subtitle = "A Very Fine Gigue"
}
\header {
title = "My Adequate Piece"
}
\header {
subtitle = "Visions of a Somber World"
}
At the end, the effective header settings are:
\header {
composer = "Best Composer"
title = "My Adequate Piece"
subtitle = "Visions of a Somber World"
}
We can use LilyPond's \tag
and \keepWithTag
commands to label parts of our score and then keep only parts with certain labels when rendering a score. If we combine that with LilyPond's \book
block functionality, we can use a single LilyPond input file to generate one PDF per musician's part in our score. Easy.
We'll see this in action in the interaction between the ScoreTemplate
class, the segments.ily
and parts.ily
files in the build/
directory, and the parts.ly
file in the build/letter-portrait/
directory.
Consider the following LilyPond:
{
\tag #'foo { c'4 d'4 e'4 f'4 }
\tag #'bar { g'4 a'4 b'4 c''4 }
}
Let's extract out only the music tagged foo
:
\keepWithTag #'foo
{
\tag #'foo { c'4 d'4 e'4 f'4 }
\tag #'bar { g'4 a'4 b'4 c''4 }
}
The above is actually equivalent to:
{
{ c'4 d'4 e'4 f'4 }
}
We can use this same principle to extract out the cello part from a string quarter!
Depending on the complexity of the operations you're performing in Python with Abjad to create a score object, it might take a while to generate a chunk of score. If you only need to tweak the length of your stems, why go through that trouble of re-computing the whole score?
Even though Abjad affords you the ability to override typographic settings, sometimes it's easier to just write those settings out - especially if they're global settings - in an external LilyPond file and then include that file.
Then, making global typographic changes is as simple as editing your external stylesheet and re-rendering (not re-computing) the previously rendederd illustration.
If scores are built from code, let's structure them just like we would structure any other code: as an installable package.
Note: We'll use the command-line tool tree
to pretty-print directory structures. The -a
flag means "show hidden files". The -F
flag causes tree
to append special characters like /
to indicate directories. The -L 1
flag tells tree
to only drill down one level deep. You can install tree
on OSX via HomeBrew.
In [2]:
!tree -a -F -L 1 trio_score/
In [3]:
!tree -a -F -L 1 trio_score/trio_score/
Materials are objects that exist "outside-of-time". In practice, this generally means configured classes, e.g. rhythm-makers, that you intend to use in various parts of your music. Typically, materials can be illustrated: the material's class implements Abjad's illustration protocol via the __illustrate__()
method.
In [4]:
!tree -F -L 1 trio_score/trio_score/materials/
In [5]:
!tree -F -L 1 trio_score/trio_score/materials/my_fast_rhythm_maker/
In [6]:
!cat trio_score/trio_score/materials/my_fast_rhythm_maker/definition.py
What's that strange block at the bottom of the material definition.py
? The conditional expression if __name__ == 'main':
tells Python to execute the following suite if and only if the module is being executed as a script. That means, by running this definition file via python definition.py
, we'll illustrate the material defined in the module, render that illustration as a PDF, and then pop open that PDF.
In [7]:
!tree -F trio_score/trio_score/tools/
In [8]:
!cat trio_score/trio_score/tools/ScoreTemplate.py
In [9]:
!cat trio_score/trio_score/tools/SegmentMaker.py
A segment is a contiguous block of temporal compositional concern. Basically, a chunk of music.
We've established that we can concatenate score together. That means we can concatenate the output of segments together.
But why? There's no reason you have to work with more than one segment. But, with experience, there's only so many details a human can focus on at once. As the contents of your segment become increasingly complicated, you'll have trouble reasoning about what's happening in your segment. If your segment is very long, you may also experience long waits for Abjad and LilyPond to do their work.
By splitting a score into at least a few segments, you both ease your cognitive burden and also speed up the cycle of composing and illustrating.
In [10]:
!tree -F -L 1 trio_score/trio_score/segments/
In [11]:
!tree -F -L 1 trio_score/trio_score/segments/section_one/
What's inside a segment definition? A configured instance of the SegmentMaker
class from our tools/
directory:
In [12]:
!cat trio_score/trio_score/segments/section_one/definition.py
Note that we have the same if __name__ == 'main':
block here. The segment definition is illustratable just like the material definitions before.
In [13]:
!tree -F trio_score/trio_score/stylesheets/
In [14]:
!cat trio_score/trio_score/stylesheets/stylesheet.ily
In [15]:
!cat trio_score/trio_score/stylesheets/parts.ily
In [17]:
!tree -F trio_score/trio_score/builds/
In [18]:
!cat trio_score/trio_score/builds/segments.ily
In [19]:
!cat trio_score/trio_score/builds/parts.ily
In [21]:
!tree -F trio_score/trio_score/builds/letter-portrait/
There are three other directories in our score project: distribution/
, etc/
and test/
.
We use etc/
to keep any notes or plans about the piece as we're writing it.
We use test/
to house any tests for our score - typically just illustrating every material and segment in the project and checking that nothing broke during the process.
Finally, we use distribution/
for housing the PDFs that are ready to be zipped-up and mailed out to any performer or ensemble who wants to play our music.
In [22]:
!tree -F trio_score/trio_score/distribution/
In [23]:
!tree -F trio_score/trio_score/etc/
In [24]:
!tree -F trio_score/trio_score/test/
In [25]:
!tree -F trio_score/
In [ ]: