Attachment 'AbcMusic.py'
Download 1 """
2 MoinMoin Processor for abc format music syntax using abcm2ps, abc2midi
3
4 Copyright (c) 2004 by Nathan Whitehead <nwhitehe *at* cs.ucsc.edu>
5 All rights reserved, see COPYING for details. (GPL)
6 """
7 Dependencies = ["time"]
8
9 import string, sys, os, re, sha
10 from MoinMoin import config
11
12
13 class Parser:
14 """ a noop parser """
15
16 def __init__(self, raw, request, **kw):
17
18 self.raw = raw
19 self.request = request
20
21 # save call arguments for later use in format
22
23 # Set these according to local environment
24 self.config_cache_dir='/home/nico/public_html/lakisawiki-666/abcps'
25
26 self.config_cache_url=self.request.cfg.url_prefix+'/abcps'
27
28 self.config_tmp_dir='/tmp'
29
30 self.config_external_abc2ps='/usr/bin/abcm2ps'
31 self.config_external_pstopnm='/usr/bin/pstopnm'
32 self.config_external_gs='/usr/bin/gs'
33 self.config_external_pnmcat='/usr/bin/pnmcat'
34 self.config_external_pnmcrop='/usr/bin/pnmcrop'
35 self.config_external_abc2midi='/usr/bin/abc2midi'
36
37 # Use png or gif?
38 self.config_use_gif=0
39 self.config_external_rasterize='/usr/bin/ppmtogif'
40 self.config_external_gzip='/bin/gzip'
41 self.config_score_orientation='-tb' # or -lr for horizontal
42 # Serve and store scores gzipped to save space?
43 self.config_zip_scores=0
44 # Cut off hash to this many characters for user friendliness
45 self.hash_length = 6
46 # Show code in HTML page by default?
47 self.show_code = 1
48 # Generate PNG graphics of each page? (slow and memory intensive for server)
49 self.show_score = 1
50 # Show an embedded MIDI controller for playing the song?
51 self.show_midi = 1
52 # Syntax highlighting colors for ABC code
53 self.header_color = '#6000a0'
54 self.info_color = '#006000'
55 self.note_color = '#000000'
56 self.bar_color = '#ff0000'
57 self.continues_color = '#ff00ff'
58 self.comment_color = '#ff5050'
59 self.chord_color = '#808080'
60
61
62 def format(self, formatter):
63 # this has the function of the old processor method
64 # just use self.raw and self.request
65 self.process(self.request, formatter, self.raw.split("\n"))
66 #self.process(self.request, formatter, self.raw)
67 #self.request.write(self.raw)
68
69
70 def quote_apply(self, f, txt):
71 """Apply a given function on text to all quotations in a string"""
72 # This is harder than it sounds because replacement text may have quotes
73 qi = txt.find('"')
74 while not qi == -1:
75 qi2 = txt.find('"', qi + 1)
76 if not qi2 == -1:
77 newtext = f(txt[qi:qi2 + 1])
78 txt = txt[:qi] + newtext + txt[qi2 + 1:]
79 lenincr = len(newtext) - (qi2 - qi)
80 qi = txt.find('"', qi2 + lenincr)
81 else:
82 qi = -1
83 return txt
84
85 def color_txt(self, c, txt):
86 """Make text appear the given ascii color"""
87 return '<font color="' + c + '">' + txt + '</font>'
88
89 def colorize_line(self, txt):
90 # If it is header, color it and we're done
91 if len(txt) >= 2 and txt[1] == ':':
92 return self.color_txt(self.header_color, txt[:2]) + self.color_txt(self.info_color, txt[2:])
93 # If it is comment, color and we're done
94 if len(txt) >= 1 and txt[0] == '%':
95 return self.color_txt(self.comment_color, txt)
96 # Color quotations (chords) first, otherwise
97 # we will get more quotes from font tags later.
98 def give_chord_color(txt):
99 return self.color_txt(self.chord_color, txt)
100 txt = self.quote_apply(give_chord_color, txt)
101 txt = txt.replace('|', self.color_txt(self.bar_color, '|'))
102 txt = txt.replace('\\', self.color_txt(self.continues_color, '\\'))
103 return self.color_txt(self.note_color, txt)
104
105 def colorize(self, lines):
106 """Do rudimentary syntax highlighting of ABC music in HTML"""
107 # Do it line by line
108 clines = map(self.colorize_line, lines)
109 return string.join(clines, '\n')
110
111 def get_title(self, lines):
112 """Return the title string, if it exists"""
113 for l in lines:
114 if len(l) >= 2 and l[0] == 'T' and l[1] == ':':
115 return l[2:]
116 return "(Untitled)"
117
118 def cleanup(self, txt):
119 """Scrub a title so it may be used in a filename"""
120 # Old way, txt.replace(' ', '_').replace('(', '').replace(')', '')
121 res = ""
122 for x in txt:
123 if x.isalnum():
124 res = res + x
125 if x == ' ':
126 res = res + '_'
127 return res
128
129 def process_arguments(self, lines):
130 global show_code
131 global show_score
132 if len(lines) > 0:
133 stripline0 = string.strip(lines[0])
134 if stripline0 == "#!AbcMusic":
135 del lines[0]
136 self.process_arguments(lines)
137 if stripline0 == "#show-code-off":
138 del lines[0]
139 show_code = 0
140 self.process_arguments(lines)
141 if stripline0 == "#show-code-on":
142 del lines[0]
143 show_code = 1
144 self.process_arguments(lines)
145 if stripline0 == "#show-score-off":
146 del lines[0]
147 show_score = 0
148 self.process_arguments(lines)
149 if stripline0 == "#show-score-on":
150 del lines[0]
151 show_score = 1
152 self.process_arguments(lines)
153 return
154
155 def process(self, request, formatter, lines):
156
157
158 global show_code
159 # Preprocessing stage
160 self.process_arguments(lines)
161 title = self.get_title(lines)
162 texstr = string.join(lines, '\n')
163 texstr = string.strip(texstr)
164 hash = sha.new(texstr).hexdigest()[:self.hash_length]
165 name = self.cleanup(title) + '_' + hash
166
167 filepath = "%s/%s" % (self.config_cache_dir, name)
168 urlpath = "%s/%s" % (self.config_cache_url, name)
169
170 if self.config_use_gif:
171 suffix = ".gif"
172 else:
173 suffix = ".png"
174 abcpath = filepath + ".abc"
175 pspath = filepath + ".ps"
176 psgzpath = filepath + ".ps.gz"
177 pngpath = filepath + suffix
178 logpath = filepath + ".log"
179 midipath = filepath + ".mid"
180 abcurl = urlpath + ".abc"
181 if self.config_zip_scores:
182 psurl = urlpath + ".ps.gz"
183 else:
184 psurl = urlpath + ".ps"
185 pngurl = urlpath + suffix
186 logurl = urlpath + ".log"
187 midiurl = urlpath + ".mid"
188
189 # Delete logfile
190 os.system("rm -f %s" % logpath)
191
192 # Generate PS
193 if not os.path.exists(pspath):
194 data = open(abcpath, "w")
195 data.write('%s' % texstr)
196 data.close()
197
198 options = "-e 1"
199 cmd = "cd %(workingdir)s ; %(abc2ps)s %(options)s -O %(outfile)s %(infile)s >> %(logfile)s 2>&1" % {
200 "workingdir" : self.config_tmp_dir,
201 "abc2ps": self.config_external_abc2ps,
202 "options": options,
203 "outfile" : pspath,
204 "infile": abcpath,
205 "logfile": logpath
206 }
207 os.system(cmd)
208 os.system("chmod 644 " + pspath)
209 os.system("chmod 644 " + abcpath)
210 if self.config_zip_scores:
211 cmd = "%(gzip)s %(file)s 2> /dev/null" % {
212 "gzip" : self.config_external_gzip,
213 "file" : pspath
214 }
215 os.system(cmd)
216 os.system("chmod 644 " + psgzpath)
217 os.system("chmod 644 " + logpath)
218
219 # Generate PNG
220 if (not os.path.exists(pngpath)) and self.show_score:
221 # delete any old stuff
222 cmd = 'rm -f %s/%s*.ppm' % (self.config_tmp_dir, name)
223 os.system(cmd)
224
225 # Use ghostscript to convert ps to pnm files
226 options = "-dNOPAUSE -q -dBATCH -sPAPERSIZE=letter -sDEVICE=ppmraw -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r120x120 -sOutputFile=%s%%003d.ppm" % name
227 cmd = 'cd %(workingdir)s ; %(gs)s %(options)s %(infile)s > /dev/null 2>&1' % {
228 "workingdir" : self.config_tmp_dir,
229 "gs" : self.config_external_gs,
230 "options" : options,
231 "infile" : pspath
232 }
233 os.system(cmd)
234
235 # Concatenate pnm files into one file
236 cmd = 'cd %(workingdir)s ; %(pnmcat)s %(orientation)s %(name)s*.ppm 2> /dev/null | %(pnmcrop)s 2> /dev/null | %(rasterize)s > %(outfile)s 2> /dev/null' % {
237 "workingdir" : self.config_tmp_dir,
238 "pnmcat" : self.config_external_pnmcat,
239 "orientation" : self.config_score_orientation,
240 "name" : name,
241 "pnmcrop" : self.config_external_pnmcrop,
242 "rasterize" : self.config_external_rasterize,
243 "outfile" : pngpath
244 }
245 os.system(cmd)
246 os.system("chmod 644 " + pngpath)
247
248 # Generate MIDI file
249 if not os.path.exists(midipath):
250 data = open(abcpath, "w")
251 data.write('%s' % texstr)
252 data.close()
253
254 options = ""
255 cmd = "cd %(workingdir)s ; %(abc2midi)s %(infile)s %(options)s -o %(outfile)s >> %(logfile)s 2>&1" % {
256 "workingdir" : self.config_tmp_dir,
257 "abc2midi": self.config_external_abc2midi,
258 "options": options,
259 "outfile" : midipath,
260 "infile": abcpath,
261 "logfile": logpath
262 }
263 os.system(cmd)
264 os.system("chmod 644 " + midipath)
265 os.system("chmod 644 " + abcpath)
266 os.system("chmod 644 " + logpath)
267
268 # Clean up junk (unzipped score if using zipped scores)
269 if self.config_zip_scores:
270 os.system("rm -f " + pspath)
271
272 # Generate page
273 # if show_code or show_score:
274 request.write(formatter.paragraph(1))
275 request.write(formatter.table(1))
276
277 request.write(formatter.table_row(1))
278 request.write(formatter.table_cell(1))
279 request.write(formatter.paragraph(1))
280 request.write(formatter.strong(1))
281 request.write(formatter.emphasis(1))
282 request.write(formatter.text(title))
283 request.write(formatter.emphasis(0))
284 request.write(formatter.strong(0))
285 request.write(formatter.linebreak(preformatted=0))
286 request.write(formatter.url(1, abcurl))
287 request.write("ABC source file")
288 request.write(formatter.url(0))
289 request.write(formatter.linebreak(preformatted=0))
290 request.write(formatter.url(1, psurl))
291 request.write("PostScript score")
292 request.write(formatter.url(0))
293 request.write(formatter.linebreak(preformatted=0))
294 request.write(formatter.url(1, midiurl))
295 request.write("MIDI rendition")
296 request.write(formatter.url(0))
297 request.write(formatter.linebreak(preformatted=0))
298 request.write(formatter.url(1, logurl))
299 request.write("Compilation log")
300 request.write(formatter.url(0))
301 request.write(formatter.paragraph(0))
302 request.write(formatter.table_cell(0))
303 request.write(formatter.table_row(0))
304
305 if 0:#show_midi:
306 request.write(formatter.table_row(1))
307 request.write(formatter.table_cell(1))
308 request.write(formatter.preformatted(1))
309 request.write(formatter.rawHTML("<embed src=%s width=120 height=40 align=center>" % midiurl))
310 request.write(formatter.preformatted(0))
311 request.write(formatter.table_cell(0))
312 request.write(formatter.table_row(0))
313
314 if self.show_score:
315 request.write(formatter.table_row(1))
316 request.write(formatter.table_cell(1))
317 request.write(formatter.rawHTML("<img src=%s>"%pngurl))
318 request.write(formatter.table_cell(0))
319 request.write(formatter.table_row(0))
320
321 if self.show_code:
322 request.write(formatter.table_row(1))
323 request.write(formatter.table_cell(1))
324 request.write(formatter.preformatted(1))
325 request.write(formatter.rawHTML(self.colorize(lines)))
326 request.write(formatter.preformatted(0))
327 request.write(formatter.table_cell(0))
328 request.write(formatter.table_row(0))
329
330 request.write(formatter.table(0))
331 request.write(formatter.paragraph(0))
332
333 # Old way with just one link
334 #request.write(formatter.url(psurl, text=title))
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.