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