]> git.donarmstrong.com Git - lilypond.git/blob - python/musicexp.py
MusicXML: Implement pitched rests
[lilypond.git] / python / musicexp.py
1 import inspect
2 import sys
3 import string
4 import re
5
6 from rational import Rational
7
8
9 class Output_stack_element:
10     def __init__ (self):
11         self.factor = Rational (1)
12     def copy (self):
13         o = Output_stack_element()
14         o.factor = self.factor
15         return o
16
17 class Output_printer:
18
19     """A class that takes care of formatting (eg.: indenting) a
20     Music expression as a .ly file.
21     
22     """
23     ## TODO: support for \relative.
24     
25     def __init__ (self):
26         self._line = ''
27         self._indent = 4
28         self._nesting = 0
29         self._file = sys.stdout
30         self._line_len = 72
31         self._output_state_stack = [Output_stack_element()]
32         self._skipspace = False
33         self._last_duration = None
34
35     def set_file (self, file):
36         self._file = file
37         
38     def dump_version (self):
39         self.newline ()
40         self.print_verbatim ('\\version "@TOPLEVEL_VERSION@"')
41         self.newline ()
42         
43     def get_indent (self):
44         return self._nesting * self._indent
45     
46     def override (self):
47         last = self._output_state_stack[-1]
48         self._output_state_stack.append (last.copy())
49         
50     def add_factor (self, factor):
51         self.override()
52         self._output_state_stack[-1].factor *=  factor
53
54     def revert (self):
55         del self._output_state_stack[-1]
56         if not self._output_state_stack:
57             raise 'empty'
58
59     def duration_factor (self):
60         return self._output_state_stack[-1].factor
61
62     def print_verbatim (self, str):
63         self._line += str
64
65     def unformatted_output (self, str):
66         self._nesting += str.count ('<') + str.count ('{')
67         self._nesting -= str.count ('>') + str.count ('}')
68         self.print_verbatim (str)
69         
70     def print_duration_string (self, str):
71         if self._last_duration == str:
72             return
73         
74         self.unformatted_output (str)
75                   
76     def add_word (self, str):
77         if (len (str) + 1 + len (self._line) > self._line_len):
78             self.newline()
79             self._skipspace = True
80
81         if not self._skipspace:
82             self._line += ' '
83         self.unformatted_output (str)
84         self._skipspace = False
85         
86     def newline (self):
87         self._file.write (self._line + '\n')
88         self._line = ' ' * self._indent * self._nesting
89         self._skipspace = True
90
91     def skipspace (self):
92         self._skipspace = True
93         
94     def __call__(self, arg):
95         self.dump (arg)
96     
97     def dump (self, str):
98         if self._skipspace:
99             self._skipspace = False
100             self.unformatted_output (str)
101         else:
102             words = string.split (str)
103             for w in words:
104                 self.add_word (w)
105
106
107     def close (self):
108         self.newline ()
109         self._file.close ()
110         self._file = None
111         
112         
113 class Duration:
114     def __init__ (self):
115         self.duration_log = 0
116         self.dots = 0
117         self.factor = Rational (1)
118         
119     def lisp_expression (self):
120         return '(ly:make-duration %d %d %d %d)' % (self.duration_log,
121                              self.dots,
122                              self.factor.numerator (),
123                              self.factor.denominator ())
124
125
126     def ly_expression (self, factor = None):
127         if not factor:
128             factor = self.factor
129             
130         str = '%d%s' % (1 << self.duration_log, '.'*self.dots)
131
132         if factor <> Rational (1,1):
133             str += '*%d/%d' % (factor.numerator (), factor.denominator ())
134
135         return str
136     
137     def print_ly (self, outputter):
138         str = self.ly_expression (self.factor / outputter.duration_factor ())
139         outputter.print_duration_string (str)
140         
141     def __repr__(self):
142         return self.ly_expression()
143         
144     def copy (self):
145         d = Duration ()
146         d.dots = self.dots
147         d.duration_log = self.duration_log
148         d.factor = self.factor
149         return d
150
151     def get_length (self):
152         dot_fact = Rational( (1 << (1 + self.dots))-1,
153                              1 << self.dots)
154
155         log = abs (self.duration_log)
156         dur = 1 << log
157         if self.duration_log < 0:
158             base = Rational (dur)
159         else:
160             base = Rational (1, dur)
161
162         return base * dot_fact * self.factor
163
164     
165 class Pitch:
166     def __init__ (self):
167         self.alteration = 0
168         self.step = 0
169         self.octave = 0
170         
171     def __repr__(self):
172         return self.ly_expression()
173
174     def transposed (self, interval):
175         c = self.copy ()
176         c.alteration  += interval.alteration
177         c.step += interval.step
178         c.octave += interval.octave
179         c.normalize ()
180         
181         target_st = self.semitones()  + interval.semitones()
182         c.alteration += target_st - c.semitones()
183         return c
184
185     def normalize (c):
186         while c.step < 0:
187             c.step += 7
188             c.octave -= 1
189         c.octave += c.step / 7
190         c.step = c.step  % 7
191
192     def lisp_expression (self):
193         return '(ly:make-pitch %d %d %d)' % (self.octave,
194                                              self.step,
195                                              self.alteration)
196
197     def copy (self):
198         p = Pitch ()
199         p.alteration = self.alteration
200         p.step = self.step
201         p.octave = self.octave 
202         return p
203
204     def steps (self):
205         return self.step + self.octave *7
206
207     def semitones (self):
208         return self.octave * 12 + [0,2,4,5,7,9,11][self.step] + self.alteration
209     
210     def ly_step_expression (self): 
211         str = 'cdefgab'[self.step]
212         if self.alteration > 0:
213             str += 'is'* (self.alteration)
214         elif self.alteration < 0:
215             str += 'es'* (-self.alteration)
216
217         return str.replace ('aes', 'as').replace ('ees', 'es')
218     
219     def ly_expression (self):
220         str = self.ly_step_expression ()
221         if self.octave >= 0:
222             str += "'" * (self.octave + 1) 
223         elif self.octave < -1:
224             str += "," * (-self.octave - 1) 
225             
226         return str
227     
228     def print_ly (self, outputter):
229         outputter (self.ly_expression())
230     
231 class Music:
232     def __init__ (self):
233         self.parent = None
234         self.start = Rational (0)
235         self.comment = ''
236         self.identifier = None
237         
238     def get_length(self):
239         return Rational (0)
240     
241     def get_properties (self):
242         return ''
243     
244     def has_children (self):
245         return False
246     
247     def get_index (self):
248         if self.parent:
249             return self.parent.elements.index (self)
250         else:
251             return None
252     def name (self):
253         return self.__class__.__name__
254     
255     def lisp_expression (self):
256         name = self.name()
257
258         props = self.get_properties ()
259         
260         return "(make-music '%s %s)" % (name,  props)
261
262     def set_start (self, start):
263         self.start = start
264
265     def find_first (self, predicate):
266         if predicate (self):
267             return self
268         return None
269
270     def print_comment (self, printer, text = None):
271         if not text:
272             text = self.comment
273
274         if not text:
275             return
276             
277         if text == '\n':
278             printer.newline ()
279             return
280         
281         lines = string.split (text, '\n')
282         for l in lines:
283             if l:
284                 printer.unformatted_output ('% ' + l)
285             printer.newline ()
286             
287
288     def print_with_identifier (self, printer):
289         if self.identifier: 
290             printer ("\\%s" % self.identifier)
291         else:
292             self.print_ly (printer)
293
294     def print_ly (self, printer):
295         printer (self.ly_expression ())
296
297 class MusicWrapper (Music):
298     def __init__ (self):
299         Music.__init__(self)
300         self.element = None
301     def print_ly (self, func):
302         self.element.print_ly (func)
303
304 class ModeChangingMusicWrapper (MusicWrapper):
305     def __init__ (self):
306         MusicWrapper.__init__ (self)
307         self.mode = 'notemode'
308
309     def print_ly (self, func):
310         func ('\\%s' % self.mode)
311         MusicWrapper.print_ly (self, func)
312
313 class TimeScaledMusic (MusicWrapper):
314     def print_ly (self, func):
315         func ('\\times %d/%d ' %
316            (self.numerator, self.denominator))
317         func.add_factor (Rational (self.numerator, self.denominator))
318         MusicWrapper.print_ly (self, func)
319         func.revert ()
320
321 class NestedMusic(Music):
322     def __init__ (self):
323         Music.__init__ (self)
324         self.elements = []
325
326     def append (self, what):
327         if what:
328             self.elements.append (what)
329             
330     def has_children (self):
331         return self.elements
332
333     def insert_around (self, succ, elt, dir):
334         assert elt.parent == None
335         assert succ == None or succ in self.elements
336
337         
338         idx = 0
339         if succ:
340             idx = self.elements.index (succ)
341             if dir > 0:
342                 idx += 1
343         else:
344             if dir < 0:
345                 idx = 0
346             elif dir > 0:
347                 idx = len (self.elements)
348
349         self.elements.insert (idx, elt)
350         elt.parent = self
351         
352     def get_properties (self):
353         return ("'elements (list %s)"
354             % string.join (map (lambda x: x.lisp_expression(),
355                       self.elements)))
356
357     def get_subset_properties (self, predicate):
358         return ("'elements (list %s)"
359             % string.join (map (lambda x: x.lisp_expression(),
360                       filter ( predicate,  self.elements))))
361     def get_neighbor (self, music, dir):
362         assert music.parent == self
363         idx = self.elements.index (music)
364         idx += dir
365         idx = min (idx, len (self.elements) -1)
366         idx = max (idx, 0)
367
368         return self.elements[idx]
369
370     def delete_element (self, element):
371         assert element in self.elements
372         
373         self.elements.remove (element)
374         element.parent = None
375         
376     def set_start (self, start):
377         self.start = start
378         for e in self.elements:
379             e.set_start (start)
380
381     def find_first (self, predicate):
382         r = Music.find_first (self, predicate)
383         if r:
384             return r
385         
386         for e in self.elements:
387             r = e.find_first (predicate)
388             if r:
389                 return r
390         return None
391         
392 class SequentialMusic (NestedMusic):
393     def print_ly (self, printer):
394         printer ('{')
395         if self.comment:
396             self.print_comment (printer)
397
398         printer.newline()
399         for e in self.elements:
400             e.print_ly (printer)
401
402         printer ('}')
403         printer.newline()
404             
405     def lisp_sub_expression (self, pred):
406         name = self.name()
407
408
409         props = self.get_subset_properties (pred)
410         
411         return "(make-music '%s %s)" % (name,  props)
412     
413     def set_start (self, start):
414         for e in self.elements:
415             e.set_start (start)
416             start += e.get_length()
417
418 class Lyrics:
419     def __init__ (self):
420         self.lyrics_syllables = []
421
422     def print_ly (self, printer):
423         printer.dump ("\lyricmode {")
424         for l in self.lyrics_syllables:
425             printer.dump ( "%s " % l )
426         printer.dump ("}")
427
428     def ly_expression (self):
429         lstr = "\lyricmode {\n  "
430         for l in self.lyrics_syllables:
431             lstr += l + " "
432         lstr += "\n}"
433         return lstr
434
435
436 class EventChord (NestedMusic):
437     def get_length (self):
438         l = Rational (0)
439         for e in self.elements:
440             l = max(l, e.get_length())
441         return l
442     
443     def print_ly (self, printer):
444         note_events = [e for e in self.elements if
445                isinstance (e, NoteEvent)]
446
447         rest_events = [e for e in self.elements if
448                isinstance (e, RhythmicEvent)
449                and not isinstance (e, NoteEvent)]
450         
451         other_events = [e for e in self.elements if
452                 not isinstance (e, RhythmicEvent)]
453
454         if rest_events:
455             rest_events[0].print_ly (printer)
456         elif len (note_events) == 1:
457             note_events[0].print_ly (printer)
458         elif note_events:
459             pitches = [x.pitch.ly_expression () for x in note_events]
460             printer ('<%s>' % string.join (pitches))
461             note_events[0].duration.print_ly (printer)
462         else:
463             pass
464         
465         for e in other_events:
466             e.print_ly (printer)
467
468         self.print_comment (printer)
469             
470
471 class BarCheck (Music):
472     def __init__ (self):
473         Music.__init__ (self)
474         self.bar_number = 0
475         
476     def print_ly (self, printer):
477         if self.bar_number > 0 and (self.bar_number % 10) == 0:
478             printer.dump ("|  \\barNumberCheck #%d " % self.bar_number)
479             printer.newline ()
480         else:
481             printer.dump ("| ")
482             printer.print_verbatim (' %% %d' % self.bar_number)
483             printer.newline ()
484  
485
486     def ly_expression (self):
487         return " | "
488
489 class Event(Music):
490     pass
491
492 class SpanEvent (Event):
493     def __init__(self):
494         Event.__init__ (self)
495         self.span_direction = 0 # start/stop
496         self.line_type = 'solid'
497         self.span_type = 0 # e.g. cres/decrescendo, ottava up/down
498         self.size = 0 # size of e.g. ocrave shift
499     def wait_for_note (self):
500         return True
501     def get_properties(self):
502         return "'span-direction  %d" % self.span_direction
503     def set_span_type (self, type):
504         self.span_type = type
505
506 class SlurEvent (SpanEvent):
507     def ly_expression (self):
508         before = ''
509         after = ''
510         # TODO: setting dashed/dotted line style does not work, because that
511         #       command needs to be written before the note, not when the
512         #       event is observed after the note!
513         #before = {'dotted': '\\slurDotted', 
514         #          'dashed' : '\\slurDashed'}.get (self.line_type, '')
515         #if before:
516             #after = '\\slurSolid'
517
518         return {-1: before + '(' + after,
519             1:')'}.get (self.span_direction, '')
520
521 class BeamEvent (SpanEvent):
522     def ly_expression (self):
523         return {-1: '[',
524             1:']'}.get (self.span_direction, '')
525
526 class PedalEvent (SpanEvent):
527     def ly_expression (self):
528         return {-1: '\\sustainDown',
529             1:'\\sustainUp'}.get (self.span_direction, '')
530
531 # type==-1 means octave up, type==-2 means octave down
532 class OctaveShiftEvent (SpanEvent):
533     def wait_for_note (self):
534         return False;
535     def set_span_type (self, type):
536         self.span_type = {'up': 1, 'down': -1}.get (type, 0)
537     def ly_octave_shift_indicator (self):
538         # convert 8/15 to lilypond indicators (+-1/+-2)
539         value = {8: 1, 15: 2}.get (self.size, 0)
540         # negative values go up!
541         value *= -1*self.span_type
542         return value
543     def ly_expression (self):
544         dir = self.ly_octave_shift_indicator ()
545         value = ''
546         if dir:
547             value = '#(set-octavation %s)' % dir
548         return { 
549             -1: value,
550             1: '#(set-octavation 0)'}.get (self.span_direction, '')
551
552 class TrillSpanEvent (SpanEvent):
553     def ly_expression (self):
554         return {-1: '\\startTrillSpan',
555             0: '', # no need to write out anything for type='continue'
556             1:'\\stopTrillSpan'}.get (self.span_direction, '')
557
558 class GlissandoEvent (SpanEvent):
559     def ly_expression (self):
560         style = ''
561         # TODO: wavy-line glissandos don't work, becasue the style has to be
562         #       set before the note, at the \glissando it's already too late!
563         #if self.line_type == 'wavy':
564             #style = "\once\override Glissando #'style = #'zigzag"
565         # In lilypond, glissando is NOT a spanner, unlike MusicXML.
566         return {-1: style + '\\glissando',
567             1:''}.get (self.span_direction, '')
568
569 class ArpeggioEvent(Event):
570     def wait_for_note (self):
571         return True;
572     def ly_expression (self):
573         return ('\\arpeggio')
574
575
576 class TieEvent(Event):
577     def ly_expression (self):
578         return '~'
579
580
581 class HairpinEvent (SpanEvent):
582     def set_span_type (self, type):
583         self.span_type = {'crescendo' : 1, 'decrescendo' : -1, 'diminuendo' : -1 }.get (type, 0)
584     def hairpin_to_ly (self):
585         if self.span_direction == 1:
586             return '\!'
587         else:
588             return {1: '\<', -1: '\>'}.get (self.span_type, '')
589     
590     def ly_expression (self):
591         return self.hairpin_to_ly ()
592     
593     def print_ly (self, printer):
594         val = self.hairpin_to_ly ()
595         if val:
596             printer.dump (val)
597
598
599
600 class DynamicsEvent (Event):
601     def __init__ (self):
602         self.type = None
603         self.available_commands = [ "ppppp", "pppp", "ppp", "pp", "p", 
604                                     "mp", "mf", 
605                                     "f", "ff", "fff", "ffff", 
606                                     "fp", "sf", "sff", "sp", "spp", "sfz", "rfz" ];
607     def wait_for_note (self):
608         return True;
609     def ly_expression (self):
610         if self.type == None:
611             return;
612         elif self.type in self.available_commands:
613             return '\%s' % self.type
614         else:
615             return '-\markup{ \dynamic %s }' % self.type
616         
617     def print_ly (self, printer):
618         if self.type == None:
619             return
620         elif self.type in self.available_commands:
621             printer.dump ("\\%s" % self.type)
622         else:
623             printer.dump ("-\\markup{ \\dynamic %s }" % self.type)
624
625
626 class ArticulationEvent (Event):
627     def __init__ (self):
628         self.type = None
629         self.force_direction = None
630
631     def direction_mod (self):
632         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '')
633
634     def ly_expression (self):
635         return '%s\\%s' % (self.direction_mod (), self.type)
636
637 class ShortArticulationEvent (ArticulationEvent):
638     def direction_mod (self):
639         # default is -
640         return { 1: '^', -1: '_', 0: '-' }.get (self.force_direction, '-')
641     def ly_expression (self):
642         return '%s%s' % (self.direction_mod (), self.type)
643
644 class TremoloEvent (Event):
645     def __init__ (self):
646         Event.__init__ (self)
647         self.bars = 0
648
649     def ly_expression (self):
650         str=''
651         if self.bars > 0:
652             str += ':%s' % (2 ** (2 + string.atoi (self.bars)))
653         return str
654
655 class BendEvent (Event):
656     def __init__ (self):
657         Event.__init__ (self)
658         self.alter = 0
659     def ly_expression (self):
660         if self.alter:
661             return "-\\bendAfter #%s" % self.alter
662         else:
663             return ''
664
665 class RhythmicEvent(Event):
666     def __init__ (self):
667         Event.__init__ (self)
668         self.duration = Duration()
669         
670     def get_length (self):
671         return self.duration.get_length()
672         
673     def get_properties (self):
674         return ("'duration %s"
675                 % self.duration.lisp_expression ())
676     
677 class RestEvent (RhythmicEvent):
678     def __init__ (self):
679         RhythmicEvent.__init__ (self)
680         self.pitch = None
681     def ly_expression (self):
682         if self.pitch:
683             return "%s%s\\rest" % (self.pitch.ly_expression (), self.duration.ly_expression ())
684         else:
685             return 'r%s' % self.duration.ly_expression ()
686     
687     def print_ly (self, printer):
688         if self.pitch:
689             self.pitch.print_ly (printer)
690             self.duration.print_ly (printer)
691             printer ('\\rest')
692         else:
693             printer('r')
694             self.duration.print_ly (printer)
695
696 class SkipEvent (RhythmicEvent):
697     def ly_expression (self):
698         return 's%s' % self.duration.ly_expression () 
699
700 class NoteEvent(RhythmicEvent):
701     def  __init__ (self):
702         RhythmicEvent.__init__ (self)
703         self.pitch = None
704         self.drum_type = None
705         self.cautionary = False
706         self.forced_accidental = False
707         
708     def get_properties (self):
709         str = RhythmicEvent.get_properties (self)
710         
711         if self.pitch:
712             str += self.pitch.lisp_expression ()
713         elif self.drum_type:
714             str += "'drum-type '%s" % self.drum_type
715
716         return str
717     
718     def pitch_mods (self):
719         excl_question = ''
720         if self.cautionary:
721             excl_question += '?'
722         if self.forced_accidental:
723             excl_question += '!'
724
725         return excl_question
726     
727     def ly_expression (self):
728         if self.pitch:
729             return '%s%s%s' % (self.pitch.ly_expression (),
730                                self.pitch_mods(),
731                                self.duration.ly_expression ())
732         elif self.drum_type:
733             return '%s%s' (self.drum_type,
734                            self.duration.ly_expression ())
735
736     def print_ly (self, printer):
737         if self.pitch:
738             self.pitch.print_ly (printer)
739             printer (self.pitch_mods ())
740         else:
741             printer (self.drum_type)
742
743         self.duration.print_ly (printer)
744
745 class KeySignatureChange (Music):
746     def __init__ (self):
747         Music.__init__ (self)
748         self.scale = []
749         self.tonic = Pitch()
750         self.mode = 'major'
751         
752     def ly_expression (self):
753         return '\\key %s \\%s' % (self.tonic.ly_step_expression (),
754                      self.mode)
755     
756     def lisp_expression (self):
757         pairs = ['(%d . %d)' % (i , self.scale[i]) for i in range (0,7)]
758         scale_str = ("'(%s)" % string.join (pairs))
759
760         return """ (make-music 'KeyChangeEvent
761      'pitch-alist %s) """ % scale_str
762
763 class TimeSignatureChange (Music):
764     def __init__ (self):
765         Music.__init__ (self)
766         self.fraction = (4,4)
767     def ly_expression (self):
768         return '\\time %d/%d ' % self.fraction
769     
770 class ClefChange (Music):
771     def __init__ (self):
772         Music.__init__ (self)
773         self.type = 'G'
774         self.position = 2
775         self.octave = 0
776
777     def octave_modifier (self):
778         return {1: "^8", 2: "^15", -1: "_8", -2: "_15"}.get (self.octave, '')
779     def clef_name (self):
780         return {('G', 2): "treble",
781                 ('G', 1): "french",
782                 ('C', 1): "soprano",
783                 ('C', 2): "mezzosoprano",
784                 ('C', 3): "alto",
785                 ('C', 4): "tenor",
786                 ('C', 5): "baritone",
787                 ('F', 3): "varbaritone",
788                 ('F', 4): "bass",
789                 ('F', 5): "subbass",
790                 ("percussion", 2): "percussion",
791                 ("TAB", 5): "tab"}.get ((self.type, self.position), None)
792     def ly_expression (self):
793         return '\\clef "%s%s"' % (self.clef_name (), self.octave_modifier ())
794
795     clef_dict = {
796         "G": ("clefs.G", -2, -6),
797         "C": ("clefs.C", 0, 0),
798         "F": ("clefs.F", 2, 6),
799         }
800     
801     def lisp_expression (self):
802         try:
803             (glyph, pos, c0) = self.clef_dict[self.type]
804         except KeyError:
805             return ""
806         clefsetting = """
807         (make-music 'SequentialMusic
808         'elements (list
809    (context-spec-music
810    (make-property-set 'clefGlyph "%s") 'Staff)
811    (context-spec-music
812    (make-property-set 'clefPosition %d) 'Staff)
813    (context-spec-music
814    (make-property-set 'middleCPosition %d) 'Staff)))
815 """ % (glyph, pos, c0)
816         return clefsetting
817
818
819 class MultiMeasureRest(Music):
820
821     def lisp_expression (self):
822         return """
823 (make-music
824   'MultiMeasureRestMusicGroup
825   'elements
826   (list (make-music (quote BarCheck))
827         (make-music
828           'EventChord
829           'elements
830           (list (make-music
831                   'MultiMeasureRestEvent
832                   'duration
833                   %s)))
834         (make-music (quote BarCheck))))
835 """ % self.duration.lisp_expression ()
836
837     def ly_expression (self):
838         return 'R%s' % self.duration.ly_expression ()
839
840
841 def test_pitch ():
842     bflat = Pitch()
843     bflat.alteration = -1
844     bflat.step =  6
845     bflat.octave = -1
846     fifth = Pitch()
847     fifth.step = 4
848     down = Pitch ()
849     down.step = -4
850     down.normalize ()
851     
852     
853     print bflat.semitones()
854     print bflat.transposed (fifth),  bflat.transposed (fifth).transposed (fifth)
855     print bflat.transposed (fifth).transposed (fifth).transposed (fifth)
856
857     print bflat.semitones(), 'down'
858     print bflat.transposed (down)
859     print bflat.transposed (down).transposed (down)
860     print bflat.transposed (down).transposed (down).transposed (down)
861
862
863
864 def test_printer ():
865     def make_note ():
866         evc = EventChord()
867         n = NoteEvent()
868         evc.append (n)
869         return n
870
871     def make_tup ():
872         m = SequentialMusic()
873         m.append (make_note ())
874         m.append (make_note ())
875         m.append (make_note ())
876
877         
878         t = TimeScaledMusic ()
879         t.numerator = 2
880         t.denominator = 3
881         t.element = m
882         return t
883
884     m = SequentialMusic ()
885     m.append (make_tup ())
886     m.append (make_tup ())
887     m.append (make_tup ())
888     
889     printer = Output_printer()
890     m.print_ly (printer)
891     printer.newline ()
892     
893 def test_expr ():
894     m = SequentialMusic()
895     l = 2  
896     evc = EventChord()
897     n = NoteEvent()
898     n.duration.duration_log = l
899     n.pitch.step = 1
900     evc.insert_around (None, n, 0)
901     m.insert_around (None, evc, 0)
902
903     evc = EventChord()
904     n = NoteEvent()
905     n.duration.duration_log = l
906     n.pitch.step = 3
907     evc.insert_around (None, n, 0)
908     m.insert_around (None, evc, 0)
909
910     evc = EventChord()
911     n = NoteEvent()
912     n.duration.duration_log = l
913     n.pitch.step = 2 
914     evc.insert_around (None, n, 0)
915     m.insert_around (None, evc, 0)
916
917     evc = ClefChange()
918     evc.type = 'treble'
919     m.insert_around (None, evc, 0)
920
921     evc = EventChord()
922     tonic = Pitch ()
923     tonic.step = 2
924     tonic.alteration = -2
925     n = KeySignatureChange()
926     n.tonic=tonic.copy()
927     n.scale = [0, 0, -2, 0, 0,-2,-2]
928     
929     evc.insert_around (None, n, 0)
930     m.insert_around (None, evc, 0)
931
932     return m
933
934
935 if __name__ == '__main__':
936     test_printer ()
937     raise 'bla'
938     test_pitch()
939     
940     expr = test_expr()
941     expr.set_start (Rational (0))
942     print expr.ly_expression()
943     start = Rational (0,4)
944     stop = Rational (4,2)
945     def sub(x, start=start, stop=stop):
946         ok = x.start >= start and x.start +x.get_length() <= stop
947         return ok
948     
949     print expr.lisp_sub_expression(sub)
950