qq refactoring avant prise en charge des répétitions.
[minwii.git] / src / songs / musicxmltosong.py
index 39df188..068017f 100755 (executable)
@@ -9,6 +9,7 @@ import sys
 from types import StringTypes
 from xml.dom.minidom import parse
 from optparse import OptionParser
 from types import StringTypes
 from xml.dom.minidom import parse
 from optparse import OptionParser
+from itertools import cycle
 #from Song import Song
 
 # Do4 <=> midi 60
 #from Song import Song
 
 # Do4 <=> midi 60
@@ -33,6 +34,10 @@ _marker = []
 
 class Part(object) :
     
 
 class Part(object) :
     
+    requiresExtendedScale = False
+    scale = [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]
+    quarterNoteLength = 400
+    
     def __init__(self, node, autoDetectChorus=True) :
         self.node = node
         self.notes = []
     def __init__(self, node, autoDetectChorus=True) :
         self.node = node
         self.notes = []
@@ -45,20 +50,24 @@ class Part(object) :
     
     def _parseMusic(self) :
         divisions = 0
     
     def _parseMusic(self) :
         divisions = 0
-        noteIndex = 0
-        next = previous = None
+        previous = None
+
         for measureNode in self.node.getElementsByTagName('measure') :
         for measureNode in self.node.getElementsByTagName('measure') :
+            measureNotes = []
             # divisions de la noire
             divisions = int(_getNodeValue(measureNode, 'attributes/divisions', divisions))
             for noteNode in measureNode.getElementsByTagName('note') :
                 note = Note(noteNode, divisions, previous)
             # divisions de la noire
             divisions = int(_getNodeValue(measureNode, 'attributes/divisions', divisions))
             for noteNode in measureNode.getElementsByTagName('note') :
                 note = Note(noteNode, divisions, previous)
-                self.notes.append(note)
-                try :
-                    self.notes[noteIndex-1].next = note
-                except IndexError:
-                    pass
+                if not note.isRest :
+                    measureNotes.append(note)
+                    if previous :
+                        previous.next = note
+                else :
+                    previous.addDuration(note)
+                    continue
                 previous = note
                 previous = note
-                noteIndex += 1
+
+            self.notes.extend(measureNotes)
 
     def _findChorus(self):
         """ le refrain correspond aux notes pour lesquelles
 
     def _findChorus(self):
         """ le refrain correspond aux notes pour lesquelles
@@ -108,16 +117,50 @@ class Part(object) :
         
     def pprint(self) :
         for note, verseIndex in self.iterNotes() :
         
     def pprint(self) :
         for note, verseIndex in self.iterNotes() :
-            print note.nom, note.name, note.midi, note.duration, note.lyrics[verseIndex]
+            print note, note.lyrics[verseIndex]
+
+
+    def assignNotesFromMidiNoteNumbers(self):
+        # TODO faire le mapping bande hauteur midi
+        for i in range(len(self.midiNoteNumbers)):
+            noteInExtendedScale = 0
+            while self.midiNoteNumbers[i] > self.scale[noteInExtendedScale] and noteInExtendedScale < len(self.scale)-1:
+                noteInExtendedScale += 1
+            if self.midiNoteNumbers[i]<self.scale[noteInExtendedScale]:
+                noteInExtendedScale -= 1
+            self.notes.append(noteInExtendedScale)
+
+
+class Barline(object) :
+
+    def __init__(self, node) :
+        self.node = node
+        self.location = node.getAttribute('location')
+        try :
+            repeat = node.getElementsByTagName('repeat')[0]
+            repeat = {'direction' : repeat.getAttribute('direction'),
+                      'times' : int(repeat.getAttribute('times') or 1)}
+            self.repeat = repeat
+        except IndexError :
+            self.repeat = None
         
         
 
 class Note(object) :
         
         
 
 class Note(object) :
+    scale = [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]
+    
     def __init__(self, node, divisions, previous) :
         self.node = node
     def __init__(self, node, divisions, previous) :
         self.node = node
-        self.step = _getNodeValue(node, 'pitch/step')
-        self.octave = int(_getNodeValue(node, 'pitch/octave'))
-        self.alter = int(_getNodeValue(node, 'pitch/alter', 0))
+        self.isRest = False
+        self.step = _getNodeValue(node, 'pitch/step', None)
+        if self.step is not None :
+            self.octave = int(_getNodeValue(node, 'pitch/octave'))
+            self.alter = int(_getNodeValue(node, 'pitch/alter', 0))
+        elif self.node.getElementsByTagName('rest') :
+            self.isRest = True
+        else :
+            NotImplementedError(self.node.toxml('utf-8'))
+            
         self._duration = float(_getNodeValue(node, 'duration'))
         self.lyrics = []
         for ly in node.getElementsByTagName('lyric') :
         self._duration = float(_getNodeValue(node, 'duration'))
         self.lyrics = []
         for ly in node.getElementsByTagName('lyric') :
@@ -127,6 +170,15 @@ class Note(object) :
         self.previous = previous
         self.next = None
     
         self.previous = previous
         self.next = None
     
+    def __str__(self) :
+        return (u'%5s %2s %2d %4s' % (self.nom, self.name, self.midi, round(self.duration, 2))).encode('utf-8')
+    
+    __repr__ = __str__
+    
+    def addDuration(self, note) :
+        self._duration = self.duration + note.duration
+        self.divisions = 1
+    
     @property
     def midi(self) :
         mid = DIATO_SCALE[self.step]
     @property
     def midi(self) :
         mid = DIATO_SCALE[self.step]
@@ -158,6 +210,10 @@ class Note(object) :
         name = '%s%s' % (name, abs(self.alter) * alterext)
         return name
     
         name = '%s%s' % (name, abs(self.alter) * alterext)
         return name
     
+    @property
+    def column(self):
+        return self.scale.index(self.midi)
+    
 
 class Lyric(object) :
     
 
 class Lyric(object) :