]> git.donarmstrong.com Git - lilypond.git/blob - scripts/etf2ly.py
release: 1.3.80
[lilypond.git] / scripts / etf2ly.py
1 #!@PYTHON@
2
3 # info mostly taken from looking at files. See also
4 # http://www.cs.uu.nl/~hanwen/lily-devel/etf.html
5
6 # This supports
7 #
8 #  * notes
9 #  * rests
10 #  * ties
11 #  * slurs
12 #  * lyrics
13 #  * articulation
14
15
16 # todo:
17 #  * slur/stem directions
18 #  * voices (2nd half of frame?)
19 #  * more intelligent lyrics
20 #  * beams (better use autobeam?)
21
22 program_name = 'etf2ly'
23 version = '@TOPLEVEL_VERSION@'
24 if version == '@' + 'TOPLEVEL_VERSION' + '@':
25         version = '(unknown version)'      # uGUHGUHGHGUGH
26   
27 import __main__
28 import getopt
29 import sys
30 import re
31 import string
32 import os
33
34 finale_clefs= ['treble', 'alto', 'tenor', 'bass', 'percussion', 'treble8vb', 'bass8vb', 'baritone']
35
36 def lily_clef (fin):
37         return finale_clefs[fin]
38
39 def gulp_file(f):
40         return open (f).read ()
41
42 # notename 0 == central C
43 distances = [0, 2, 4, 5, 7, 9, 11, 12]
44 def semitones (name, acc):
45         return (name / 7 ) * 12 + distances[name % 7] + acc
46
47 # represent pitches as (notename, alteration), relative to C-major scale
48 def transpose(orig, delta):
49         (oname, oacc) = orig
50         (dname, dacc) = delta
51         
52         old_pitch =semitones (oname, oacc)
53         delta_pitch = semitones (dname, dacc)
54         nname = (oname + dname) 
55         nacc = oacc
56         new_pitch = semitones (nname, nacc) 
57
58         nacc = nacc - (new_pitch - old_pitch - delta_pitch)
59
60         return (nname, nacc)
61
62
63
64 # find transposition of C-major scale that belongs here. 
65 def interpret_finale_key_sig (finale_id):
66         p = (0,0)
67         if 0 <= finale_id < 7:
68                 while finale_id > 0:
69                         p = transpose (p, (4,0)) # a fifth up
70                         finale_id = finale_id - 1
71         elif 248 < finale_id <= 255:
72                 while finale_id < 256:
73                         p = transpose (p, (3,0))
74                         finale_id = finale_id + 1
75
76         p  = (p[0] % 7, p[1])
77         return p
78
79 # should cache this.
80 def find_scale (transposition):
81         cscale = map (lambda x: (x,0), range (0,7))
82         trscale = map(lambda x, k=transposition: transpose(x, k), cscale)
83
84         return trscale
85
86 def gcd (a,b):
87         if b == 0:
88                 return a
89         c = a
90         while c: 
91                 c = a % b
92                 a = b
93                 b = c
94         return a
95         
96
97 def rat_simplify (r):
98         (n,d) = r
99         if d < 0:
100                 d = -d
101                 n = -n
102         if n == 0:
103                 return (0,1)
104         else:
105                 g = gcd (n, d)
106                 return (n/g, d/g)
107         
108 def rat_multiply (a,b):
109         (x,y) = a
110         (p,q) = b
111
112         return rat_simplify ((x*p, y*q))
113
114 def rat_add (a,b):
115         (x,y) = a
116         (p,q) = b
117
118         return rat_simplify ((x*q + p*y, y*q))
119
120 def rat_neg (a):
121         (p,q) = a
122         return (-p,q)
123
124 def rat_subtract (a,b ):
125         return rat_add (a, rat_neg (b))
126
127 def lily_notename (tuple2):
128         (n, a) = tuple2
129         nn = chr ((n+ 2)%7 + ord ('a'))
130
131         if a == -1:
132                 nn = nn + 'es'
133         elif a == -2:
134                 nn = nn + 'eses'
135         elif a == 1:
136                 nn = nn + 'is'
137         elif a == 2:
138                 nn = nn + 'isis'
139
140         return nn
141
142
143 class Slur:
144         def __init__ (self, number):
145                 self.number = number
146                 self.finale = []
147
148         def append_entry (self, finale_e):
149                 self.finale.append (finale_e)
150
151         def calculate (self, chords):
152                 startnote = self.finale[0][5]
153                 endnote = self.finale[3][2]
154
155                 cs = chords[startnote]
156                 cs.note_suffix = '(' + cs.note_suffix 
157                 ce = chords[endnote]
158                 ce.prefix = ce.prefix + ')'
159                 
160 class Global_measure:
161         def __init__ (self, number):
162                 self.timesig = ''
163                 self.number = number
164                 self.keysignature = None
165                 self.scale = None
166
167                 self.finale = []
168
169         def set_timesig (self, finale):
170                 (beats, fdur) = finale
171                 (log, dots) = EDU_to_duration (fdur)
172                 assert dots == 0
173                 self.timesig = (beats, log)
174
175         def length (self):
176                 return self.timesig
177         
178         def set_keysig (self, finale):
179                 k = interpret_finale_key_sig (finale)
180                 self.keysignature = k
181                 self.scale = find_scale (k)
182
183
184 articulation_dict ={
185         11: '\\prall',
186         12: '\\mordent',
187         8: '\\fermata',
188         18: '"arp"' , # arpeggio
189 };
190
191 class Articulation:
192         def __init__ (self, a,b, finale):
193                 self.type = finale[0]
194                 self.notenumber = b
195         def calculate (self, chords):
196                 c = chords[self.notenumber]
197
198                 try:
199                         a = articulation_dict[self.type]
200                 except KeyError:
201                         a = '"art"'
202                         
203                 c.note_suffix = '-' + a + c.note_suffix
204
205 class Syllable:
206         def __init__ (self, a,b , finale):
207                 self.chordnum = b
208                 self.syllable = finale[1]
209                 self.verse = finale[0]
210         def calculate (self, chords, lyrics):
211                 self.chord = chords[self.chordnum]
212
213 class Verse:
214         def __init__ (self, number, body):
215                 self.body = body
216                 self.number = number
217                 self.split_syllables ()
218         def split_syllables (self):
219                 ss = re.split ('(-| +)', self.body)
220
221                 sep = 0
222                 syls = [None]
223                 for s in ss:
224                         if sep:
225                                 septor = re.sub (" +", "", s)
226                                 septor = re.sub ("-", " -- ", septor) 
227                                 syls[-1] = syls[-1] + septor
228                         else:
229                                 syls.append (s)
230                         
231                         sep = not sep 
232
233                 self.syllables = syls
234
235         def dump (self):
236                 str = ''
237                 line = ''
238                 for s in self.syllables[1:]:
239                         line = line + ' ' + s
240                         if len (line) > 72:
241                                 str = str + ' ' * 4 + line + '\n'
242                                 line = ''
243                         
244                 str = """\nverse%s = \\lyrics {\n %s}\n""" %  (encodeint (self.number - 1) ,str)
245                 return str
246
247         
248 class Measure:
249         def __init__(self, no):
250                 self.number = no
251                 self.frames = []
252                 self.flags = 0
253                 self.clef = 0
254                 self.finale = []
255                 self.global_measure = None
256                 
257         def add_finale_entry (self, entry):
258                 self.finale.append (entry)
259
260         def calculate (self):
261                 f0  = self.finale[0]
262                 f1 = self.finale[1]
263                 
264                 self.clef = string.atoi (f0[0])
265                 self.flags = string.atoi (f0[1])
266                 fs = map (string.atoi, list (f0[2:]) + [f1[0]])
267
268                 self.frames = fs
269
270 class Frame:
271         def __init__ (self, finale):
272                 self.measure = None
273                 self.finale = finale
274                 (number, start, end ) = finale
275                 self.number = number
276                 self.start = start
277                 self.end = end
278                 self.chords  = []
279
280         def set_measure (self, m):
281                 self.measure = m
282
283         def dump (self):
284                 str = ''
285                 left = self.measure.global_measure.length ()
286                 for c in self.chords:
287                         str = str + c.ly_string () + ' '
288                         left = rat_subtract (left, c.length ())
289                 if left[0] < 0:
290                         print self.number
291                         print self.start, self.end
292                         print left
293                         raise 'bla'
294                 if left[0]:
295                         str = str + 's*%d/%d' % left
296                         
297                 str = str + '\n'
298                 return str
299                 
300 def encodeint (i):
301         return chr ( i  + ord ('A'))
302
303 class Staff:
304         def __init__ (self, number):
305                 self.number = number
306                 self.measures = []
307
308         def get_measure (self, no):
309                 if len (self.measures) <= no:
310                         self.measures = self.measures + [None]* (1 + no - len (self.measures))
311
312                 if self.measures[no] == None:
313                         self.measures [no] = Measure (no)
314
315                 return self.measures[no]
316         def staffid (self):
317                 return 'staff' + encodeint (self.number - 1)
318         def layerid (self, l):
319                 return self.staffid() +  'layer%s' % chr (l -1 + ord ('A'))
320         
321         def dump_time_key_sigs (self):
322                 k  = ''
323                 last_key = None
324                 last_time = None
325                 last_clef = None
326                 gap = (0,1)
327                 for m in self.measures[1:]:
328                         g = m.global_measure
329                         e = ''
330                         if last_key <> g.keysignature:
331                                 e = e + "\\key %s \\major; " % lily_notename (g.keysignature)
332                                 last_key = g.keysignature
333                         if last_time <> g.timesig :
334                                 e = e + "\\time %d/%d; " % g.timesig
335                                 last_time = g.timesig
336                         if last_clef <> m.clef :
337                                 e = e + '\\clef %s;' % lily_clef (m.clef)
338                                 last_clef = m.clef
339                         if e:
340                                 if gap <> (0,1):
341                                         k = k +' s1*%d/%d \n ' % gap
342                                 gap = (0,1)
343                                 k = k + e
344                         
345                         gap = rat_add (gap, g.length ())
346
347                                 
348                 k = '%sglobal = \\notes  { %s }\n\n ' % (self.staffid (), k)
349                 return k
350         
351         def dump (self):
352                 str = ''
353
354
355                 layerids = []
356                 for x in range (1,5): # 4 layers.
357                         laystr =  ''
358                         last_frame = None
359                         first_frame = None
360                         gap = (0,1)
361                         for m in self.measures[1:]:
362                                 fr = m.frames[x]
363                                 if fr:
364                                         first_frame = fr
365                                         if gap <> (0,1):
366                                                 laystr = laystr +'} s1*%d/%d {\n ' % gap
367                                                 gap = (0,1)
368                                         laystr = laystr + fr.dump ()
369                                 else:
370                                         gap = rat_add (gap, m.global_measure.length ())
371
372                         if first_frame:
373                                 l = self.layerid (x)
374                                 laystr = '%s =  \\notes { { %s } }\n\n' % (l, laystr)
375                                 str = str  + laystr
376                                 layerids.append (l)
377
378                 str = str +  self.dump_time_key_sigs ()         
379                 stafdef = '\\%sglobal' % self.staffid ()
380                 for i in layerids:
381                         stafdef = stafdef + ' \\' + i
382                         
383
384                 str = str + '%s = \\context Staff = %s <\n %s\n >\n' % \
385                       (self.staffid (), self.staffid (), stafdef)
386                 return str
387
388                                 
389 def EDU_to_duration (edu):
390         log = 1
391         d = 4096
392         while d > edu:
393                 d = d >> 1
394                 log = log << 1
395
396         edu = edu - d
397         dots = 0
398         if edu == d /2:
399                 dots = 1
400         elif edu == d*3/4:
401                 dots = 2
402         return (log, dots)      
403
404 class Chord:
405         def __init__ (self, finale_entry):
406                 self.pitches = []
407                 self.frame = None
408                 self.finale = finale_entry
409                 self.duration  = None
410                 self.next = None
411                 self.prev = None
412                 self.note_prefix= ''
413                 self.note_suffix = ''
414                 self.chord_suffix = ''
415                 self.chord_prefix = ''
416                 
417         def measure (self):
418                 if not self.frame:
419                         return None
420                 return self.frame.measure
421
422         def length (self):
423                 l = (1, self.duration[0])
424
425                 d = 1 << self.duration[1]
426
427                 dotfact = rat_subtract ((2,1), (1,d))
428                 return rat_multiply (dotfact, l)
429                 
430         def number (self):
431                 return self.finale[0][0]
432         def set_duration (self):
433                 ((no, prev, next, dur, pos, entryflag, extended, follow),
434                  notelist) = self.finale
435                 self.duration = EDU_to_duration(dur)
436         def find_realpitch (self):
437                 
438                 ((no, prev, next, dur, pos, entryflag, extended, follow), notelist) = self.finale
439
440                 meas = self.measure ()
441                 tiestart = 0
442                 if not meas or not meas.global_measure  :
443                         print 'note %d not in measure' % self.number ()
444                 elif not meas.global_measure.scale:
445                         print  'note %d: no scale in this measure.' % self.number ()
446                 else:
447                         for p in notelist:
448                                 (pitch, flag) = p
449                                 
450                                 nib1 = pitch & 0x0f
451                                 if nib1 > 8:
452                                         nib1 = -(nib1 - 8)
453                                 rest = pitch / 16
454
455                                 scale =  meas.global_measure.scale 
456                                 (sn, sa) =scale[rest % 7]
457                                 sn = sn + (rest - (rest%7)) + 7
458                                 acc = sa + nib1
459                                 self.pitches.append ((sn, acc))
460                                 tiestart =  tiestart or (flag & Chord.TIE_START_MASK)
461                 if tiestart :
462                         self.chord_suffix = self.chord_suffix + ' ~ '
463                 
464         REST_MASK = 0x40000000L
465         TIE_START_MASK = 0x40000000L
466         def ly_string (self):
467                 s = ''
468
469                 rest = ''
470
471                 if not (self.finale[0][5] & Chord.REST_MASK):
472                         rest = 'r'
473                 
474                 for p in self.pitches:
475                         (n,a) =  p
476                         o = n/ 7
477                         n = n % 7
478
479                         nn = lily_notename ((n,a))
480
481                         if o < 0:
482                                 nn = nn + (',' * -o)
483                         elif o > 0:
484                                 nn = nn + ('\'' * o)
485                                 
486                         if s:
487                                 s = s + ' '
488
489                         if rest:
490                                 nn = rest
491                                 
492                         s = s + '%s%d%s' % (nn, self.duration[0], '.'* self.duration[1])
493
494                 if not self.pitches:
495                         s  = 'r%d%s' % (self.duration[0] , '.'* self.duration[1])
496                 s = self.note_prefix + s + self.note_suffix
497                 if len (self.pitches) > 1:
498                         s = '<%s>' % s
499                 
500                 s = self.chord_prefix + s + self.chord_suffix
501                 return s
502
503 GFre = re.compile(r"""^\^GF\(([0-9-]+),([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""")
504 BCre = re.compile (r"""^\^BC\(([0-9-]+)\) ([0-9-]+) .*$""")
505 eEre = re.compile(r"""^\^eE\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) \$([0-9A-Fa-f]+) ([0-9-]+) ([0-9-]+)""")
506 FRre = re.compile (r"""^\^FR\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""")
507 MSre = re.compile (r"""^\^MS\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""")
508 note_re = re.compile (r"""^ +([0-9-]+) \$([A-Fa-f0-9]+)""")
509 Sxre  = re.compile (r"""^\^Sx\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""")
510 IMre = re.compile (r"""^\^IM\(([0-9-]+),([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""")
511 vere = re.compile(r"""^\^(ve|ch|se)\(([0-9-]+),([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""")
512 versere = re.compile(r"""^\^verse\(([0-9]+)\)(.*)\^end""")
513
514 class Etf_file:
515         def __init__ (self, name):
516                 self.measures = [None]
517                 self.entries = [None]
518                 self.chords = [None]
519                 self.frames = [None]
520                 self.staffs = [None]
521                 self.slurs = [None]
522                 self.articulations = [None]
523                 self.syllables = [None]
524                 self.verses = [None]
525                 
526                 ## do it
527                 self.parse (name)
528
529         def get_global_measure (self, no):
530                 if len (self.measures) <= no:
531                         self.measures = self.measures + [None]* (1 + no - len (self.measures))
532
533                 if self.measures[no] == None:
534                         self.measures [no] = Global_measure (no)
535
536                 return self.measures[no]
537
538                 
539         def get_staff(self,staffno):
540                 if len (self.staffs) <= staffno:
541                         self.staffs = self.staffs + [None] * (1 + staffno - len (self.staffs))
542
543                 if self.staffs[staffno] == None:
544                         self.staffs[staffno] = Staff (staffno)
545
546                 return self.staffs[staffno]
547
548         # staff-spec
549         def try_IS (self, l):
550                 pass
551
552         def try_BC (self, l):
553                 m =  BCre.match  (l)
554                 if m:
555                         bn = string.atoi (m.group (1))
556                         where = string.atoi (m.group (2)) / 1024.0
557                 return m
558
559         def try_IM (self, l):
560                 m = IMre.match (l)
561                 if m:
562                         a = string.atoi (m.group (1))
563                         b = string.atoi (m.group (2))
564
565                         fin = map (string.atoi, m.groups ()[2:])
566
567                         self.articulations.append (Articulation (a,b,fin))
568                 return m
569         def try_verse (self,l):
570                 m =  versere .match (l)
571                 if m:
572                         a = string.atoi (m.group (1))
573                         body =m.group (2)
574
575                         body = re.sub (r"""\^[a-z]+\([^)]+\)""", "", body)
576                         body = re.sub ("\^[a-z]+", "", body)
577                         self.verses.append (Verse (a, body))
578                         
579                 return m
580         def try_ve (self,l):
581                 m = vere .match (l)
582                 if m:
583                         a = string.atoi (m.group (1))
584                         b = string.atoi (m.group (2))
585
586                         fin = map (string.atoi, m.groups ()[2:])
587
588                         self.syllables.append (Syllable (a,b,fin))
589                 return m
590         def try_eE (self, l):
591                 m = eEre.match (l)
592                 if m:
593                         tup = m.groups()
594                         (no, prev, next, dur, pos, entryflag, extended, follow) = tup
595                         (no, prev, next, dur, pos,extended, follow) \
596                           = tuple (map (string.atoi, [no,prev,next,dur,pos,extended,follow]))
597
598                         entryflag = string.atol (entryflag,16)
599                         assert (no==len (self.entries))
600                         current_entry = ((no, prev, next, dur, pos, entryflag, extended, follow), [])
601                         self.entries.append (current_entry)
602                 return m
603
604         def try_Sx(self,l):
605                 m = Sxre.match (l)
606                 if m:
607                         slurno = string.atoi (m.group (1))
608
609                         if len (self.slurs) == slurno:
610                                 self.slurs.append (Slur (slurno))
611
612                         params = list (m.groups ()[1:])
613                         params = map (string.atoi, params)
614                         self.slurs[-1].append_entry (params)
615
616                 return m        
617         def try_GF(self, l):
618                 m = GFre.match (l)
619                 if m:
620                         (staffno,measno) = m.groups ()[0:2]
621                         s = string.atoi (staffno)
622                         me = string.atoi (measno)
623                         
624                         entry = m.groups () [2:]
625                         st = self.get_staff (s)
626                         meas = st.get_measure (me)
627                         meas.add_finale_entry (entry)
628                 
629         # frame  ?
630         def try_FR(self, l):
631                 m = FRre.match (l)
632                 if m:
633                         (frameno, startnote, endnote, foo, bar) = m.groups ()
634                         (frameno, startnote, endnote)  = tuple (map (string.atoi, [frameno, startnote, endnote]))
635                         self.frames.append (Frame ((frameno, startnote, endnote)))
636                         
637                 return m
638         def try_MS (self, l):
639                 m = MSre.match (l)
640                 if m:
641                         measno = string.atoi (m.group (1))
642                         keynum = string.atoi (m.group (3))
643                         meas =self. get_global_measure (measno)
644                         meas.set_keysig (keynum)
645
646                         beats = string.atoi (m.group (4))
647                         beatlen = string.atoi (m.group (5))
648                         meas.set_timesig ((beats, beatlen))
649                                                 
650                 return m
651
652         def try_note (self, l):
653                 m = note_re.match (l)
654                 if m:
655                         (pitch, flag) = m.groups ()
656                         pitch = string.atoi (pitch)
657                         flag = string.atol (flag,16)
658                         self.entries[-1][1].append ((pitch,flag))
659
660         def parse (self, name):
661                 sys.stderr.write ('parsing ...')
662                 sys.stderr.flush ()
663                 
664                 ls = open (name).readlines ()
665                 for l in ls:
666                         m = None
667                         if not m: 
668                                 m = self.try_MS (l)
669                         if not m: 
670                                 m = self.try_FR (l)
671                         if not m: 
672                                 m = self.try_GF (l)
673                         if not m: 
674                                 m = self.try_note (l)
675                         if not m: 
676                                 m = self.try_eE (l)
677                         if not m:
678                                 m = self.try_IM (l)
679                         if not m:
680                                 m = self.try_Sx (l)
681                         if not m:
682                                 m = self.try_verse (l)
683
684                 sys.stderr.write ('processing ...')
685                 sys.stderr.flush ()
686
687                 self.unthread_entries ()
688
689                 for st in self.staffs[1:]:
690                         if not st:
691                                 continue
692                         mno = 1
693                         for m in st.measures[1:]:
694                                 m.global_measure = self.measures[mno]
695                                 m.calculate()
696
697                                 frame_obj_list = [None]
698                                 for frno in m.frames:
699                                         fr = self.frames[frno]
700                                         frame_obj_list.append (fr)
701
702                                 m.frames = frame_obj_list
703                                 for fr in frame_obj_list[1:]:
704                                         if not fr:
705                                                 continue
706                                         
707                                         fr.set_measure (m)
708                                         
709                                         fr.chords = self.get_thread (fr.start, fr.end)
710                                         for c in fr.chords:
711                                                 c.frame = fr
712                                 mno = mno + 1
713
714                 for c in self.chords[1:]:
715                         c.find_realpitch ()
716                         c.set_duration ()
717                         
718                 for s in self.slurs [1:]:
719                         s.calculate (self.chords)
720                 for s in self.articulations[1:]:
721                         s.calculate (self.chords)
722                         
723         def get_thread (self, startno, endno):
724
725                 thread = []
726                 c = self.chords[startno]
727                 while c and c.number () <> endno:
728                         thread.append (c)
729                         c = c.next
730
731                 if c: 
732                         thread.append (c)
733                 
734                 return thread
735
736         def dump (self):
737                 str = ''
738                 staffs = []
739                 for s in self.staffs[1:]:
740                         if s:
741                                 str = str + '\n\n' + s.dump () 
742                                 staffs.append ('\\' + s.staffid ())
743
744                 if staffs:
745                         str = str + '\\score { < %s > } ' % string.join (staffs)
746
747                 # should use \addlyrics ?
748
749                 for v in self.verses[1:]:
750                         str = str + v.dump()
751
752                 if len (self.verses) > 1:
753                         sys.stderr.write ("\nLyrics found; edit to use \\addlyrics to couple to a staff\n")
754                         
755                 return str
756
757
758         def __str__ (self):
759                 return self.dump ()
760         
761         def unthread_entries (self):
762                 self.chords = [None]
763                 for e in self.entries[1:]:
764                         self.chords.append (Chord (e))
765
766                 for e in self.chords[1:]:
767                         e.prev = self.chords[e.finale[0][1]]
768                         e.next = self.chords[e.finale[0][2]]
769
770
771          
772
773
774
775 def identify():
776         sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
777
778 def help ():
779         print r"""
780 Convert ETF to LilyPond.
781
782 Usage: etf2ly [OPTION]... ETF-FILE
783
784 Options:
785   -h, --help          this help
786   -o, --output=FILE   set output filename to FILE
787   -v, --version       version information
788
789 Enigma Transport Format is a format used by Coda Music Technology's
790 Finale product. This program will convert a subset of ETF to a
791 ready-to-use lilypond file.
792
793
794 """
795
796 def print_version ():
797         print r"""etf2ly (GNU lilypond) %s""" % version
798
799
800
801 (options, files) = getopt.getopt (sys.argv[1:], 'vo:h', ['help','version', 'output='])
802 out_filename = None
803
804 for opt in options:
805         o = opt[0]
806         a = opt[1]
807         if o== '--help' or o == '-h':
808                 help ()
809                 sys.exit (0)
810         if o == '--version' or o == '-v':
811                 print_version ()
812                 sys.exit(0)
813                 
814         if o == '--output' or o == '-o':
815                 out_filename = a
816         else:
817                 print o
818                 raise getopt.error
819
820 identify()
821
822 # header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
823 for f in files:
824         if f == '-':
825                 f = ''
826
827         sys.stderr.write ('Processing `%s\'\n' % f)
828         e = Etf_file(f)
829         if not out_filename:
830                 out_filename = os.path.basename (re.sub ('.etf$', '.ly', f))
831                 
832         if out_filename == f:
833                 out_filename = os.path.basename (f + '.ly')
834                 
835         sys.stderr.write ('Writing `%s\'' % out_filename)
836         ly = e.dump()
837
838         
839         
840         fo = open (out_filename, 'w')
841         fo.write ('%% lily was here -- automatically converted by etf2ly from %s\n' % f)
842         fo.write(ly)
843         fo.close ()
844