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
12 #from Song import Song
16 DIATO_SCALE
= {'C' : 60,
24 FR_NOTES
= {'C' : u
'Do',
36 requiresExtendedScale
= False
37 scale
= [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]
38 quarterNoteLength
= 400
40 def __init__(self
, node
, autoDetectChorus
=True) :
48 self
._findVersesLoops
()
50 def _parseMusic(self
) :
53 next
= previous
= None
54 for measureNode
in self
.node
.getElementsByTagName('measure') :
55 # divisions de la noire
56 divisions
= int(_getNodeValue(measureNode
, 'attributes/divisions', divisions
))
57 for noteNode
in measureNode
.getElementsByTagName('note') :
58 note
= Note(noteNode
, divisions
, previous
)
59 self
.notes
.append(note
)
61 self
.notes
[noteIndex
-1].next
= note
67 def _findChorus(self
):
68 """ le refrain correspond aux notes pour lesquelles
69 il n'existe q'une seule syllable attachée.
72 for i
, note
in enumerate(self
.notes
) :
74 if start
is None and ll
== 1 :
76 elif start
is not None and ll
> 1 :
79 self
.chorus
= self
.notes
[start
:stop
]
81 def _findVersesLoops(self
) :
82 "recherche des couplets / boucles"
83 verse
= self
.verses
[0]
84 for note
in self
.notes
[:-1] :
87 nll
= len(note
.next
.lyrics
)
90 self
.verses
.append(verse
)
91 verse
.append(self
.notes
[-1])
95 "exécution de la chanson avec l'alternance couplets / refrains"
96 for verse
in self
.verses
:
98 repeats
= len(verse
[0].lyrics
)
100 for i
in range(repeats
) :
102 print "---couplet%d---" % i
106 print "---refrain---"
107 for note
in self
.chorus
:
114 for note
, verseIndex
in self
.iterNotes() :
115 print note
.nom
, note
.name
, note
.midi
, note
.duration
, note
.lyrics
[verseIndex
]
118 def assignNotesFromMidiNoteNumbers(self
):
119 # TODO faire le mapping bande hauteur midi
120 for i
in range(len(self
.midiNoteNumbers
)):
121 noteInExtendedScale
= 0
122 while self
.midiNoteNumbers
[i
] > self
.scale
[noteInExtendedScale
] and noteInExtendedScale
< len(self
.scale
)-1:
123 noteInExtendedScale
+= 1
124 if self
.midiNoteNumbers
[i
]<self
.scale
[noteInExtendedScale
]:
125 noteInExtendedScale
-= 1
126 self
.notes
.append(noteInExtendedScale
)
132 scale
= [55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72]
134 def __init__(self
, node
, divisions
, previous
) :
136 self
.step
= _getNodeValue(node
, 'pitch/step')
137 self
.octave
= int(_getNodeValue(node
, 'pitch/octave'))
138 self
.alter
= int(_getNodeValue(node
, 'pitch/alter', 0))
139 self
._duration
= float(_getNodeValue(node
, 'duration'))
141 for ly
in node
.getElementsByTagName('lyric') :
142 self
.lyrics
.append(Lyric(ly
))
144 self
.divisions
= divisions
145 self
.previous
= previous
150 mid
= DIATO_SCALE
[self
.step
]
151 mid
= mid
+ (self
.octave
- OCTAVE_REF
) * 12
152 mid
= mid
+ self
.alter
157 return self
._duration
/ self
.divisions
161 name
= '%s%d' % (self
.step
, self
.octave
)
166 name
= '%s%s' % (name
, abs(self
.alter
) * alterext
)
171 name
= FR_NOTES
[self
.step
]
176 name
= '%s%s' % (name
, abs(self
.alter
) * alterext
)
181 return self
.scale
.index(self
.midi
)
184 class Lyric(object) :
186 _syllabicModifiers
= {
193 def __init__(self
, node
) :
195 self
.syllabic
= _getNodeValue(node
, 'syllabic', 'single')
196 self
.text
= _getNodeValue(node
, 'text')
199 text
= self
._syllabicModifiers
[self
.syllabic
] % self
.text
200 return text
.encode('utf-8')
206 def _getNodeValue(node
, path
, default
=_marker
) :
208 for name
in path
.split('/') :
209 node
= node
.getElementsByTagName(name
)[0]
210 return node
.firstChild
.nodeValue
212 if default
is _marker
:
217 def musicXml2Song(input, partIndex
=0, printNotes
=False) :
218 if isinstance(input, StringTypes
) :
219 input = open(input, 'r')
222 doc
= d
.documentElement
224 # TODO conversion préalable score-timewise -> score-partwise
225 assert doc
.nodeName
== u
'score-partwise'
227 parts
= doc
.getElementsByTagName('part')
228 leadPart
= parts
[partIndex
]
230 part
= Part(leadPart
)
238 # divisions de la noire
240 # midiNotes, durations, lyrics = [], [], []
242 # for measureNode in leadPart.getElementsByTagName('measure') :
243 # divisions = int(_getNodeValue(measureNode, 'attributes/divisions', divisions))
244 # for noteNode in measureNode.getElementsByTagName('note') :
245 # note = Note(noteNode, divisions)
247 # print note.name, note.midi, note.duration, note.lyric
248 # midiNotes.append(note.midi)
249 # durations.append(note.duration)
250 # lyrics.append(note.lyric)
253 # midiNoteNumbers = midiNotes,
254 # noteLengths = durations,
256 # notesInExtendedScale=None)
261 usage
= "%prog musicXmlFile.xml outputSongFile.smwi [options]"
262 op
= OptionParser(usage
)
263 op
.add_option("-i", "--part-index", dest
="partIndex"
265 , help = "Index de la partie qui contient le champ.")
266 op
.add_option("-p", '--print', dest
='printNotes'
267 , action
="store_true"
269 , help = "Affiche les notes sur la sortie standard (debug)")
271 options
, args
= op
.parse_args()
274 raise SystemExit(op
.format_help())
276 musicXml2Song(args
[0], partIndex
=options
.partIndex
, printNotes
=options
.printNotes
)
280 if __name__
== '__main__' :