0db98be7809cd50e7d3a86fa3ee751f4032842ce
[minwii.git] / src / songs / musicxmltosong.py
1 # -*- coding: utf-8 -*-
2 """
3 converstion d'un fichier musicxml en objet song minwii.
4
5 $Id$
6 $URL$
7 """
8 import sys
9 from types import StringTypes
10 from xml.dom.minidom import parse
11 from optparse import OptionParser
12 from Song import Song
13
14 # Do4 <=> midi 60
15 OCTAVE_REF = 4
16 DIATO_SCALE = {'C' : 60,
17 'D' : 62,
18 'E' : 64,
19 'F' : 65,
20 'G' : 67,
21 'A' : 69,
22 'B' : 71}
23 _marker = []
24
25
26 class Note(object) :
27 def __init__(self, node, divisions) :
28 self.step = _getNodeValue(node, 'pitch/step')
29 self.octave = int(_getNodeValue(node, 'pitch/octave'))
30 self._duration = float(_getNodeValue(node, 'duration'))
31 self.lyric = _getNodeValue(node, 'lyric/text')
32
33 self.divisions = divisions
34
35 @property
36 def midi(self) :
37 mid = DIATO_SCALE[self.step]
38 mid = mid + (self.octave - OCTAVE_REF) * 12
39 return mid
40
41 @property
42 def duration(self) :
43 return self._duration / self.divisions
44
45
46
47 def _getNodeValue(node, path, default=_marker) :
48 try :
49 for name in path.split('/') :
50 node = node.getElementsByTagName(name)[0]
51 return node.firstChild.nodeValue
52 except :
53 if default is _marker :
54 raise
55 else :
56 return default
57
58 def musicXml2Song(input, output, partIndex=0) :
59 if isinstance(input, StringTypes) :
60 input = open(input, 'r')
61
62 d = parse(input)
63 doc = d.documentElement
64
65 # TODO conversion préalable score-timewise -> score-partwise
66 assert doc.nodeName == u'score-partwise'
67
68 parts = doc.getElementsByTagName('part')
69 leadPart = parts[partIndex]
70
71 # divisions de la noire
72 divisions = 0
73 midiNotes, durations, lyrics = [], [], []
74
75 for measureNode in leadPart.getElementsByTagName('measure') :
76 divisions = int(_getNodeValue(measureNode, 'attributes/divisions', divisions))
77 for noteNode in measureNode.getElementsByTagName('note') :
78 note = Note(noteNode, divisions)
79 midiNotes.append(note.midi)
80 durations.append(note.duration)
81 lyrics.append(note.lyric)
82
83 song = Song(None,
84 midiNoteNumbers = midiNotes,
85 noteLengths = durations,
86 lyrics = lyrics,
87 notesInExtendedScale=None)
88 song.save(output)
89
90
91 def main() :
92 usage = "%prog musicXmlFile.xml outputSongFile.smwi [options]"
93 op = OptionParser(usage)
94 op.add_option("-i", "--part-index", dest="partIndex"
95 , default = 0
96 , help = "Index de la partie qui contient le champ.")
97
98 options, args = op.parse_args()
99
100 if len(args) != 2 :
101 raise SystemExit(op.format_help())
102
103 musicXml2Song(*args)
104
105
106
107 if __name__ == '__main__' :
108 sys.exit(main())