I've heard of lilypond1 for a while now and never gave it much more than a passing look. It seemed pretty cool, but I just never really had a need for it (I barely interact with sheet music, much less publish any).
Thinking it could get handy if I ever write another music article which involve writing down some tab2, I started to think now might be the time to finally look into it.
A reasonable workflow would be to simply use lilypond to generate the tablature / sheet music I want as an image and include that in the article, but I'd rather be able to type the source right in my markdown post and have it generate the image on the fly. I'm lazy, and switching gears annoys me fast (I'm slowly getting used to taking the time to resize regular images before I publish something, but I still loathe it). I don't want to add another step to my workflow, especially not for something I might want to edit as I go.
I started to look for a markdown extension that could let me integrate it with pelican, and was suprised to find very little3. I found some stuff to highlight lilypond code, which is not what I want (although ironically it would be handy for this very post), and found an article about doing something similar, but with different tools.
So i thought, how hard can it be to just write this myself ?
So here we go. I can type this (most of it is straight from the lilypond docs):
!!!
\paper {
page-breaking = #ly:one-page-breaking
make-footer=##f
}
upper = \relative c' {
\time 12/8
\key e \minor
\voiceOne
r4. r8 e, fis g16 b g e e' b c b a g fis e
}
lower = \relative c {
\key e \minor
\voiceTwo
r16 e d c b a g4 fis8 e fis g a b c
}
\score {
<<
\new StaffGroup = "tab with traditional" <<
\new Staff = "guitar traditional" <<
\clef "treble_8"
\new Voice = "upper" \upper
\new Voice = "lower" \lower
>>
\new TabStaff = "guitar tab" <<
\new TabVoice = "upper" \upper
\new TabVoice = "lower" \lower
>>
>>
>>
}
!!!
And get this on the generated page:
So hurray, it works.
It's far from done, though. I'll probably throw the code on github when it is, but right now it's an awful mess, so I'll keep it to myself until I clean things up.
Shenanigans
The overall process was pretty straightforwerd, but I got some troubles with
python's ElementTree
library (which I use to parse the generated svg code
and insert it in the page html) which made a mess because of the way it handles
XML namespaces.
I barely know anything about those, and was not interested in the slightest in learning more about them. I wasted a couple hours on this, and came up with several solutions (including skipping messing with the XML tree altogether and just spit out the raw source), but I'm still not sure which one I actually want.
This pissed me off enough to stop here for today, though, so I'll look into that later.
Integrating with lilypond was dead easy, though.4 I kinda wish it could write its output to stdout, which mould save me from writing a temporary file, but hey, that's not too bad.
Roadmap to 0.1
As I said, there's quite a bit left to do for this to be more than a quick hack and turn into something potentially useful:
-
cleanup the code and decide how I want to handle the xml shenanigans I mentioned above.
-
Decide on a sane syntax for the markdown blocks for lily-processing.
I've used simple!!!
delimiters from the markdown docs example, but this is bound to conflict with something else. This shouldn't be too hard, but better to think a bit and be reasonably certain I don't pick something that might already be taken by something else. -
Which output format should I use ?
Right now I'm generating svg, but png could be just as nice. I don't know. Making this a config option seems easy enough, but I can already see some of the complexity handling several formats will cause. And whetever I can see right now is probably just the tip of the iceberg. -
What to do with the generated file ?
Due to the way lilypond works, I have to dump its output in a temporary file which I then read to stick its content straigth in animg
tag's source attribute. I could see keeping the file around being handy though, especially if switching to png. But then, how do I know what to name the generated file and where to save it ? What if I'm using this several times on one page ? This will probably require some way to pass options right from the markdown block, and involve parsing those. -
lilypond (or maybe this is just the regular svg format ? I barely know anything about that one either) seems to include some color information, which is nice, but breaks when I switch the color theme. I added a quick css hack to handle this for now, but I'll have to see if we can get rid of this information (png file should not have this problem, though).
-
Right now I'm having to include a directive to tell lilypond not to output a full page5, which it does by default. This is annoying, as I'm probably gonna use this for short snippets and I'm likely to forget the syntax, so I'm tempted to insert this directive automatically. This may not be what a potential user expects, though, so do I add another option for this ? And if I do, how to I handle cases were several pages are generated ?
-
I'm already noticing pelican is being slower at generating the blog. I should cache the output and avoid regenerating the whole thing when the source hasn't changed since the last time.
This seems like the easiest issue to handle right now, so that's probably the first one I'll do.
Most of those points aren't really technical, but rather part of the overall tool's design. Designing a good API is hard enough to begin with, but it's even worse when dealing with stuff I'm not really used to, and yet more when I'm not even sure what I want yet. This is making lean quite hard on the "keep things as simple as possible" approach. So this extension is probably not gonna be very configurable for a while.
First impressions on Lilypond
Honeslty ? I don't know. I barely looked at the source I used to test the code. My thinking is to make sure the general idea works, and learn to use it later.
It seems pretty complete, though. Maybe too complete for what I need. But hey, I figure learning the basics can't hurt. I can always ditch it if I find some simpler tool.
A quick digression about Markdown extensions
For some reason I've been enjoying writing markdown extensions quite a bit over the last two weeks. So far I did:
-
An extension to generate a link to a django model, using its
get_absolute_url
. It's cute and was fun to do, but I predict I won't have any real use for it. Oh well. -
Another one to generate an
hx-ref
attribute on all links. Dead simple and useful if dealing with htmx. -
Yet another one to automatically add some atributes to HTML elements. This one is on github, and I'm using it on this blog to autoamtically add a
loading="true"
attribute to all images. -
I'm thinking of another one to auto generate a slugified title for headings. Addding those manually is exactly the kind of tedious crap I'd rather avoid.
And now this lilypond thing.
I don't know why, but something about parsing fascinates me. I barely get to insert some code in the middle of the general tree processing here, but I dunno, this feels nice to do. It's easy, but it make me feel like I understand something about how languages work.
The python-markdown's API to write extensions is pretty simple, too, so that's nice.
Alright, off to go grab a drink now. I'll make an effort to clean up the code tomorow, but after that I'll probably try and use the damn thing and learn more about lilypond before I get any further. I'll post another article if this thing gets nice enough to share.
So long, interwebs!
-
Hi, Flo! ↩
-
I got away in that one with using simple code blocks and writing the tabs in raw ASCII, but this could get unwieldy and hard to read for anything more complex. ↩
-
This might be due to just how awful search engines have become... I'll spare you the rant about trying to find some technical info on pretty basic problems, which I run into every other day. ↩
-
"Integrating" is kind of a big word here. I'm just calling the CLI tool as a subprocess with the right options. ↩
-
The
\paper
lines in the example source above. ↩