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