9 Ways to NRT, part 3: Generating piano rolls with NRT, PostScript, and ImageMagick


Welcome to Part 3 of 9 Ways to NRT

Today, I'll be explaining how NRT notation can be used to generate piano rolls.

In other words,


"d4. r8 m s t4 s m r2 m4 d2."

will output this:

A few words on PostScript

PostScript is something I've known about for quite a while, but I never thought much about the file format until I read this guy's blog post in which he created generated scatterplot graphs using postscript.

Postscript files are easy to generate because it's just text, and the format is pretty standard. I liked the fact that I didn't need to install any special libraries or software to view postscript files that I generated.

Because PostScript is just text, just about any programming language could be used to generate PostScript files. For consistency, I decided to use Awk, but the language is pretty arbitrary (while writing this in Awk, I realized I could have done a cleaner job in Perl).

The Awk Script

Here is the Awk script that generates the PostScript:

File gen_ps.awk:

printf "%!PS\n%%BoundingBox: 0 0 %d %d\n\
1 dict begin\n\
/y1 exch def\n\
/x1 exch def\n\
/width exch def\n\
/height 1 def\n\
/x2 x1 width add def\n\
/y2 y1 height add def\n\
newpath \n\
x1 y1 moveto\n\
x1 y2 lineto\n\
x2 y2 lineto\n\
x2 y1 lineto\n\
} def\n\
0.176470588235294 0.243137254901961 0.282352941176471 setrgbcolor\n\
%d %d scale\n\
0 0 moveto\n\
0 1 lineto\n\
1 1 lineto\n\
1 0 lineto\n\
0.666666666666667 0.847058823529412 0.694117647058824 setrgbcolor \n\
%d %d scale\n", width, height, width, height, scaleX, scaleY


 printf "%g %g %g box fill\n", $2, $1, $3 + offset

END{ print "grestore showpage" }

The awk script consists of writing the header, drawing the rectangles from NRT output, and writing the footer.

Using ImageMagick

Different postscript viewers will read the files in slightly different ways, so ImageMagick commandline utility convert is used to convert PostScript data to a png file. Convert can read from standard input, which makes it possible to send the output of AWK directly to it without the need to write an intermediary file.

Piping it all together

Here is what the entire program looks like:

echo "d4. r8 m s t4 s m r2 m4 d2." | nrt | awk -F ',' \
-v scaleX=30 -v scaleY=10 -v offset=12 -v width=400 -v height=300 \
-f gen_ps.awk \
| convert - output.png

The flow of the program reads like this:

  1. NRT notation is piped into NRT
  2. NRT outputs a text table which is piped to Awk
  3. Awk creates postscript, which is then piped to ImageMagick
  4. ImageMagick reads the postscript and converts it to a PNG file.

The Awk script takes in a few arguments, which can be used to tweak the output. width and height are used to define the image dimensions. scaleX and scaleY are used to change the size of piano roll notes in relation to the size of the picture, and offset shifts the piano roll up N notes (negative numbers work fine too).

And that's about all there is to it! You can find this code (along with code with other ways to NRT) on github