source: rtems-tools/doc/asciidoc/filters/music/music2png.py @ f91e023

4.104.115
Last change on this file since f91e023 was f91e023, checked in by Chris Johns <chrisj@…>, on 02/17/14 at 07:04:46

Add the documentation.

  • Property mode set to 100755
File size: 6.3 KB
Line 
1#!/usr/bin/env python
2'''
3NAME
4    music2png - Converts textual music notation to classically notated PNG file
5
6SYNOPSIS
7    music2png [options] INFILE
8
9DESCRIPTION
10    This filter reads LilyPond or ABC music notation text from the input file
11    INFILE (or stdin if INFILE is -), converts it to classical music notation
12    and writes it to a trimmed PNG image file.
13
14    This script is a wrapper for LilyPond and ImageMagick commands.
15
16OPTIONS
17    -f FORMAT
18        The INFILE music format. 'abc' for ABC notation, 'ly' for LilyPond
19        notation. Defaults to 'abc' unless source starts with backslash.
20
21    -o OUTFILE
22        The file name of the output file. If not specified the output file is
23        named like INFILE but with a .png file name extension.
24
25    -m
26        Skip if the PNG output file is newer that than the INFILE.
27        Compares timestamps on INFILE and OUTFILE. If
28        INFILE is - (stdin) then compares MD5 checksum stored in file
29        named like OUTFILE but with a .md5 file name extension.
30        The .md5 file is created if the -m option is used and the
31        INFILE is - (stdin).
32
33    -v
34        Verbosely print processing information to stderr.
35
36    --help, -h
37        Print this documentation.
38
39    --version
40        Print program version number.
41
42SEE ALSO
43    lilypond(1), abc2ly(1), convert(1)
44
45AUTHOR
46    Written by Stuart Rackham, <srackham@gmail.com>
47
48COPYING
49    Copyright (C) 2006 Stuart Rackham. Free use of this software is
50    granted under the terms of the GNU General Public License (GPL).
51'''
52
53# Suppress warning: "the md5 module is deprecated; use hashlib instead"
54import warnings
55warnings.simplefilter('ignore',DeprecationWarning)
56
57import os, sys, tempfile, md5
58
59VERSION = '0.1.2'
60
61# Globals.
62verbose = False
63
64class EApp(Exception): pass     # Application specific exception.
65
66def print_stderr(line):
67    sys.stderr.write(line + os.linesep)
68
69def print_verbose(line):
70    if verbose:
71        print_stderr(line)
72
73def write_file(filename, data, mode='w'):
74    f = open(filename, mode)
75    try:
76        f.write(data)
77    finally:
78        f.close()
79
80def read_file(filename, mode='r'):
81    f = open(filename, mode)
82    try:
83        return f.read()
84    finally:
85        f.close()
86
87def run(cmd):
88    global verbose
89    if not verbose:
90        cmd += ' 2>%s' % os.devnull
91    print_verbose('executing: %s' % cmd)
92    if os.system(cmd):
93        raise EApp, 'failed command: %s' % cmd
94
95def music2png(format, infile, outfile, modified):
96    '''Convert ABC notation in file infile to cropped PNG file named outfile.'''
97    outfile = os.path.abspath(outfile)
98    outdir = os.path.dirname(outfile)
99    if not os.path.isdir(outdir):
100        raise EApp, 'directory does not exist: %s' % outdir
101    basefile = tempfile.mktemp(dir=os.path.dirname(outfile))
102    temps = [basefile + ext for ext in ('.abc', '.ly', '.ps', '.midi')]
103    skip = False
104    if infile == '-':
105        source = sys.stdin.read()
106        checksum = md5.new(source).digest()
107        filename = os.path.splitext(outfile)[0] + '.md5'
108        if modified:
109            if os.path.isfile(filename) and os.path.isfile(outfile) and \
110                    checksum == read_file(filename,'rb'):
111                skip = True
112            else:
113                write_file(filename, checksum, 'wb')
114    else:
115        if not os.path.isfile(infile):
116            raise EApp, 'input file does not exist: %s' % infile
117        if modified and os.path.isfile(outfile) and \
118                os.path.getmtime(infile) <= os.path.getmtime(outfile):
119            skip = True
120        source = read_file(infile)
121    if skip:
122        print_verbose('skipped: no change: %s' % outfile)
123        return
124    if format is None:
125        if source and source.startswith('\\'):  # Guess input format.
126            format = 'ly'
127        else:
128            format = 'abc'
129    # Write temporary source file.
130    write_file('%s.%s' % (basefile,format), source)
131    abc = basefile + '.abc'
132    ly = basefile + '.ly'
133    png = basefile + '.png'
134    saved_pwd = os.getcwd()
135    os.chdir(outdir)
136    try:
137        if format == 'abc':
138            run('abc2ly -o "%s" "%s"' % (ly,abc))
139        run('lilypond --png -o "%s" "%s"' % (basefile,ly))
140        os.rename(png, outfile)
141    finally:
142        os.chdir(saved_pwd)
143    # Chop the bottom 75 pixels off to get rid of the page footer then crop the
144    # music image. The -strip option necessary because FOP does not like the
145    # custom PNG color profile used by Lilypond.
146    run('convert "%s" -strip -gravity South -chop 0x75 -trim "%s"' % (outfile, outfile))
147    for f in temps:
148        if os.path.isfile(f):
149            print_verbose('deleting: %s' % f)
150            os.remove(f)
151
152def usage(msg=''):
153    if msg:
154        print_stderr(msg)
155    print_stderr('\n'
156                 'usage:\n'
157                 '    music2png [options] INFILE\n'
158                 '\n'
159                 'options:\n'
160                 '    -f FORMAT\n'
161                 '    -o OUTFILE\n'
162                 '    -m\n'
163                 '    -v\n'
164                 '    --help\n'
165                 '    --version')
166
167def main():
168    # Process command line options.
169    global verbose
170    format = None
171    outfile = None
172    modified = False
173    import getopt
174    opts,args = getopt.getopt(sys.argv[1:], 'f:o:mhv', ['help','version'])
175    for o,v in opts:
176        if o in ('--help','-h'):
177            print __doc__
178            sys.exit(0)
179        if o =='--version':
180            print('music2png version %s' % (VERSION,))
181            sys.exit(0)
182        if o == '-f': format = v
183        if o == '-o': outfile = v
184        if o == '-m': modified = True
185        if o == '-v': verbose = True
186    if len(args) != 1:
187        usage()
188        sys.exit(1)
189    infile = args[0]
190    if format not in (None, 'abc', 'ly'):
191        usage('invalid FORMAT')
192        sys.exit(1)
193    if outfile is None:
194        if infile == '-':
195            usage('OUTFILE must be specified')
196            sys.exit(1)
197        outfile = os.path.splitext(infile)[0] + '.png'
198    # Do the work.
199    music2png(format, infile, outfile, modified)
200    # Print something to suppress asciidoc 'no output from filter' warnings.
201    if infile == '-':
202        sys.stdout.write(' ')
203
204if __name__ == "__main__":
205    try:
206        main()
207    except SystemExit:
208        raise
209    except KeyboardInterrupt:
210        sys.exit(1)
211    except Exception, e:
212        print_stderr("%s: %s" % (os.path.basename(sys.argv[0]), str(e)))
213        sys.exit(1)
Note: See TracBrowser for help on using the repository browser.