Automatic guitar tablature generator, part 1
There seems to be a link between music and computers — that is, it seems to me that a lot of programmers are also (at least amateur) musicians as well. I am, too — I've been playing guitar most of my life. Like many similarly amateur guitarists, I learned to play primarily by reading tablature. Music for virtually every other instrument is written out in a standard form: circles with lines sticking out of the top or bottom on a five-line staff. The position of the circle on the lines indicate which note to play (the pitch) and the appearance of the circle (filled or unfilled) along with the presence or absence of a line coming out of it tell you how long to play that note for. Notes can be marked sharp or flat by prepending an octothorpe/"hash tag" or a stylized lower-case b to the left of the circle.
Unlike the piano, which has exactly one key for each possible note, the guitar allows you to play the same note in many different places along the fretboard. To help the would-be guitarist read the music, the concept of tablature was invented. Underneath the music proper, a diagram of a guitar fretboard was added and the strings were marked with the fret at which ones fingers ought to be positioned to play the note above it. Now, strictly speaking, that tablature was supposed to be a hint — you were expected to be reading the actual music at the same time. A LOT of people (like me), though, realized that if you knew the rhythm of the song pretty well, you could ignore the music part and just read the tablature and work out how to play the song.
In fact, there are online repositories of guitar tablature that only show the fingering positions, printed out in a sort of ASCII art, like the one in figure 1, below.
--------------15----14------
----15----------------------
--------14-12----14----14---
-12-------------------------
----------------------------
----------------------------
One project I've been kicking around for a while is working out how to translate ASCII-art tablature into proper tab, along with the corresponding music notes. As it turns out, it's mostly a math exercise, but sort of an interesting one.
First, I wanted to be able to print out the final result, so I decided to use the open-source iText PDF generation library to render the output. It includes a nice canvas object that allows you to output vector graphics in a reasonably efficient way, which is exactly what i was looking for. Once you have access to that, generating and rendering a blank page of tablature is a simple matter of adding and subtracting. First, though, a little bit of terminology.
Figure 2 illustrates what proper "standard notation" along with its associated tablature should look like. As far as I can tell, the parts of the notation don't have technical names other than "lines" and "gaps". The five lines at the top are where the traditional music notes go and the six lines at the bottom are where you put the fingering positions of the corresponding guitar string. The vertical lines are measure lines; in standard 4/4 time, there are four beats per measure, so four quarter notes fit inside each measure.
To make this a little bit on the configurable side, I start with a set of constants that define the proportions of the individual pieces:
public static float marginTop = 15.0f;
public static float marginLeft = 15.0f;
public static float staffGap = 6.0f; // gap between the lines in the music staff
public static float separatorGap = 20.0f; // gap between the music staff and the tab staff
public static float tabGap = 5.0f; // gap between the lines in the tab staff
public static float barGap = 20.0f; // gap between each complete combined staff
public static float clefWidth = 20.0F;
In example 1, I've defined some white space along the top and left, and left 6 pixels between the standard notation lines and 5 between the tablature lines (why 6? As it turns out, I need this to be an even number to line up the notes correctly; I'll come back to this later). The tablature is separated from the standard notation staff by 20 pixels and then each tab/staff combination is separated by another 20. I came up with these proportions by experimentation as well as looking at other people's musical notation; as far as I can tell, there are no standard proportions here.
The only other concept in example 1 that I haven't touched on yet is that I set aside some horizontal space in the staff for the treble clef which is a sort of ampersand-looking symbol that indicates that the first, or lowest, note, on the staff is an E note.
Creating a PDF document with iText is pretty straightforward:
Document document = new Document();
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("blanktab.pdf"));
document.open();
PdfContentByte canvas = writer.getDirectContent();
Rectangle pageSize = document.getPageSize();
canvas.setColorStroke(BaseColor.BLACK);
canvas.setLineWidth(1.0F);
If you've experimented at all with the HTML5 canvas object, this concept should be familiar to you; but even if you aren't, it's pretty self explanatory. You can stroke or fill lines, rectangles, text and ellipses (circles). Now that I've done this, I can work out how many lines of tablature I can print on a single page, assuming four measures per line.
float staffWidth = pageSize.getWidth() - (2 * marginLeft);
float staffHeight = (staffGap * 5) + separatorGap + (tabGap * 5);
// Assume four measures per tab line, apportion them out evenly
float measureWidth = (staffWidth - clefWidth) / 4.0F;
float noteWidth = measureWidth / 8.0F; // leave room for eight eighth-notes per measure
// Figure out how many bars will fit comfortably on this page
int barCount = (int) (pageSize.getHeight() /
((staffGap * 5) + separatorGap + (tabGap * 5) + barGap));
And finally, I move down the page barCount
times, outputting a complete bar across
the entire page each time. This process tripped me up a couple of times because iText has the
y coordinate of 0 starting in the lower left (like in a standard Cartesian coordinate system)
instead of the upper left which I'm more accustomed to, so I kept having to remind myself
to switch my additions to subtractions and vice versa, but otherwise, it's pretty
straightforward.
float y = pageSize.getHeight() - marginTop;
float x = marginLeft;
// The bottom of the tab occurs every staffHeight + barGap pixels.
for (int i = 0; i < barCount; i++) {
// Start by drawing all the vertical lines
// Left bar; connects staff to tab
canvas.moveTo(x, y);
canvas.lineTo(x, y - staffHeight);
// Four measure separators, evenly distributed
for (int j = 1; j < 4; j++) {
canvas.moveTo(x + clefWidth + (measureWidth * j), y);
canvas.lineTo(x + clefWidth + (measureWidth * j), y - (staffGap * 4));
canvas.moveTo(x + clefWidth + (measureWidth * j), y - ((staffGap * 5) + separatorGap));
canvas.lineTo(x + clefWidth + (measureWidth * j), y - staffHeight);
}
// Draw the last one exactly on the end
canvas.moveTo(x + staffWidth, y);
canvas.lineTo(x + staffWidth, y - (staffGap * 4));
canvas.moveTo(x + staffWidth, y - ((staffGap * 5) + separatorGap));
canvas.lineTo(x + staffWidth, y - staffHeight);
// Now output the music staff; five lines that cross the entire page, minus the horizontal
// padding
for (int j = 0; j < 5; j++) {
canvas.moveTo(x, y);
canvas.lineTo(x + staffWidth, y);
y -= staffGap; // I move y just a hair too far here. Rather than back up, I just account
// for it in the multiplications above
}
y -= separatorGap;
// Finally, output the tablature lines: six, one for each string on a standard guitar
for (int j = 0; j < 6; j++) {
canvas.moveTo(x, y);
canvas.lineTo(x + staffWidth, y);
y -= tabGap;
}
y -= barGap;
}
At this point, the lines have all been defined, but not output to the actual open PDF document. To write them, I stroke them to the canvas, and close the document:
canvas.stroke();
document.close();
And voila! A page of blank tablature to fill out.
Of course, there's not much reason to go to all this trouble when you can just download blank tablature pages for free. It becomes much more interesting if you go ahead and provide a means to include the actual tablature and notes along with it. I'll come back to this in part two of this series.