Mercurial > hg > multiomr
comparison Process/SymbolConversion.py @ 2:46fb79167a61 tip
Main Code
author | Victor Padilla <victor.padilla.mc@gmail.com> |
---|---|
date | Mon, 04 May 2015 22:56:18 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1:0f7f611deca4 | 2:46fb79167a61 |
---|---|
1 ''' | |
2 Created on 10/11/2014 | |
3 | |
4 @organization: Lancaster University & University of Leeds | |
5 @version: 1.0 | |
6 Created on 11/12/2014 | |
7 | |
8 @author: Victor Padilla | |
9 @contact: v.padilla@lancaster.ac.uk | |
10 | |
11 Functions related to convert music21 output | |
12 to symbols for aligning and voting | |
13 ''' | |
14 from music21 import note | |
15 from music21 import chord | |
16 from music21 import meter | |
17 from music21 import key | |
18 from music21 import stream | |
19 from music21 import tie | |
20 from music21 import bar | |
21 from music21 import harmony | |
22 from music21 import clef | |
23 class SymbolConversion: | |
24 | |
25 | |
26 def __getSymbolMesure(self,symbol): | |
27 ''' | |
28 Detects if it is a barline symbol and gets the first element | |
29 ''' | |
30 if isinstance(symbol,list): | |
31 if symbol[0].find('!')!=-1: | |
32 return symbol | |
33 | |
34 def convertM21(self,symbolArray): | |
35 ''' | |
36 Converts one Array of symbols to music21 | |
37 | |
38 usage: | |
39 | |
40 outVote=vote.vote(omr_symbolsAlign) | |
41 #apply voices if it is needed | |
42 | |
43 outVote=sc.setVoices(outVote) | |
44 #convert to music21 | |
45 resultS2=sc.convertM21(outVote) | |
46 | |
47 ''' | |
48 | |
49 sOut=stream.Score() | |
50 sPart=stream.Part() | |
51 measureIndex=1 | |
52 measure=stream.Measure() | |
53 measure.number=measureIndex | |
54 voicesLength=8 | |
55 for v in range(voicesLength): | |
56 voice=stream.Voice() | |
57 voice.id=v | |
58 measure.append(voice) | |
59 indexS=0 | |
60 | |
61 for symbol in symbolArray: | |
62 mytie="" | |
63 realDuration=None | |
64 s=symbol | |
65 | |
66 if isinstance(s,list): | |
67 s=s[0] | |
68 if s.find('TS:')!=-1: | |
69 ts=meter.TimeSignature(s[3:]) | |
70 | |
71 measure.append(ts) | |
72 if s.find('KS:')!=-1: | |
73 k=key.KeySignature(int(s[3:])) | |
74 | |
75 measure.append(k) | |
76 if s.find('CL:')!=-1: | |
77 c=clef.clefFromString(str(s[3:])) | |
78 | |
79 measure.append(c) | |
80 if s.find('N:')!=-1: | |
81 try: | |
82 if isinstance(symbol,list): | |
83 realDuration=symbol[1] | |
84 mytie=symbol[2] | |
85 | |
86 sep=s.index("_") | |
87 duration=s[sep+1:] | |
88 #************************************* | |
89 #duration vs realDuration for triplets | |
90 if realDuration!=None: | |
91 duration=realDuration | |
92 #************************************* | |
93 if(float(duration)>0): | |
94 n=note.Note(s[2:sep],quarterLength=float(duration)) | |
95 if symbol[5]!=None: | |
96 n.color=symbol[5] | |
97 if mytie!="": | |
98 n.tie=tie.Tie(mytie) | |
99 if len(symbol)>6:#voices | |
100 measure.voices[symbol[6]].append(n) | |
101 else: | |
102 measure.append(n) | |
103 except: | |
104 print "error"+s | |
105 | |
106 if s.find('R:')!=-1: | |
107 try: | |
108 if isinstance(symbol,list): | |
109 realDuration=symbol[1] | |
110 mytie=symbol[2] | |
111 duration=s[2:] | |
112 #************************************* | |
113 #duration vs realDuration for triplets | |
114 if realDuration!=None: | |
115 duration=realDuration | |
116 #************************************* | |
117 n=note.Rest(quarterLength=float(duration)) | |
118 if symbol[5]!=None: | |
119 n.color=symbol[5] | |
120 if len(symbol)>6:#voices | |
121 measure.voices[symbol[6]].append(n) | |
122 else: | |
123 measure.append(n) | |
124 except: | |
125 print "error"+s | |
126 | |
127 if s.find('C:')!=-1: | |
128 notes=s.split("[:") | |
129 cPitch=[] | |
130 for n in notes: | |
131 if n!='C:': | |
132 sep=n.index("_") | |
133 duration=n[sep+1:] | |
134 pitch= n[0:sep] | |
135 cPitch.append(pitch) | |
136 c=chord.Chord(cPitch) | |
137 c.duration.quarterLength=float(duration) | |
138 if symbol[5]!=None: | |
139 c.color=symbol[5] | |
140 if mytie!="": | |
141 c.tie=tie.Tie(mytie) | |
142 | |
143 if len(symbol)>6:#voices | |
144 measure.voices[symbol[6]].append(c) | |
145 else: | |
146 measure.append(c) | |
147 | |
148 if s.find('!')!=-1: | |
149 if isinstance(symbol,list): | |
150 barType= symbol[1] | |
151 barRepeat= symbol[2] | |
152 if barType!="": | |
153 try: | |
154 mybartype=bar.styleToMusicXMLBarStyle(barType) | |
155 myBar=bar.Barline(style=mybartype) | |
156 measure.rightBarline=myBar | |
157 except: | |
158 print "error barline" | |
159 | |
160 if barRepeat!="": | |
161 try: | |
162 myBar=bar.Repeat(direction=barRepeat) | |
163 if barRepeat=="start": | |
164 measure.leftBarline=myBar | |
165 if barRepeat=="end": | |
166 measure.rightBarline=myBar | |
167 except: | |
168 print "error barline" | |
169 sPart.append(measure) | |
170 measureIndex+=1 | |
171 measure=stream.Measure() | |
172 measure.number=measureIndex | |
173 for v in range(voicesLength): | |
174 voice=stream.Voice() | |
175 voice.id=v | |
176 measure.append(voice) | |
177 | |
178 indexS+=1 | |
179 | |
180 sOut.append(sPart) | |
181 return sOut | |
182 | |
183 | |
184 | |
185 | |
186 def __orderChord(self,mychord): | |
187 ''' | |
188 Private function that returns a chord ordered | |
189 in a string from lower to higher | |
190 ''' | |
191 midi=[] | |
192 midi2=[] | |
193 orderC=[] | |
194 for n in mychord: | |
195 if isinstance(n,note.Note): | |
196 midi.append(n.midi) | |
197 midi2.append(n.midi) | |
198 | |
199 while len(midi)>0: | |
200 indexMin=midi2.index(min(midi)) | |
201 indexPop=midi.index(min(midi)) | |
202 orderC.append(mychord[indexMin]) | |
203 midi.pop(indexPop) | |
204 | |
205 myOrderChord=chord.Chord(orderC) | |
206 myOrderChord.duration.quarterLength=mychord.duration.quarterLength | |
207 return myOrderChord | |
208 | |
209 | |
210 def __getKeyOffset(self,item): | |
211 ''' | |
212 Private function that returns the offset of one item | |
213 ''' | |
214 return item['offset'] | |
215 | |
216 def __removeDuplicates(self,input_raw): | |
217 ''' | |
218 Private function to remove duplicates in the string input | |
219 ''' | |
220 seen = set() | |
221 new_l = [] | |
222 for d in input_raw: | |
223 t = tuple(d.items()) | |
224 if t not in seen: | |
225 seen.add(t) | |
226 new_l.append(d) | |
227 | |
228 return new_l | |
229 | |
230 | |
231 def getElementsFromMeasureOrderByOffset(self,measure): | |
232 ''' | |
233 Returns the symbols of a measure ordered by offset | |
234 ''' | |
235 offset=measure.flat.offsetMap | |
236 offset_n=self.__removeDuplicates(offset) | |
237 sorted_offset=sorted(offset_n,key=self.__getKeyOffset) | |
238 return sorted_offset | |
239 | |
240 def __swapSymbols(self,s1,s2): | |
241 ''' | |
242 Swap the s1,s2 symbols | |
243 ''' | |
244 inter=s1 | |
245 s1=s2 | |
246 s2=inter | |
247 return s1,s2 | |
248 | |
249 def orderSymbolsByPitch(self,symbols): | |
250 ''' | |
251 Orders the symbol string from lower to high if they have | |
252 the same offset | |
253 ''' | |
254 for i in range(len(symbols)-1): | |
255 for j in range(i,i+2): | |
256 pitch_i=0 | |
257 pitch_j=0 | |
258 if(symbols[i]['offset']==symbols[j]['offset']): | |
259 if isinstance(symbols[i]['element'],note.Note): | |
260 pitch_i=symbols[i]['element'].pitch.midi | |
261 if isinstance(symbols[j]['element'],note.Note): | |
262 pitch_j=symbols[j]['element'].pitch.midi | |
263 | |
264 if pitch_j>pitch_i:#higher pitch first | |
265 symbols[i],symbols[j]=self.__swapSymbols(symbols[i],symbols[j]) | |
266 | |
267 if pitch_j==pitch_i and pitch_j>0:#longest first if equal pitch | |
268 if symbols[i]['element'].duration<symbols[j]['element'].duration: | |
269 symbols[i],symbols[j]=self.__swapSymbols(symbols[i],symbols[j]) | |
270 | |
271 if isinstance(symbols[j]['element'],note.Rest):#rest first | |
272 symbols[i],symbols[j]=self.__swapSymbols(symbols[i],symbols[j]) | |
273 | |
274 return symbols | |
275 | |
276 def getRepetitionMarks(self,measure): | |
277 ''' | |
278 Returns the repetition marks symbol from a measure | |
279 ''' | |
280 symbols=[] | |
281 for barline in measure.getElementsByClass(bar.Barline): | |
282 symbol={'voiceIndex': None, 'element': barline, 'endTime': 0.0, 'offset': barline.offset} | |
283 symbols.append(symbol) | |
284 return symbols | |
285 | |
286 def getTS(self,measure): | |
287 ''' | |
288 Returns the time signature symbol from a measure | |
289 ''' | |
290 symbols=[] | |
291 for ts in measure.getElementsByClass(meter.TimeSignature): | |
292 symbol={'voiceIndex': None, 'element': ts, 'endTime': 0.0, 'offset': ts.offset} | |
293 symbols.append(symbol) | |
294 return symbols | |
295 | |
296 def removeTS(self,symbols): | |
297 ''' | |
298 Removes the time signature from the symbols array | |
299 ''' | |
300 for symbol in symbols: | |
301 s=symbol['element'] | |
302 if 'TimeSignature' in s.classes: | |
303 symbols.pop(symbols.index(symbol)) | |
304 break | |
305 return symbols | |
306 | |
307 def filterOMR(self,omr,idPart): | |
308 ''' | |
309 Removes elements from the music21 input as text, slurs and expressions (p, mp, f...) | |
310 | |
311 Returns the omr filtered and the symbols array converted | |
312 | |
313 ''' | |
314 omr_filtered=stream.Stream() | |
315 omr_symbols=[] | |
316 #for single parts | |
317 print "Partes=",len(omr.parts) | |
318 if len(omr.parts)==1: | |
319 idPart=0 | |
320 if len(omr.parts)==0: | |
321 idPart=0 | |
322 mypart=omr | |
323 if len(omr.parts)>=1: | |
324 if(int(idPart)>=len(omr.parts)): | |
325 print "error in OMR" | |
326 return [],[] | |
327 mypart=omr.parts[int(idPart)] | |
328 | |
329 | |
330 | |
331 isFirstClef=True | |
332 indexMes=0 | |
333 print idPart | |
334 for measure in mypart.getElementsByClass(stream.Measure): | |
335 indexMes+=1 | |
336 symbols=self.getElementsFromMeasureOrderByOffset(measure.semiFlat)#key signature time signature and clef are missing if not flat | |
337 symbols=self.orderSymbolsByPitch(symbols) | |
338 symbolsRepetition=self.getRepetitionMarks(measure) | |
339 symbolsTS=self.getTS(measure) | |
340 symbols=self.removeTS(symbols) | |
341 symbols=symbolsTS+symbols+symbolsRepetition | |
342 | |
343 newMeasure=stream.Measure() | |
344 styleBarline="" | |
345 directionRepeat="" | |
346 strClef="" | |
347 | |
348 for symbol in symbols: | |
349 #symbol={'voiceIndex': 0, 'element': <music21.note.Note C>, 'endTime': 1.0, 'offset': 0.0} | |
350 s=symbol['element'] | |
351 if 'TimeSignature' in s.classes: | |
352 newMeasure.append(s) | |
353 str0="TS:"+str(s.numerator)+"/"+str(s.denominator) | |
354 omr_symbols.append([str0,None,None,symbol['offset'],symbol['endTime'],None]) | |
355 | |
356 elif 'KeySignature' in s.classes: | |
357 newMeasure.append(s) | |
358 str0="KS:"+str(s.sharps) | |
359 omr_symbols.append([str0,None,None,symbol['offset'],symbol['endTime'],None]) | |
360 | |
361 elif 'Clef' in s.classes: | |
362 newMeasure.append(s) | |
363 strClef="CL:"+str(s.sign) | |
364 if isFirstClef: | |
365 omr_symbols.append([strClef,None,None,symbol['offset'],symbol['endTime'],None]) | |
366 strClef="" | |
367 isFirstClef=False | |
368 | |
369 elif 'Note' in s.classes: | |
370 newMeasure.append(s) | |
371 mytype=s.duration.type | |
372 duration=note.duration.convertTypeToQuarterLength(mytype) | |
373 realDuration=s.duration.quarterLength | |
374 if realDuration==duration+duration/2: #dot case | |
375 duration=realDuration | |
376 n="N:"+s.pitch.nameWithOctave+"_"+str(duration) | |
377 # Ties case | |
378 mytie="" | |
379 if s.tie!=None: | |
380 mytie=s.tie.type | |
381 | |
382 if float(realDuration)>0: | |
383 omr_symbols.append([n,realDuration,mytie,symbol['offset'],symbol['endTime'],s.color] ) | |
384 | |
385 elif 'Rest' in s.classes: | |
386 newMeasure.append(s) | |
387 mytype=s.duration.type | |
388 if mytype!="complex": | |
389 duration=note.duration.convertTypeToQuarterLength(mytype) | |
390 else: | |
391 duration=s.duration.quarterLength | |
392 realDuration=s.duration.quarterLength | |
393 if realDuration==duration+duration/2: #dot case | |
394 duration=realDuration | |
395 n="R:"+str(duration) | |
396 # realDuration=s.duration.quarterLength | |
397 omr_symbols.append([n,realDuration,False,symbol['offset'],symbol['endTime'],s.color]) | |
398 | |
399 elif 'Chord' in s.classes: | |
400 if type(s) is not harmony.ChordSymbol: | |
401 newMeasure.append(s) | |
402 mytype=s.duration.type | |
403 duration=note.duration.convertTypeToQuarterLength(mytype) | |
404 realDuration=s.duration.quarterLength | |
405 if realDuration==duration+duration/2: #dot case | |
406 duration=realDuration | |
407 chord="C:" | |
408 sOrder=self.__orderChord(s) | |
409 for n in sOrder: | |
410 chord+="[:"+n.pitch.nameWithOctave+"_"+str(duration) | |
411 | |
412 | |
413 | |
414 # Ties case | |
415 mytie="" | |
416 if s.tie!=None: | |
417 mytie=s.tie.type | |
418 omr_symbols.append([chord,realDuration,mytie,symbol['offset'],symbol['endTime'],s.color]) | |
419 | |
420 elif 'Barline' in s.classes: | |
421 styleBarline=s.style | |
422 try: | |
423 directionRepeat=s.direction | |
424 except: | |
425 directionRepeat="" | |
426 | |
427 | |
428 omr_symbols.append(['!',styleBarline,directionRepeat,symbol['offset'],symbol['endTime'],None]) | |
429 if strClef!="": | |
430 omr_symbols.append([strClef,None,None,0,0,None]) | |
431 omr_filtered.append(newMeasure) | |
432 return omr_filtered,omr_symbols | |
433 | |
434 def removeExtraTies(self,arraySymbols): | |
435 ''' | |
436 Removes the non logical ties from the symbol array | |
437 | |
438 ''' | |
439 for s in arraySymbols: | |
440 if isinstance(s,list): | |
441 mainSymbol=s[0] | |
442 if mainSymbol.find('N:')!=-1: | |
443 tie1=s[2] | |
444 if arraySymbols.index(s)<len(arraySymbols): | |
445 sNext=arraySymbols[arraySymbols.index(s)+1] | |
446 if isinstance(sNext,list): | |
447 mainSymbol2=sNext[0] | |
448 if mainSymbol2.find('N:')!=-1: | |
449 tie2=sNext[2] | |
450 if tie1=='start' and tie2!='end': | |
451 s[2]='' | |
452 | |
453 return arraySymbols | |
454 | |
455 | |
456 def setVoices(self,strSymbols): | |
457 ''' | |
458 Divides each measure in voices, if it is necessary. | |
459 | |
460 The output is the symbol string using voices | |
461 | |
462 ''' | |
463 quarters=self.__getTimeSignatureQuarters(strSymbols) | |
464 measures=self.__chopMeasures(strSymbols) | |
465 newString=[] | |
466 for measure in measures: | |
467 newMeasure=self.divideMeasureinVoices(measure,quarters) | |
468 for s in newMeasure: | |
469 newString.append(s) | |
470 return newString | |
471 | |
472 def __getTimeSignatureQuarters(self,strSymbols): | |
473 ''' | |
474 returns the number of quarters of the first time signature found | |
475 ''' | |
476 quarters=4 #4/4 by default | |
477 for s in strSymbols: | |
478 if isinstance(s,list): | |
479 s=s[0] | |
480 if s.find('TS:')!=-1: | |
481 ts=s[3:] | |
482 timeSig=ts.split("/") | |
483 num=int(timeSig[0]) | |
484 den=int(timeSig[1]) | |
485 quarters=(4/den)*num | |
486 return quarters | |
487 | |
488 def __chopMeasures(self,strSymbols): | |
489 ''' | |
490 Divides the symbol string in measures | |
491 ''' | |
492 strMeasures=[] | |
493 strMeasure=[] | |
494 for s in strSymbols: | |
495 strMeasure.append(s) | |
496 bar=self.__getSymbolMesure(s) | |
497 if bar!=None: | |
498 strMeasures.append(strMeasure) | |
499 strMeasure=[] | |
500 return strMeasures | |
501 | |
502 | |
503 def isNoteRest(self,symbol): | |
504 ''' | |
505 Returns true if the symbol is note, rest or chord | |
506 ''' | |
507 s=symbol | |
508 if isinstance(s,list): | |
509 if s[0].find('N:')!=-1: | |
510 return True | |
511 if s[0].find('R:')!=-1: | |
512 return True | |
513 if s[0].find('C:')!=-1: | |
514 return True | |
515 return False | |
516 return False | |
517 | |
518 def divideMeasureinVoices(self,strMeasure,quarters): | |
519 ''' | |
520 If the length of each measure in the string is higher than quarters, | |
521 adjusting to voices | |
522 | |
523 ''' | |
524 newMeasure=[] | |
525 duration=0 | |
526 #if duration is higher than time signatura | |
527 for s in strMeasure: | |
528 if self.isNoteRest(s): | |
529 if s[1]!=None: | |
530 try: | |
531 duration+=s[1] | |
532 except: | |
533 pass | |
534 # voices only if needed | |
535 if duration<=quarters: | |
536 return strMeasure | |
537 | |
538 | |
539 for s in strMeasure: | |
540 if not self.isNoteRest(s): | |
541 if self.__getSymbolMesure(s)==None: | |
542 newMeasure.append(s) | |
543 voiceslength=8 | |
544 for v in range(voiceslength-1): | |
545 offset=0.0 | |
546 firstNote=False | |
547 for s in strMeasure: | |
548 if self.isNoteRest(s): | |
549 if len(s)<7:#not voice yet | |
550 offsetNote=s[3] | |
551 if firstNote==False: | |
552 offset=offsetNote | |
553 firstNote=True | |
554 if offsetNote>0: | |
555 rest=[] | |
556 rest.append("R:"+str(offsetNote)) | |
557 rest.append(offsetNote) | |
558 rest.append(False) | |
559 rest.append(0) | |
560 rest.append(offsetNote) | |
561 rest.append(None) | |
562 rest.append(v) | |
563 newMeasure.append(rest) | |
564 if(offset==offsetNote): | |
565 s.append(v) | |
566 newMeasure.append(s) | |
567 offset=s[4] | |
568 | |
569 | |
570 for s in strMeasure: | |
571 if self.__getSymbolMesure(s)!=None: | |
572 newMeasure.append(s) | |
573 | |
574 | |
575 return newMeasure |