+ chansons dans svn:ignore.
[minwii.git] / src / minwii / musicxml.py
index 0ed7085..54d859f 100755 (executable)
@@ -57,9 +57,8 @@ class Part(object) :
         self._parseMusic()
         self.verses = [[]]
         self.chorus = []
-        if autoDetectChorus :
-            self._findChorus()
-        self._findVersesLoops()
+        self.songStartsWithChorus = False
+        self._findVersesLoops(autoDetectChorus)
     
     def _parseMusic(self) :
         divisions = 0
@@ -83,7 +82,13 @@ class Part(object) :
                     previous.addDuration(note)
                     continue
                 else :
-                    previous.addDuration(note)
+                    try :
+                        previous.addDuration(note)
+                    except AttributeError :
+                        # can occur if part starts with a rest.
+                        if previous is not None :
+                            # something else is wrong.
+                            raise 
                     continue
                 previous = note
 
@@ -115,25 +120,7 @@ class Part(object) :
         self.quarterNoteDuration = int(round(60000/tempo))
         
         
-
-    def _findChorus(self):
-        """ le refrain correspond aux notes pour lesquelles
-            il n'existe q'une seule syllable attachée.
-        """
-        start = stop = None
-        for i, note in enumerate(self.notes) :
-            ll = len(note.lyrics)
-            if start is None and ll == 1 :
-                start = i
-            elif start is not None and ll > 1 :
-                stop = i
-                break
-        if not (start or stop) :
-            self.chorus = []
-        else :
-            self.chorus = self.notes[start:stop]
-    
-    def _findVersesLoops(self) :
+    def _findVersesLoops(self, autoDetectChorus) :
         "recherche des couplets / boucles"
         verse = self.verses[0]
         for note in self.notes[:-1] :
@@ -145,14 +132,21 @@ class Part(object) :
                 self.verses.append(verse)
         verse.append(self.notes[-1])
         
+        if autoDetectChorus and len(self.verses) > 1 :
+            for i, verse in enumerate(self.verses) :
+                if len(verse[0].lyrics) == 1 :
+                    self.chorus = self.verses.pop(i)
+                    self.songStartsWithChorus = i==0
+                    break
+        
     
-    def iterNotes(self, indefinitely=True) :
+    def iterNotes(self) :
         "exécution de la chanson avec l'alternance couplets / refrains"
-        if indefinitely == False :
-            iterable = self.verses
-        else :
-            iterable = cycle(self.verses)
-        for verse in iterable :
+        for verse in self.verses :
+            if self.songStartsWithChorus :
+                for note in self.chorus :
+                    yield note, 0
+                
             #print "---partie---"
             repeats = len(verse[0].lyrics)
             if repeats > 1 :
@@ -168,6 +162,31 @@ class Part(object) :
             else :
                 for note in verse :
                     yield note, 0
+    
+    @property
+    def intervalsHistogram(self) :
+        histogram = {}
+        it = self.iterNotes()
+        previousNote = it.next()[0]
+        for note, _ in it :
+            interval = note.midi - previousNote.midi
+            if histogram.has_key(interval) :
+                histogram[interval] += 1
+            else :
+                histogram[interval] = 1
+            previousNote = note
+        return histogram
+    
+    @property
+    def duration(self) :
+        'Durée de référence du morceau en milisecondes'
+        it = self.iterNotes()
+        duration = 0
+        for note, verseIndex in it :
+            duration = duration + note.duration
+        duration = duration * self.quarterNoteDuration # en milisecondes
+        return duration
+        
         
     def pprint(self) :
         for note, verseIndex in self.iterNotes(indefinitely=False) :
@@ -371,7 +390,8 @@ def musicXml2Song(input, partIndex=0, autoDetectChorus=True, printNotes=False) :
     doc = d.documentElement
     
     # TODO conversion préalable score-timewise -> score-partwise
-    assert doc.nodeName == u'score-partwise'
+    if doc.nodeName != u'score-partwise' :
+        raise ValueError('not a musicxml file')
     
     parts = doc.getElementsByTagName('part')
     leadPart = parts[partIndex]
@@ -408,10 +428,13 @@ def main() :
     if len(args) != 1 :
         raise SystemExit(op.format_help())
     
-    musicXml2Song(args[0],
+    song = musicXml2Song(args[0],
                   partIndex=options.partIndex,
                   autoDetectChorus=options.autoDetectChorus,
                   printNotes=options.printNotes)
+    from pprint import pprint
+    pprint(song.intervalsHistogram)
+    print song.duration
 
 
 if __name__ == '__main__' :