#!/usr/bin/env python ''' NAME music2png - Converts textual music notation to classically notated PNG file SYNOPSIS music2png [options] INFILE DESCRIPTION This filter reads LilyPond or ABC music notation text from the input file INFILE (or stdin if INFILE is -), converts it to classical music notation and writes it to a trimmed PNG image file. This script is a wrapper for LilyPond and ImageMagick commands. OPTIONS -f FORMAT The INFILE music format. 'abc' for ABC notation, 'ly' for LilyPond notation. -o OUTFILE The file name of the output file. If not specified the output file is named like INFILE but with a .png file name extension. -m Skip if the PNG output file is newer that than the INFILE. When INFILE is - (stdin) previously retained input is compared. -v Verbosely print processing information to stderr. --help, -h Print this documentation. --version Print program version number. SEE ALSO lilypond(1), abc2ly(1), convert(1) AUTHOR Written by Stuart Rackham, COPYING Copyright (C) 2006 Stuart Rackham. Free use of this software is granted under the terms of the GNU General Public License (GPL). ''' import os, sys VERSION = '0.1.0' # Globals. verbose = False class EApp(Exception): pass # Application specific exception. def print_stderr(line): sys.stderr.write(line + os.linesep) def print_verbose(line): if verbose: print_stderr(line) def run(cmd): global verbose if not verbose: cmd += ' 2>/dev/null' print_verbose('executing: %s' % cmd) if os.system(cmd): raise EApp, 'failed command: %s' % cmd def music2png(format, infile, outfile, modified): '''Convert ABC notation in file infile to cropped PNG file named outfile.''' outfile = os.path.abspath(outfile) outdir = os.path.dirname(outfile) if not os.path.isdir(outdir): raise EApp, 'directory does not exist: %s' % outdir basefile = os.path.splitext(outfile)[0] abc = basefile + '.abc' ly = basefile + '.ly' temps = [ basefile + ext for ext in ('.abc', '.ly', '.ps', '.midi') ] # Don't delete files that already exist. temps = [ f for f in temps if not os.path.exists(f) ] skip = False if infile == '-': lines = sys.stdin.readlines() if format == 'abc': f = abc else: f = ly if modified: if f in temps: del temps[temps.index(f)] # Don't delete previous source. if os.path.isfile(outfile) and os.path.isfile(f): old = open(f, 'r').readlines() skip = lines == old if not skip: open(f, 'w').writelines(lines) else: if not os.path.isfile(infile): raise EApp, 'input file does not exist: %s' % infile if modified and os.path.isfile(outfile): skip = os.path.getmtime(infile) <= os.path.getmtime(outfile) if skip: print_verbose('skipped: no change: %s' % outfile) return saved_pwd = os.getcwd() os.chdir(outdir) try: if format == 'abc': run('abc2ly "%s"' % abc) run('lilypond --png "%s"' % ly) finally: os.chdir(saved_pwd) # Chop the bottom 75 pixels off to get rid of the page footer. run('convert "%s" -gravity South -crop 1000x10000+0+75 "%s"' % (outfile, outfile)) # Trim all blank areas from sides, top and bottom. run('convert "%s" -trim "%s"' % (outfile, outfile)) for f in temps: if os.path.isfile(f): print_verbose('deleting: %s' % f) os.remove(f) def usage(msg=''): if msg: print_stderr(msg) print_stderr('\n' 'usage:\n' ' music2png [options] INFILE\n' '\n' 'options:\n' ' -f FORMAT\n' ' -o OUTFILE\n' ' -m\n' ' -v\n' ' --help\n' ' --version') def main(): # Process command line options. global verbose format = None outfile = None modified = False import getopt opts,args = getopt.getopt(sys.argv[1:], 'f:o:mhv', ['help','version']) for o,v in opts: if o in ('--help','-h'): print __doc__ sys.exit(0) if o =='--version': print('music2png version %s' % (VERSION,)) sys.exit(0) if o == '-f': format = v if o == '-o': outfile = v if o == '-m': modified = True if o == '-v': verbose = True if len(args) != 1: usage() sys.exit(1) infile = args[0] if format is None: usage('FORMAT must be specified') sys.exit(1) if format not in ('abc', 'ly'): usage('invalid FORMAT') sys.exit(1) if outfile is None: if infile == '-': usage('OUTFILE must be specified') sys.exit(1) outfile = os.path.splitext(infile)[0] + '.png' # Do the work. music2png(format, infile, outfile, modified) # Print something to suppress asciidoc 'no output from filter' warnings. if infile == '-': sys.stdout.write(' ') if __name__ == "__main__": try: main() except SystemExit: raise except KeyboardInterrupt: sys.exit(1) except Exception, e: print_stderr("%s: %s" % (os.path.basename(sys.argv[0]), str(e))) sys.exit(1)