X-Git-Url: https://scm.cri.mines-paristech.fr/git/minwii.git/blobdiff_plain/1bdf29a0b4412135ee91f6165e3c3125e05096f8..da27397af045e89d82771d70851ec7cc9d158f70:/src/app/musicxml.py diff --git a/src/app/musicxml.py b/src/app/musicxml.py index 49e1984..0ed7085 100755 --- a/src/app/musicxml.py +++ b/src/app/musicxml.py @@ -53,6 +53,7 @@ class Part(object) : self.notes = [] self.repeats = [] self.distinctNotes = [] + self.quarterNoteDuration = 500 self._parseMusic() self.verses = [[]] self.chorus = [] @@ -73,10 +74,14 @@ class Part(object) : divisions = int(_getNodeValue(measureNode, 'attributes/divisions', divisions)) for noteNode in measureNode.getElementsByTagName('note') : note = Note(noteNode, divisions, previous) - if not note.isRest : + if (not note.isRest) and (not note.tiedStop) : measureNotes.append(note) if previous : previous.next = note + elif note.tiedStop : + assert previous.tiedStart + previous.addDuration(note) + continue else : previous.addDuration(note) continue @@ -100,6 +105,15 @@ class Part(object) : self.repeats.append(barline) self.distinctNotes.sort(lambda a, b : cmp(a.midi, b.midi)) + sounds = self.node.getElementsByTagName('sound') + tempo = 120 + for sound in sounds : + if sound.hasAttribute('tempo') : + tempo = float(sound.getAttribute('tempo')) + break + + self.quarterNoteDuration = int(round(60000/tempo)) + def _findChorus(self): @@ -114,7 +128,10 @@ class Part(object) : elif start is not None and ll > 1 : stop = i break - self.chorus = self.notes[start:stop] + if not (start or stop) : + self.chorus = [] + else : + self.chorus = self.notes[start:stop] def _findVersesLoops(self) : "recherche des couplets / boucles" @@ -131,22 +148,21 @@ class Part(object) : def iterNotes(self, indefinitely=True) : "exécution de la chanson avec l'alternance couplets / refrains" - print 'indefinitely', indefinitely if indefinitely == False : iterable = self.verses else : iterable = cycle(self.verses) for verse in iterable : - print "---partie---" + #print "---partie---" repeats = len(verse[0].lyrics) if repeats > 1 : for i in range(repeats) : # couplet - print "---couplet%d---" % i + #print "---couplet%d---" % i for note in verse : yield note, i # refrain - print "---refrain---" + #print "---refrain---" for note in self.chorus : yield note, 0 else : @@ -226,7 +242,7 @@ class Tone(object) : @property def name(self) : - name = '%s%d' % (self.step, self.octave) + name = u'%s%d' % (self.step, self.octave) if self.alter < 0 : alterext = 'b' else : @@ -238,10 +254,10 @@ class Tone(object) : def nom(self) : name = FR_NOTES[self.step] if self.alter < 0 : - alterext = 'b' + alterext = u'♭' else : - alterext = '#' - name = '%s%s' % (name, abs(self.alter) * alterext) + alterext = u'#' + name = u'%s%s' % (name, abs(self.alter) * alterext) return name @@ -252,6 +268,16 @@ class Note(Tone) : def __init__(self, node, divisions, previous) : self.node = node self.isRest = False + self.tiedStart = False + self.tiedStop = False + + tieds = _getElementsByPath(node, 'notations/tied', []) + for tied in tieds : + if tied.getAttribute('type') == 'start' : + self.tiedStart = True + elif tied.getAttribute('type') == 'stop' : + self.tiedStop = True + self.step = _getNodeValue(node, 'pitch/step', None) if self.step is not None : self.octave = int(_getNodeValue(node, 'pitch/octave')) @@ -292,10 +318,10 @@ class Note(Tone) : class Lyric(object) : _syllabicModifiers = { - 'single' : '%s', - 'begin' : '%s -', - 'middle' : '- %s -', - 'end' : '- %s' + 'single' : u'%s', + 'begin' : u'%s -', + 'middle' : u'- %s -', + 'end' : u'- %s' } def __init__(self, node) : @@ -303,12 +329,12 @@ class Lyric(object) : self.syllabic = _getNodeValue(node, 'syllabic', 'single') self.text = _getNodeValue(node, 'text') - def syllabus(self, encoding='utf-8'): + def syllabus(self): text = self._syllabicModifiers[self.syllabic] % self.text - return text.encode(encoding) + return text def __str__(self) : - return self.syllabus() + return self.syllabus().encode('utf-8') __repr__ = __str__ @@ -325,7 +351,19 @@ def _getNodeValue(node, path, default=_marker) : else : return default -def musicXml2Song(input, partIndex=0, printNotes=False) : +def _getElementsByPath(node, path, default=_marker) : + try : + parts = path.split('/') + for name in parts[:-1] : + node = node.getElementsByTagName(name)[0] + return node.getElementsByTagName(parts[-1]) + except IndexError : + if default is _marker : + raise + else : + return default + +def musicXml2Song(input, partIndex=0, autoDetectChorus=True, printNotes=False) : if isinstance(input, StringTypes) : input = open(input, 'r') @@ -338,7 +376,7 @@ def musicXml2Song(input, partIndex=0, printNotes=False) : parts = doc.getElementsByTagName('part') leadPart = parts[partIndex] - part = Part(leadPart) + part = Part(leadPart, autoDetectChorus=autoDetectChorus) if printNotes : part.pprint() @@ -353,18 +391,27 @@ def main() : op.add_option("-i", "--part-index", dest="partIndex" , default = 0 , help = "Index de la partie qui contient le champ.") + op.add_option("-p", '--print', dest='printNotes' , action="store_true" , default = False , help = "Affiche les notes sur la sortie standard (debug)") + + op.add_option("-c", '--no-chorus', dest='autoDetectChorus' + , action="store_false" + , default = True + , help = "désactive la détection du refrain") + options, args = op.parse_args() if len(args) != 1 : raise SystemExit(op.format_help()) - musicXml2Song(args[0], partIndex=options.partIndex, printNotes=options.printNotes) - + musicXml2Song(args[0], + partIndex=options.partIndex, + autoDetectChorus=options.autoDetectChorus, + printNotes=options.printNotes) if __name__ == '__main__' :