]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
76f9078a0bd2eca4ba92c2e8bb55ff13d7c44034
[lilypond.git] / python / musicexp.py
1 import inspect
2 import sys
3 import string
4 from rational import Rational
5
6 def flatten_list (fl):
7         if type(fl) == type((1,)):
8                 return 
9         
10         flattened = []
11         for f in fl:
12                 flattened += flatten_list (fl)
13         
14 def is_derived (deriv_class, maybe_base):
15         if deriv_class == maybe_base:
16                 return True
17
18         for c in deriv_class.__bases__:
19                 if is_derived (c, maybe_base):
20                         return True
21
22         return False
23
24 class Output_printer:
25         def __init__ (self):
26                 self.line = ''
27                 self.indent = 0
28                 self.file = sys.stdout
29                 self.line_len = 72
30                 
31         def add_word (self, str):
32                 if (len (str) + 1 + len (self.line) > self.line_len):
33                         self.newline()
34
35                 self.indent += str.count ('<') + str.count ('{')
36                 self.indent -= str.count ('>') + str.count ('}')
37                 self.line += ' ' + str
38                 
39         def newline (self):
40                 self.file.write (self.line + '\n')
41                 self.line = ' ' * self.indent
42                 
43         def dump (self, str):
44                 words = string.split (str)
45                 for w in words:
46                         self.add_word (w)
47                 
48 class Duration:
49         def __init__ (self):
50                 self.duration_log = 2
51                 self.dots = 0
52                 self.factor = Rational (1)
53
54         def lisp_expression (self):
55                 return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
56                                                            self.dots,
57                                                            self.factor.numerator (),
58                                                            self.factor.denominator ())
59
60         def ly_expression (self):
61                 str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
62
63                 if self.factor <> Rational (1,1):
64                         str += '*%d/%d' % (self.factor.numerator (),self.factor.denominator ())
65
66                 return str
67
68         def copy (self):
69                 d = Duration ()
70                 d.dots = self.dots
71                 d.duration_log = self.duration_log
72                 d.factor = self.factor
73                 return d
74
75         def get_length (self):
76                 dot_fact = Rational( (1 << (1 + self.dots))-1,
77                                      1 << self.dots)
78
79                 log = abs (self.duration_log)
80                 dur = 1 << log
81                 if self.duration_log < 0:
82                         base = Rational (dur)
83                 else:
84                         base = Rational (1, dur)
85
86                 return base * dot_fact * self.factor
87         
88 class Pitch:
89         def __init__ (self):
90                 self.alteration = 0
91                 self.step = 0
92                 self.octave = 0
93
94         def lisp_expression (self):
95                 return '(ly:make-pitch %d %d %d)' % (self.octave,
96                                                      self.step,
97                                                      self.alteration)
98
99         def copy (self):
100                 p = Pitch ()
101                 p.alteration = self.alteration
102                 p.step = self.step
103                 p.octave = self.octave 
104                 return p
105
106         def steps (self):
107                 return self.step + self.octave * 7
108         
109         def ly_step_expression (self): 
110                 str = 'cdefgab'[self.step]
111                 if self.alteration > 0:
112                         str += 'is'* (self.alteration)
113                 elif self.alteration < 0:
114                         str += 'es'* (-self.alteration)
115
116                 return str.replace ('aes', 'as').replace ('ees', 'es')
117         
118         def ly_expression (self):
119                 str = self.ly_step_expression ()
120                 if self.octave >= 0:
121                         str += "'" * (self.octave + 1) 
122                 elif self.octave < -1:
123                         str += "," * (-self.octave - 1) 
124                         
125                 return str
126
127 class Music:
128         def __init__ (self):
129                 self.tag = None
130                 self.parent = None
131                 self.start = Rational (0)
132                 pass
133
134         def get_length(self):
135                 return Rational (0)
136         
137         def set_tag (self, counter, tag_dict):
138                 self.tag = counter
139                 tag_dict [counter] = self
140                 return counter + 1
141         
142         def get_properties (self):
143                 return ''
144         
145         def has_children (self):
146                 return False
147         
148         def get_index (self):
149                 if self.parent:
150                         return self.parent.elements.index (self)
151                 else:
152                         return None
153                 
154         def lisp_expression (self):
155                 name = self.name()
156                 tag = ''
157                 if self.tag:
158                         tag = "'input-tag %d" % self.tag
159
160                 props = self.get_properties ()
161 #               props += 'start %f ' % self.start
162                 
163                 return "(make-music '%s %s %s)" % (name, tag,  props)
164
165         def set_start (self, start):
166                 self.start = start
167
168         def find_first (self, predicate):
169                 if predicate (self):
170                         return self
171                 return None
172
173         def print_ly (self, printer):
174                 printer (self.ly_expression ())
175                 
176 class Music_document:
177         def __init__ (self):
178                 self.music = test_expr ()
179                 self.tag_dict = {}
180                 self.touched = True
181                 
182         def recompute (self):
183                 self.tag_dict = {}
184                 self.music.set_tag (0, self.tag_dict)
185                 self.music.set_start (Rational (0))
186                 
187 class NestedMusic(Music):
188         def __init__ (self):
189                 Music.__init__ (self)
190                 self.elements = [] 
191         def has_children (self):
192                 return self.elements
193         def set_tag (self, counter, dict):
194                 counter = Music.set_tag (self, counter, dict)
195                 for e in self.elements :
196                         counter = e.set_tag (counter, dict)
197                 return counter
198
199         def insert_around (self, succ, elt, dir):
200                 assert elt.parent == None
201                 assert succ == None or succ in self.elements
202
203                 
204                 idx = 0
205                 if succ:
206                         idx = self.elements.index (succ)
207                         if dir > 0:
208                                 idx += 1
209                 else:
210                         if dir < 0:
211                                 idx = 0
212                         elif dir > 0:
213                                 idx = len (self.elements)
214
215                 self.elements.insert (idx, elt)
216                 elt.parent = self
217                 
218         def get_properties (self):
219                 return ("'elements (list %s)"
220                         % string.join (map (lambda x: x.lisp_expression(),
221                                             self.elements)))
222
223         def get_subset_properties (self, predicate):
224                 return ("'elements (list %s)"
225                         % string.join (map (lambda x: x.lisp_expression(),
226                                             filter ( predicate,  self.elements))))
227         def get_neighbor (self, music, dir):
228                 assert music.parent == self
229                 idx = self.elements.index (music)
230                 idx += dir
231                 idx = min (idx, len (self.elements) -1)
232                 idx = max (idx, 0)
233
234                 return self.elements[idx]
235
236         def delete_element (self, element):
237                 assert element in self.elements
238                 
239                 self.elements.remove (element)
240                 element.parent = None
241                 
242         def set_start (self, start):
243                 self.start = start
244                 for e in self.elements:
245                         e.set_start (start)
246
247         def find_first (self, predicate):
248                 r = Music.find_first (self, predicate)
249                 if r:
250                         return r
251                 
252                 for e in self.elements:
253                         r = e.find_first (predicate)
254                         if r:
255                                 return r
256                 return None
257                 
258 class SequentialMusic (NestedMusic):
259         def name(self):
260                 return 'SequentialMusic'
261         
262         def print_ly (self, printer):
263                 printer ('{')
264                 for e in self.elements:
265                         e.print_ly (printer)
266                 printer ('}')
267
268         def lisp_sub_expression (self, pred):
269                 name = self.name()
270                 tag = ''
271                 if self.tag:
272                         tag = "'input-tag %d" % self.tag
273
274
275                 props = self.get_subset_properties (pred)
276                 
277                 return "(make-music '%s %s %s)" % (name, tag,  props)
278         
279         def set_start (self, start):
280                 for e in self.elements:
281                         e.set_start (start)
282                         start += e.get_length()
283                         
284 class EventChord(NestedMusic):
285         def name(self):
286                 return "EventChord"
287
288         def get_length (self):
289                 l = Rational (0)
290                 for e in self.elements:
291                         l = max(l, e.get_length())
292                 return l
293         
294         def print_ly (self, printer):
295                 note_events = [e for e in self.elements if
296                                is_derived (e.__class__, NoteEvent)]
297                 rest_events = [e for e in self.elements if
298                                is_derived (e.__class__, RhythmicEvent)
299                                and not is_derived (e.__class__, NoteEvent)]
300                 
301                 other_events = [e for e in self.elements if
302                                 not is_derived (e.__class__, RhythmicEvent)]
303
304                 if rest_events:
305                         printer (rest_events[0].ly_expression ())
306                 elif len (note_events) == 1:
307                         printer (note_events[0].ly_expression ())
308                 elif note_events:
309                         pitches = [x.pitch.ly_expression () for x in note_events]
310                         printer ('<%s>' % string.join (pitches)
311                                  + note_events[0].duration.ly_expression ())
312                 else:
313                         pass
314                 #       print  'huh', rest_events, note_events, other_events
315                         
316                 for e in other_events:
317                         e.print_ly (printer)
318                 
319                         
320 class Event(Music):
321         def __init__ (self):
322                 Music.__init__ (self)
323
324         def name (self):
325                 return "Event"
326
327 class ArpeggioEvent(Music):
328         def name (self):
329                 return 'ArpeggioEvent'
330         
331         def ly_expression (self):
332                 return ('\\arpeggio')
333         
334 class RhythmicEvent(Event):
335         def __init__ (self):
336                 Event.__init__ (self)
337                 self.duration = Duration()
338                 
339         def get_length (self):
340                 return self.duration.get_length()
341                 
342         def get_properties (self):
343                 return ("'duration %s"
344                         % self.duration.lisp_expression ())
345         
346         def name (self):
347                 return 'RhythmicEvent'
348
349 class RestEvent (RhythmicEvent):
350         def name (self):
351                 return 'RestEvent'
352         def ly_expression (self):
353                 return 'r%s' % self.duration.ly_expression ()
354
355 class SkipEvent (RhythmicEvent):
356         def name (self):
357                 return 'SkipEvent'
358         def ly_expression (self):
359                 return 's%s' % self.duration.ly_expression () 
360
361 class NoteEvent(RhythmicEvent):
362         def  __init__ (self):
363                 RhythmicEvent.__init__ (self)
364                 self.pitch = Pitch()
365
366         def name (self):
367                 return 'NoteEvent'
368         
369         def get_properties (self):
370                 return ("'pitch %s\n 'duration %s"
371                         % (self.pitch.lisp_expression (),
372                            self.duration.lisp_expression ()))
373
374         def ly_expression (self):
375                 return '%s%s' % (self.pitch.ly_expression (),
376                                  self.duration.ly_expression ())
377
378
379
380 class KeySignatureEvent (Event):
381         def __init__ (self, tonic, scale):
382                 Event.__init__ (self)
383                 self.scale = scale
384                 self.tonic = tonic
385         def name (self):
386                 return 'KeySignatureEvent'
387         def ly_expression (self):
388                 return '\\key %s \\major' % self.tonic.ly_step_expression ()
389         
390         def lisp_expression (self):
391                 pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
392                 scale_str = ("'(%s)" % string.join (pairs))
393
394                 return """ (make-music 'KeyChangeEvent
395           'pitch-alist %s) """ % scale_str
396
397 class ClefEvent (Event):
398         def __init__ (self, t):
399                 Event.__init__ (self)
400                 self.type = t
401                 
402         def name (self):
403                 return 'ClefEvent'
404         def ly_expression (self):
405                 return '\\clef "%s"' % self.type
406         clef_dict = {
407                 "G": ("clefs.G", -2, -6),
408                 "C": ("clefs.C", 0, 0),
409                 "F": ("clefs.F", 2, 6),
410                 }
411         
412         def lisp_expression (self):
413                 (glyph, pos, c0) = self.clef_dict [self.type]
414                 clefsetting = """
415                 (make-music 'SequentialMusic
416                 'elements (list
417       (context-spec-music
418        (make-property-set 'clefGlyph "%s") 'Staff)
419       (context-spec-music
420        (make-property-set 'clefPosition %d) 'Staff)
421       (context-spec-music
422        (make-property-set 'middleCPosition %d) 'Staff)))
423 """ % (glyph, pos, c0)
424                 return clefsetting
425
426 def test_expr ():
427         m = SequentialMusic()
428         l = 2  
429         evc = EventChord()
430         n = NoteEvent()
431         n.duration.duration_log = l
432         n.pitch.step = 1
433         evc.insert_around (None, n, 0)
434         m.insert_around (None, evc, 0)
435
436         evc = EventChord()
437         n = NoteEvent()
438         n.duration.duration_log = l
439         n.pitch.step = 3
440         evc.insert_around (None, n, 0)
441         m.insert_around (None, evc, 0)
442
443         evc = EventChord()
444         n = NoteEvent()
445         n.duration.duration_log = l
446         n.pitch.step = 2 
447         evc.insert_around (None, n, 0)
448         m.insert_around (None, evc, 0)
449
450         evc = ClefEvent("G")
451         m.insert_around (None, evc, 0)
452
453         evc = EventChord()
454         tonic = Pitch ()
455         tonic.step = 2
456         tonic.alteration = -2
457         n = KeySignatureEvent(tonic, [0, 0, -2, 0, 0,-2,-2]  )
458         evc.insert_around (None, n, 0)
459         m.insert_around (None, evc, 0)
460
461         return m
462
463
464 if __name__ == '__main__':
465         expr = test_expr()
466         expr.set_start (Rational (0))
467         print expr.ly_expression()
468         start = Rational (0,4)
469         stop = Rational (4,2)
470         def sub(x, start=start, stop=stop):
471                 ok = x.start >= start and x.start +x.get_length() <= stop
472                 return ok
473         
474         print expr.lisp_sub_expression(sub)
475