A Fine Manual for Sly

sly

The notes in lilypond scores are usually entered in separate voices rather than measure by measure. With sly, you decide the format.

Invoking Sly

Python programs may be invoked slightly differently on different systems, but one of these will be appropriate. Python comes with superb documentation:

    sly file[.sly]
    sly.py file
    python sly file
    python sly.py file

The argument ``file'' is the name of the sly file. For safety, the ``.sly'' extension is required in the file name, but sly will process foobar.sly whether you specify it as foobar.sly or just as foobar.

The Sly File

A sly file contains notes data, but also tells sly what to do with that data. The field separator in the examples following is:

!!

You choose the field separator by making it the first two bytes in the file. I have indented the examples only for appearance's sake. The field separator should stand out on the line, be unusual, and be easy to type. Don't use white space for one of the characters.

    !! example.sly 

The rest of the first line is not written anywhere except to the screen. Some kind of memo there would be good to identify the file.

The list of files which will contain the parts or voices to follow is terminated by an extra field separator, which again must be the first thing in the line. This is the second and last field separator used for a special purpose. Both must be the very first thing on their respective lines. The rest of the field separators do nothing but separate fields, and new lines may be substituted for any of them.

The portion of the sly file described so far is the header of the file:

    !! example.sly
    dummy.ly    !! soprano.ly    !! alto.ly
    tenor.ly    !! bass.ly
    !!

Next come notes data. How many fields on this line?

    !! field 1 !! field 2 !! field 3

Four! Field 0 is empty.

When sly is run on your sly file, it splits the notes among the files that you have listed as if dealing hands for a game of cards. These files will be read by lilypond if you include them in your lilypond source file in this manner:

    \notes {
    \key c \major etc.....
    \include "soprano.ly"
    }

The examples below list five files, so sly will read six fields each cycle, starting with the “field 0”, the empty comment field, until it reaches the end of the file. Whatever is in a comment field will be written to all the destination files, but if the comment field is empty, nothing at all of it will be written. All other empty fields will be written to their destination files as empty lines.

The fields could be measures, parts of measures, or several measures, or something entirely else, because sly just deals fields and lines. It doesn't know anything about measures.

These three examples would create the same output:

    !! example1.sly
    dummy.ly    !! soprano.ly    !! alto.ly
    tenor.ly    !! bass.ly
    !!!! 1 !! soprano notes    !! alto notes
    tenor notes    !! bass notes
    !! 2 !!   soprano notes    !! alto notes
    tenor notes    !! bass notes

    !! example2.sly
    dummy.ly !! soprano.ly !! alto.ly !! tenor.ly !! bass.ly
    !!!! 1 !! soprano notes !! alto notes !! tenor notes !! bass notes
    !! 2 !!  soprano notes !! alto notes !! tenor notes !! bass notes

    !! example3.sly
    dummy.ly
    soprano.ly
    alto.ly
    tenor.ly 
    bass.ly
    !!
     1 
    soprano notes
    alto notes
    tenor notes
    bass notes
    !! 2
    soprano notes
    alto notes
    tenor notes
    bass notes

The headers and notes data are interchangeable between the examples. A new line can act as a field separator too.

Here are the contents of dummy.ly:

    1
    2    

Here are the contents of soprano.ly. The other target files would be similar:

    soprano notes
    soprano notes

Now we come to a great new opportunity to mess up. No comment fields were written by sly previously, because they were empty in the sly files. Here is an example with comments not empty. “%measure 1” is a comment:

    !! example4.sly
    dummy.ly !! soprano.ly !! alto.ly
    tenor.ly !! bass.ly
    !!%measure 1 !! 1 !! soprano notes !! alto notes
    tenor notes !! bass notes
    %measure 2 !! 2 !!  soprano notes !! alto notes
    tenor notes !! bass notes

The new contents of soprano.ly:

    %measure 1
    soprano notes
    %measure 2
    soprano notes

This is no problem until you wish to create a new sly file from parts on disk. The shortcoming of previous versions of sly was really my own, because I was not able to write a program which would build or rebuild a new sly file.

Writesly

Writesly does the reverse of what sly does. When writesly is run on a header, it grabs all the notes data from disk and appends them to the header. Writesly has no way of distinguishing comment from content, so everything goes into the new file, and writesly prepends new empty comment fields to the data fields it appends to the header. Otherwise, the new sly file would have the wrong number of fields and the data would be scrambled. Writesly writes the notes data in the same format as the header to which they are appended. Here is what writesly would give you if it were run on the header ``example.sly'', now ``example5.sly'':

    !! example5.sly
    dummy.ly !! soprano.ly !! alto.ly
    tenor.ly !! bass.ly
    !!!! %measure 1 !! %measure 1 !! %measure 1
    %measure 1 !! %measure 1
    !! 1 !! soprano notes !! alto notes
    tenor notes !! bass notes
    !! %measure 2 !! %measure 2 !! %measure 2
    %measure 2 !! %measure 2 
    !! 2 !!  soprano notes !! alto notes
    tenor notes !! bass notes

``example5.sly'' has no more usable data than any of the previous examples. Of course you could remove all the lines in all the files which had “%measure” in them:

    $ sed -i '/%measure/d' file

It is best to use a dummy file instead of the comment field to track measure numbers, unless you have content destined for every part in your score or until you are done and you are pasting everything into a lilypond source file to put it in storage, to send it to someone else, or to finish editing the source file without using sly. “lyinclude” can write included files into your LilyPond source in short order if you wish to do any of those things.

Creating an Empty Sly File

Save typing by using writesly to create your sly file. Writesly doesn't stop writing when a part file has a white line, it stops when all the parts have white lines. By creating a dummy file and numbering its lines...

    % meas 1
    % meas 2
    % meas 3
    % meas 4

and running writesly on this header...

    !! example6.sly
    dummy.ly !! soprano.ly !! alto.ly
    tenor.ly !! bass.ly  
    !!
You get this:
    !! example6.sly
    dummy.ly !! soprano.ly !! alto.ly
    tenor.ly !! bass.ly  
    !!!! % meas 1 !! !! 
      !!
    !! % meas 2 !! !! 
      !!
    !! % meas 3 !! !! 
      !!
    !! % meas 4 !! !! 
      !!

Automatic Line Numbers and numtxt

To make a new file with numbered lines, try numtxt. It can also write lines with measure rests or anything else, with or without numbering. Numtxt creates a brand new file. Syntax example:

    $ numtxt starting_number step "text " end_number '
    more text' (redirect to file)

    $ numtxt 1 2 "text " 7 '
      more text' > mezzo.ly 

The result will be on screen. When you see what you want, redirect the output to file, and its contents would be:

    text 1
    more text
    text 3
    more text
    text 5
    more text
    text 7
    more text
You can easily copy such a file as every part, include it in your master xxx.ly file, and then be able to keep the parts aligned because all the line numbering was done already. You are better off with a sly file if you intend to move measures about, because you can renumber everything at once by replacing your dummy file.

Back up your sly file often. Sly won't write files from a header without any notes data, but one or two extra bytes and if you run sly instead of writesly that data is gone. That's why sly and writesly are two separate programs.

You may send more than one field at a time to a file by ending the line with a “%”. You may have a particularly long measure or lilypond command which takes several lines, or you might want a workspace in dummy file fields into which to put long sequences for copying:

    !! example7.sly
    dummy.ly !! soprano.ly !! alto.ly !! tenor.ly !! bass.ly
    !!!! 1 !! soprano notes !! alto notes %
    more alto notes %
    more alto notes yet !! tenor notes !! bass notes
    !! 2 !!  soprano notes !! alto notes !! tenor notes !! bass notes %
    more bass notes %
    more bass notes

Content of alto.ly:

    alto notes %
    more alto notes %
    more alto notes yet
    alto notes

Writesly will be able to reconstruct such data correctly.

Using Sed With Sly and Writesly

While the parts are on your disk it is convenient to slice and dice them in all sorts of ways. There may also be cases where you want to work over several parts at once, still in a sly file. Sed stands for “stream editor”, but when sed was developed a stream meant a stream of lines. Here are examples of how to invoke sed:

    $ sed 'command' file > resultfile

    $ sed '{command1       # yes, you can type this at the command line
    command2}' file > resultfile

    $ sed -i 'command' file  # makes new file, original destroyed

You can put commands in a file, which is a simple textfile:

   $ sed -f path/commandfile  file > resultfile

Because the parts and voices on disk amount to temporary files, using sed with the “-i” option is not dangerous as it might be in other circumstances. You can also run sed on a selected region in emacs. The problem with that is that the edit is finalized before you get a chance to see lilypond compile the score, but it's probably good to get started that way. After you have marked a region, type C-u, then A-|. Then enter the command, such as sed -f filename (enter). If it explodes, use undo.

subtimes.sed

A sed script which might well be used to edit a sly file is subtimes.sed. It multiplies instances of a word:

@x7 @2

becomes:

@2 @2 @2 @2 @2 @2 @2

Then it substitutes:

gu gab gibe gobber @1 @2 @2 @2 @2 @2 @2 @2 @3 @4 @3

becomes:

gu gab gab gab gab gab gab gab gibe gobber gibe

Subtimes won't change lines which begin with "%", so you can keep templates handy in a dummy field. You redirect sed's output to another filename and run sly on that to see the music, and save that as your new sly file when you're done. @0 works like the old star substitution.

engkeys.sed

You probably want to use this on individual parts. Put the code for the number of flats or sharps which you want to be automatically tacked on after a comment at the beginning of the part, because it doesn't automatically disappear. It will apply the commands to a line, and to all lines following, no matter where the code is on the line. The code is @k[1-7][bs] where [1-7] is the number of chromatics and ``b'' or ``s'' is flat or sharp. Use an ``n'' to protect a note you don't want to change. You probably would not be interested in saving the edited version of the part file until you were done. This script is very simple. There is no transposition, and notes which have chromatics of any sort already attached are not affected. If you wish to process a few lines at a time, move the code down the page as you go.

User sed scripts

You can use simple substitution to save typing where a lilypond command takes different values when otherwise the whole command would have to be repeated for each value.

Sed has a peculiarity which it is good to know. (not just sed) Let's say you want to substitute ( s ) “-or-” for “-and-”:

    s/-and-/-or-/g

The “g” is for global, meaning every substitution on every line, so that ought to do it, right? Wrong!

    -and-and-and-

would become:

    -or-and-or-

Why? Because sed found -and-, but following that was just “and-”. When sed has found something, it moves on. Now this...

    -and--and--and-

would become:

    -or--or--or-

With dashes it's quite easy to see, but it trips people up when there are spaces instead.

Also because there is no “or” in sed, writing sed scripts to perform substitutions is greatly simplified if a script puts spaces at the beginnings and ends of lines and changes all instances of tabs to spaces and then doubles all spaces.

Even though this is hardly finished,I would welcome suggestions of ways to improve this document.


Home
© 2004 David Raleigh Arnold. All rights reserved. 20040412