1 # -*- coding: utf-8 -*-
3 converstion d'un fichier musicxml en objet song minwii.
9 from types
import StringTypes
10 from xml
.dom
.minidom
import parse
11 from optparse
import OptionParser
16 DIATO_SCALE
= {'C' : 60,
27 def __init__(self
, node
, autoDetectChorus
=True) :
35 self
._findVersesLoops
()
37 def _parseMusic(self
) :
40 next
= previous
= None
41 for measureNode
in self
.node
.getElementsByTagName('measure') :
42 # divisions de la noire
43 divisions
= int(_getNodeValue(measureNode
, 'attributes/divisions', divisions
))
44 for noteNode
in measureNode
.getElementsByTagName('note') :
45 note
= Note(noteNode
, divisions
, previous
)
46 self
.notes
.append(note
)
48 self
.notes
[noteIndex
-1].next
= note
54 def _findChorus(self
):
55 """ le refrain correspond aux notes pour lesquelles
56 il n'existe q'une seule syllable attachée.
59 for i
, note
in enumerate(self
.notes
) :
61 if start
is None and ll
== 1 :
63 elif start
is not None and ll
> 1 :
66 self
.chorus
= self
.notes
[start
:stop
]
68 def _findVersesLoops(self
) :
69 "recherche des couplets / boucles"
70 verse
= self
.verses
[0]
71 for note
in self
.notes
[:-1] :
74 nll
= len(note
.next
.lyrics
)
77 self
.verses
.append(verse
)
78 verse
.append(self
.notes
[-1])
82 "exécution de la chanson avec l'alternance couplets / refrains"
83 for verse
in self
.verses
:
84 repeats
= len(verse
[0].lyrics
)
86 for i
in range(repeats
) :
91 for note
in self
.chorus
:
98 for note
, verseIndex
in self
.iterNotes() :
99 print note
.name
, note
.midi
, note
.duration
, note
.lyrics
[verseIndex
]
104 def __init__(self
, node
, divisions
, previous
) :
106 self
.step
= _getNodeValue(node
, 'pitch/step')
107 self
.octave
= int(_getNodeValue(node
, 'pitch/octave'))
108 self
.alter
= int(_getNodeValue(node
, 'pitch/alter', 0))
109 self
._duration
= float(_getNodeValue(node
, 'duration'))
111 for ly
in node
.getElementsByTagName('lyric') :
112 self
.lyrics
.append(Lyric(ly
))
114 self
.divisions
= divisions
115 self
.previous
= previous
120 mid
= DIATO_SCALE
[self
.step
]
121 mid
= mid
+ (self
.octave
- OCTAVE_REF
) * 12
122 mid
= mid
+ self
.alter
127 return self
._duration
/ self
.divisions
131 name
= '%s%d' % (self
.step
, self
.octave
)
136 name
= '%s%s' % (name
, abs(self
.alter
) * alterext
)
140 class Lyric(object) :
142 _syllabicModifiers
= {
149 def __init__(self
, node
) :
151 self
.syllabic
= _getNodeValue(node
, 'syllabic', 'single')
152 self
.text
= _getNodeValue(node
, 'text')
155 text
= self
._syllabicModifiers
[self
.syllabic
] % self
.text
156 return text
.encode('utf-8')
162 def _getNodeValue(node
, path
, default
=_marker
) :
164 for name
in path
.split('/') :
165 node
= node
.getElementsByTagName(name
)[0]
166 return node
.firstChild
.nodeValue
168 if default
is _marker
:
173 def musicXml2Song(input, output
, partIndex
=0, printNotes
=False) :
174 if isinstance(input, StringTypes
) :
175 input = open(input, 'r')
178 doc
= d
.documentElement
180 # TODO conversion préalable score-timewise -> score-partwise
181 assert doc
.nodeName
== u
'score-partwise'
183 parts
= doc
.getElementsByTagName('part')
184 leadPart
= parts
[partIndex
]
186 part
= Part(leadPart
)
191 # divisions de la noire
193 # midiNotes, durations, lyrics = [], [], []
195 # for measureNode in leadPart.getElementsByTagName('measure') :
196 # divisions = int(_getNodeValue(measureNode, 'attributes/divisions', divisions))
197 # for noteNode in measureNode.getElementsByTagName('note') :
198 # note = Note(noteNode, divisions)
200 # print note.name, note.midi, note.duration, note.lyric
201 # midiNotes.append(note.midi)
202 # durations.append(note.duration)
203 # lyrics.append(note.lyric)
206 # midiNoteNumbers = midiNotes,
207 # noteLengths = durations,
209 # notesInExtendedScale=None)
214 usage
= "%prog musicXmlFile.xml outputSongFile.smwi [options]"
215 op
= OptionParser(usage
)
216 op
.add_option("-i", "--part-index", dest
="partIndex"
218 , help = "Index de la partie qui contient le champ.")
219 op
.add_option("-p", '--print', dest
='printNotes'
220 , action
="store_true"
222 , help = "Affiche les notes sur la sortie standard (debug)")
224 options
, args
= op
.parse_args()
227 raise SystemExit(op
.format_help())
229 musicXml2Song(args
[0], args
[1], partIndex
=options
.partIndex
, printNotes
=options
.printNotes
)
233 if __name__
== '__main__' :