]> git.donarmstrong.com Git - lilypond.git/blob - scripts/abc-2-ly.py
release: 1.1.45
[lilypond.git] / scripts / abc-2-ly.py
1 #!@PYTHON@
2
3 # once upon a rainy monday afternoon.
4 #
5 #   ...
6 #
7 # (not finished.)
8
9
10 program_name = 'abc-to-ly'
11 version = '0.1'
12 import __main__
13 import getopt
14 import sys
15 import re
16 import string
17
18
19 header = {}
20 global_voice_stuff = []
21 default_len = 4
22 global_key = [0] * 7                    # UGH
23
24
25
26 def dump_header (hdr):
27         print '\\header {'
28         for k in hdr.keys ():
29                 print '%s = "%s";\n'% (k,hdr[k])
30         print '};'
31
32 def set_default_length (s):
33         m =  re.search ('1/([0-9]+)', s)
34         if m:
35                 __main__.default_len = string.atoi ( m.group (1))
36
37 def gulp_file(f):
38         try:
39                 i = open(f)
40                 i.seek (0, 2)
41                 n = i.tell ()
42                 i.seek (0,0)
43         except:
44                 print 'can\'t open file: ' + f + '\n'
45                 return ''
46         s = i.read (n)
47         if len (s) <= 0:
48                 print 'gulped empty file: ' + f + '\n'
49         i.close ()
50         return s
51
52
53 # pitch manipulation. Tuples are (name, alteration).
54 # 0 is (central) C. Alteration -1 is a flat, Alteration +1 is a sharp
55 # pitch in semitones. 
56 def semitone_pitch  (tup):
57         p =0
58
59         t = tup[0]
60         p = p + 12 * (t / 7)
61         t = t % 7
62         
63         if t > 2:
64                 p = p- 1
65                 
66         p = p + t* 2 + tup[1]
67         return p
68
69 def fifth_above_pitch (tup):
70         (n, a)  = (tup[0] + 4, tup[1])
71
72         difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
73         a = a + difference
74         
75         return (n,a)
76
77 def sharp_keys ():
78         p = (0,0)
79         l = []
80         k = 0
81         while 1:
82                 l.append (p)
83                 (t,a) = fifth_above_pitch (p)
84                 if semitone_pitch((t,a)) % 12 == 0:
85                         break
86
87                 p = (t % 7, a)
88         return l
89
90 def flat_keys ():
91         p = (0,0)
92         l = []
93         k = 0
94         while 1:
95                 l.append (p)
96                 (t,a) = quart_above_pitch (p)
97                 if semitone_pitch((t,a)) % 12 == 0:
98                         break
99
100                 p = (t % 7, a)
101         return l
102
103 def quart_above_pitch (tup):
104         (n, a)  = (tup[0] + 3, tup[1])
105
106         difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
107         a = a + difference
108         
109         return (n,a)
110
111
112 def compute_key (k):
113         k = string.lower (k)
114         intkey = (ord (k[0]) - ord('a') + 5) % 7
115         intkeyacc =0
116         k = k[1:]
117         
118         if k and k[0] == 'b':
119                 intkeyacc = -1
120                 k = k[1:]
121         elif  k and k[0] == '#':
122                 intkeyacc = 1
123                 k = k[1:]
124
125         keytup = (intkey, intkeyacc)
126         
127         sharp_key_seq = sharp_keys ()
128         flat_key_seq = flat_keys ()
129
130         accseq = None
131         accsign = 0
132         if keytup in sharp_key_seq:
133                 accsign = 1
134                 key_count = sharp_key_seq.index (keytup)
135                 accseq = map (lambda x: (4*x -1 ) % 7, range (1, key_count + 1))
136
137         elif keytup in flat_key_seq:
138                 accsign = -1
139                 key_count = flat_key_seq.index (keytup)
140                 accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1))
141         else:
142                 raise "Huh"
143         
144         key_table = [0] * 7
145         for a in accseq:
146                  key_table[a] = key_table[a] + accsign
147                 
148
149         return key_table
150
151 def try_parse_header_line (ln):
152         m = re.match ('^(.): *(.*)$', ln)
153
154         if m:
155                 g =m.group (1)
156                 a = m.group (2)
157                 if g == 'T':
158                         header['title'] =  a
159                 if g == 'M':
160                         global_voice_stuff.append ('\\time %s;' % a)
161                 if g == 'K':
162                         __main__.global_key  =compute_key (a)# ugh.
163
164                         global_voice_stuff.append ('\\key %s;' % a)
165                 if g == 'O': 
166                         header ['origin'] = a
167                 if g == 'X': 
168                         header ['crossRefNumber'] = a
169
170                 if g == 'A':
171                         header ['area'] = a
172                 if g == 'H':
173                         header ['history'] = a
174                 if g == 'B':
175                         header ['book'] = a
176                 if g == 'S':
177                         header ['subtitle'] = a
178                 if g == 'L':
179                         set_default_length (ln)
180         
181
182         return m
183
184 def pitch_to_mudela_name (name, acc):
185         s = ''
186         if acc < 0:
187                 s = 'es'
188                 acc = -acc
189         elif acc > 0:
190                 s = 'is'
191
192         if name > 4:
193                 name = name -7
194         return chr (name  + ord('c'))  + s * acc
195
196 def octave_to_mudela_quotes (o):
197         s =''
198         if o < 0:
199                 o = -o
200                 s=','
201         else:
202                 s ='\''
203
204         return s * o
205
206 def parse_num (str):
207         durstr = ''
208         while str[0] in "1234567890":
209                 durstr = durstr + str[0]
210                 str = str[1:]
211
212         n = None
213         if durstr:
214                 n  =string.atoi (durstr) 
215         return (str,n)
216
217
218 def duration_to_mudela_duration  (multiply_tup, defaultlen):
219         base = 1
220
221 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP  !
222 def try_parse_note (str):
223         mud = ''
224
225         slur_begin =0
226         if str[0] == '(':
227                 slur_begin = 1
228                 str = str[1:]
229
230         acc = 0
231         if str[0] in '^=_':
232                 c = str[0]
233                 str = str[1:]
234                 if c == '^':
235                         acc = 1
236                 if c == '=':
237                         acc = 0
238                 if c == '_':
239                         acc = -1
240
241         octave = 0;
242         if str[0] in "ABCDEFG":
243                 str = string.lower (str[0]) + str[1:]
244                 octave = -1
245
246
247         notename = 0
248         if str[0] in "abcdefg":
249                 notename = (ord(str[0]) - ord('a') + 5)%7
250                 str = str[1:]
251         else:
252                 return str              # failed; not a note!
253
254         while str[0] == ',':
255                  octave = octave - 1
256                  str = str[1:]
257         while str[0] == '\'':
258                  octave = octave + 1
259                  str = str[1:]
260
261         num = 0
262         den = 0
263
264         (str, num) = parse_num (str)
265         if not num:
266                 num = 1
267         
268         if str[0] == '/':
269                 divide =1
270                 str = str[1:]
271                 (str, den) =parse_num (str)
272
273         if not den: den = 1
274                 
275         print duration_to_mudela_duration ((num,den), default_len)
276         print '%s%s%d' %  (pitch_to_mudela_name(notename, acc + global_key[notename]) , octave_to_mudela_quotes (octave), duration_mult)
277
278         slur_end =0
279         if str[0] == ')':
280                 slur_begin = 1
281                 str = str[1:]
282
283
284         return str
285
286 def junk_space (str):
287         while str and str[0] in '\t\n ':
288                 str = str[1:]
289
290         return str
291
292 def try_parse_bar (str):
293         if str[0] == '|':
294                 str = str[1:]
295         return str
296         
297 def try_parse_body_line (ln):
298         prev_ln = ''
299         while ln and  ln != prev_ln:
300                 prev_ln = ln
301                 ln = try_parse_note  (ln)
302                 ln = try_parse_bar (ln)
303                 ln = junk_space (ln)
304         if ln:
305                 print 'Huh %s' % ln
306                 
307
308 def parse_file (fn):
309         f = open (fn)
310         ls = f.readlines ()
311
312         head = 1
313         for l in ls:
314                 if re.match ('^[\t ]*(%.*)?$', l):
315                         continue
316                 
317                 if head:
318                         m = try_parse_header_line (l)
319                         if not m:
320                                 head = 0
321
322                 if not head:
323                         m = try_parse_body_line (l)
324
325
326 def identify():
327         print '%s %s' % (program_name, version)
328
329 def help ():
330         print r"""
331 This is a disfunctional ABC to mudela convertor.  It only gulps input, and
332 says huh when confused.  Does not do chords.  Go ahead and fix me.
333
334 -h, --help   this help.
335 """
336
337
338
339 identify()
340 (options, files) = getopt.getopt (sys.argv[1:], 'h', ['help'])
341
342 for opt in options:
343         o = opt[0]
344         a = opt[1]
345         if o== '--help' or o == '-h':
346                 help ()
347         else:
348                 print o
349                 raise getopt.error
350
351
352 for f in files:
353         if f == '-':
354                 f = ''
355         
356         parse_file (f)
357         dump_header (header)
358         print global_voice_stuff, 1
359