sly.txt and ptfilter.txt contain the entire distribution. (c)2002 David Raleigh Arnold This software is completely free for all under the rules of GNU. The sly program itself contains only 12 lines of working bash code. sly is just a tiny trivial bash script which takes a file with extension .sly and splits it into parts to be read by lilypond in an .ly file by means of includes. To use this, you need bash, gawk and sed. All are available for windows. GNU textutils are nice to have too. sly is not a preprocessor, it is an editing tool, but by using ptfilter.sed or the conversion program of your choice you can do all the preprocessing you can stand. It's usefulness comes from getting the notes out of the .ly file, not from anything that is done to customize syntax. Putting the parts into readable columns is by far the most important use for this program. Actually, it is the only use for sly itself, but by taking the parts out of the .ly file much else becomes possible. While sly is designed entirely for lilypond, it could probably be adapted for any text based music entry where the music can be entered by voices rather than comb fashion. pt1 | pt2 | pt3 etc. pt1 | pt2 | pt3 pt1 | pt2 | pt3 pt1 | pt2 | pt3 etc. So a .sly file is simply a spreadsheet with the fields delineated by " |", a space and a bar, for convenient editing in any editor. You probably want each record (line) to be a measure, but it is not necessary to do it that way. Here is sly. Make it executable and put it in your path. (suggestion: ~/bin) Read the comments for the syntax. ----begin sly executable. cut here -- #!/bin/bash # sly (c)2002David Raleigh Arnold free for all under GNU etc.etc. # syntax: sly infilename outfilename # Two arguments are required for a good outcome, but they can # be the same. infilename file must have .sly extension but # leave it off in the argument. # outfilename will be the name of outfilename-ptn.ly files. # That's what you include in your .ly file. Nothing messes # with your .ly file, unless you put "-pt" in its name, # an unlikely mistake, we hope. # ptfilter.sed must exist in current directory. It can be # empty though, in case you don't want to use it or I have # screwed it up. An empty version is automatically created # if it does not exist. touch ptfilter.sed # clobber old $2-ptn.ly files, and get rid of error message # if there are none such files. rm $2-pt* 2> /dev/null # Get the number of fields in the record with the most. fieldnum=$(gawk 'BEGIN{FS=" \\|"}{OFS=" |"} {print NF}' $1.sly | sort -r | head -n1) count=1 while [ $count -le $fieldnum ] do gawk 'BEGIN {FS=" \\|"}{OFS=" |"}{print $i} ' i=$count $1.sly | sed -f ptfilter.sed $2-pt$count.ly let "count = $count + 1" done ========== end sly cut here If you want to use barchecks, ``|'' is not a field separator. The field separator is `` |'', that is, a space *and* a bar. A ``|'' at the beginning of a line without a preceding space will come through as a barcheck. Use `` ||'' for the rest. An easier way to use barchecks is to perform a matrix transform on the suspect -ptn.ly files with sly-mate. Using sly-mate on a part: 1 1 1 becomes 1 | 1 | 1 and vice-versa. I see no reason why lilypond should choke on a part in one long line. If you use sly-mate on your score: 1 | 2 1 | 2 1 | 2 becomes 1 | 1 | 1 2 | 2 | 2 and vice-versa. Really cool for taking parts out, rearranging their order, or putting in parts. This arrangement gives you tons of flexibility. You could write your string parts and chorus parts in different files. You don't have to use all the files generated, either, so you could use a dummy part to help coordinate things if you don't have lyrics. If you haven't put ptfilter.sed in yet, it was created as a 0 byte file when you ran the previous. It must be in current directory, so you need a separate copy for each project. If the syntax of a lilypond command changes, you shouldn't have to change more than one line anywhere, so define commands as much as possible in .ly, but if passing numbers is involved, ptfilter.sed, so you can give different values note after note to padding or stemLength or.... The advantages of taking the notes out of the .ly file now should be apparent, if you want to repeat or move sections, add fingering, add or remove another part, tweak spacing or collisions, make a midi version, score for different instruments, etc. etc. Those who, like me, have suspended work on large projects because of the difficulty of revising notes in .ly can resume work, and you can finally score that symphony you wrote. An additional luxury is that lilypond will give you the line and column in your -ptn.ly file when it finds errors, so it is super easy to find the offending dot or comma in your score. sly-tidy vertically aligns the field separators by inserting spaces. Syntax: sly-tidy filename sly-mate performs a matrix transform on the file. Syntax: sly-mate filename Both of these simply dump their output to screen or, using emacs-xemacs, there is a way of doing it to a buffer. :-). ---------------begin sly-tidy executable #!/bin/bash # sly-tidy (c)2002 David Raleigh Arnold under GNU etc etc etc. A trivial # utility which lines up the | bars in a file which uses a space followed # by a bar as a field separator. If there is no space after the bar it # inserts one. It gets rid of tabs. It does not affect bars not preceded # by a space. # Usage: sly-tidy filename # Result: dump to console. Redirect, or use in emacs-xemacs buffer. # This is very ugly, but I am no programmer. cp $1 tempfile0 sed ' s/ / /g s/ |/ | /g s/^ *//g s/ *$//g s/ */ /g /^ $/d /^$/d ' tempfile0 > tempfile1 echo "" >> tempfile1 tac tempfile1 | gawk 'BEGIN {FS=" \\|"}{OFS=" |"}{ for(i = 1; i <= NF; i++ ){ while (len[i] < length($i) ){ len[i]++ } while (len[i] > length($i)){ $i = $i " " } } print $0 > "tempfile2" }' tac tempfile2 | gawk 'BEGIN {FS=" \\|"}{OFS=" |"}{ for(i = 1; i <= NF; i++ ){ while (len[i] < length($i) ){ len[i]++ } while (len[i] > length($i)){ $i = $i " " } } print $0 > "tempfile3" }' sed ' /^$/d ' tempfile3 rm tempfile? =========================end sly-tidy executable ------------A two part .sly file after sly-tidy d16 d' f' a' f' d' f' d' a d' f d' | d a f <>4 r4 <>8 <> d16 d' f' a' f' d' f' d' a d' f d' | d a f <>4 r4 <>8 <> d16 d' g' bf' g' d' g' d' bf d' g d' | d bf g <>4 r4 <>8 <> d16 d' g' bf' g' d' g' d' bf d' g d' | d bf g <>4 r4 <>8 <> d16 cs' e' g' e' cs' e' cs' bf cs' g cs' | d bf g <>4 r4 <>8 <> d16 cs' e' g' e' cs' e' cs' bf cs' g cs' | d bf g <>4 r4 <>8 <> d a d' f' d' a d' a f a d a | d f d <>4 r4 <>8 <> c a d' f' d' a d' a f a d a | c f d <>4 r4 <>8 <> bf, a d' f' d' a d' a f a d a | bf, f d <>4 r4 <>8 <> a, a d' f' d' a d' a f a d a | a, f d <>4 r4 <>8 <> gs, b d' f' d' b d' b f b d b | gs, f d <>4 r4 <>8 <> gs, b d' f' d' b d' b gs b e b | gs, gs e <>4 r4 <>8 <> a, b c' e' c' b c' b e b c b | a, e c <>4 r4 <>8 <> a, a c' e' c' a c' a c a a, a | a, c a, <>4 r4 <>8 <> f, c' e' a' e' c' e' c' a c' f c' | f, a f <>4 r4 <>8 <> d b f' a' f' b f' b d b b, b | d d b, <>4 r4 <>8 <> e, b d' gs' d' b d' b e b b, b | e, e b, <>4 r4 <>8 <> e, b d' gs' d' b d' b e b b, b | e, e b, <>4 r4 <>8 <> e, c' e' a' e' c' e' c' e c' c c' | e, e c <>4 r4 <>8 <> e, d' gs' b' gs' d' gs' d' f d' d d' | e, f d <>4 r4 <>8 <> e, e' a' c'' a' e' a' e' a e' e e' | e, a e <>4 r4 <>8 <> e, e' gs' d'' gs' e' gs' e' b e' gs e' | e, b gs <>4 r4 <>8 <> ======================== Just a hair prettier, wouldn't you say? Another way is to run ``column -t infile.sly > outfile.sly'' on the previous. ``column'', ``cut'', ``colrm'', and ``paste'' are GNU textutils. They can shift columns about too. If there is a leading space in any line of the file, the column program will fail, giving a "line too long" message. Bug. ------------------what "column" does: d16 d' f' a' f' d' f' d' a d' f d' | d a f <>4 r4 <>8 <> d16 d' f' a' f' d' f' d' a d' f d' | d a f <>4 r4 <>8 <> d16 d' g' bf' g' d' g' d' bf d' g d' | d bf g <>4 r4 <>8 <> d16 d' g' bf' g' d' g' d' bf d' g d' | d bf g <>4 r4 <>8 <> d16 cs' e' g' e' cs' e' cs' bf cs' g cs' | d bf g <>4 r4 <>8 <> d16 cs' e' g' e' cs' e' cs' bf cs' g cs' | d bf g <>4 r4 <>8 <> d a d' f' d' a d' a f a d a | d f d <>4 r4 <>8 <> c a d' f' d' a d' a f a d a | c f d <>4 r4 <>8 <> bf, a d' f' d' a d' a f a d a | bf, f d <>4 r4 <>8 <> a, a d' f' d' a d' a f a d a | a, f d <>4 r4 <>8 <> gs, b d' f' d' b d' b f b d b | gs, f d <>4 r4 <>8 <> gs, b d' f' d' b d' b gs b e b | gs, gs e <>4 r4 <>8 <> a, b c' e' c' b c' b e b c b | a, e c <>4 r4 <>8 <> a, a c' e' c' a c' a c a a, a | a, c a, <>4 r4 <>8 <> f, c' e' a' e' c' e' c' a c' f c' | f, a f <>4 r4 <>8 <> d b f' a' f' b f' b d b b, b | d d b, <>4 r4 <>8 <> e, b d' gs' d' b d' b e b b, b | e, e b, <>4 r4 <>8 <> e, b d' gs' d' b d' b e b b, b | e, e b, <>4 r4 <>8 <> e, c' e' a' e' c' e' c' e c' c c' | e, e c <>4 r4 <>8 <> e, d' gs' b' gs' d' gs' d' f d' d d' | e, f d <>4 r4 <>8 <> e, e' a' c'' a' e' a' e' a e' e e' | e, a e <>4 r4 <>8 <> e, e' gs' d'' gs' e' gs' e' b e' gs e' | e, b gs <>4 r4 <>8 <> ============================ And at long last, here's sly-mate. Lines all have the same number of fields. To prevent the possibility of data loss, blank fields are added as necessary. ------------ begin sly-mate executable #!/bin/bash if [ $# -ne 1 ] then echo "sly-mate (c)2002 David Raleigh Arnold, under GNU etc etc etc. See" echo "sly.txt for more information." echo " Usage:" $ ./slymate scoreorpartfile > outfile" echo "" exit 0 fi # get the number of fields in the line with the most fields. numfields=$(gawk 'BEGIN{FS=" \\|"}{OFS=" |"}{print NF}' $1 | sort -r | head -n1) let count=1 while [ $count -le $numfields ] do gawk 'BEGIN{FS=" \\|"}{OFS=" |"}{print $i}' i=$count $1 > matrix-tmp sed ' s/ / /g s/^ *//g s/ *$//g :begin $!N s/\n/ | /g t begin s/ |/ | /g s/ */ /g ' matrix-tmp rm matrix-tmp let count=count+1 done ===================end sly-mate executable This is a working example, at least it was. I have changed flowing.ly from the LilyPond tutorial into two files, flowing2.ly and flowing2.sly. This is a very short example, but it has three parts, so it justifies using sly, barely, maybe. By using lyinclude, on my website, you can generate a new .ly file with the \included parts written into the file. A comment % on the same line prevents the writing in. You don't want "english.ly" written in, not that the script could find it anyway. Using lyinclude gives more flexibility than having it a part of sly. lyinclude will hunt down and write in all the included files you might want, not just the ones in the particular .sly file you are processing, and if you have a script like ./up in your local directory it is just as easy to put lyinclude in there to bring up with gv or xdvi whatever .ly you wish as it is to add confusing arguments to the sly command. Here we have flowing2.ly: ---------------------begin flowing2.ly \header { title = "The river is flowing" composer = "Traditonal (?)" } \include "paper16.ly" % the "%" is so lyinclude won't write in. % chordnames: accompaniment = { \context ChordNames \chords { \include "flowing2-pt1.ly" }} % melody: melody = {\context Staff = mel { \property Staff.noAutoBeaming = ##t \property Staff.automaticMelismata = ##t \notes \relative c' \include "flowing2-pt2.ly" }} % lyrics: flolyrics = {\context Lyrics \lyrics { \include "flowing2-pt3.ly" }} \score { \simultaneous { \accompaniment \addlyrics \melody \flolyrics } \midi { } \paper { linewidth = 10.0\cm } } -----------end flowing2.ly and here we have flowing2.sly, the sly score. If you copy this into two files and run ``./sly flowing2 flowing2'', running ly2dvi on either flowing.ly from the tutorial or flowing2.ly will produce the same output. %stets are not necessary unless you use ptfilter.sed.: -----begin flowing2.sly r8 %stet | \partial 8 g8 | The %stet c2:3- f:3-.7 | c4 c8 d [es () d] c4 | ri -- ver is flo- __ wing, d:min es4 c8:min r8 | f4 f8 g [es() d] c g | flo -- wing and gro -- wing, the c2:min f:min7 | c4 c8 d [es () d] c4 | ri -- ver is flo -- wing g:7^3.5 c:min | d4 es8 d c4. \bar "|." | down to the sea. =========end flowing2.sly Troubleshooting: If your ly2dvi output is garbage, check your .ptn parts. If the parts are mixed up, there is really only one possible cause. Search for `` |'' with your editor and make sure you aren't missing any field separators. If ptfilter.sed causes trouble, comment out suspects until you find the guilty, or don't use it, or better yet fix it. Eventually, ptfilter.txt will be chock full of goodies.