comparison Functions/MeasureFunctions.py @ 1:0f7f611deca4

Functions
author Victor Padilla <victor.padilla.mc@gmail.com>
date Mon, 04 May 2015 22:53:31 +0200
parents
children
comparison
equal deleted inserted replaced
0:1eb6054a1f84 1:0f7f611deca4
1 '''
2 @organization: Lancaster University & University of Leeds
3 @version: 1.0
4 Created on 11/12/2014
5
6 @author: Victor Padilla
7 @contact: v.padilla@lancaster.ac.uk
8
9 Functions to manipulate measures
10
11
12 '''
13
14 from music21 import stream
15 from music21 import note
16 from music21 import meter
17 from music21 import omr
18 from music21 import spanner
19 from music21 import chord
20 import copy
21
22
23 class MeasureFunctions:
24
25 def getDuration(self,bar):
26 '''
27 Returns the duration of a single measure
28 '''
29 duration=0
30 try:
31 for event in bar:
32 try:
33 if(event.isNote):
34 duration+=event.duration.quarterLength
35 if(event.isRest):
36 duration+=event.duration.quarterLength
37 except:
38 pass
39 except:
40 pass
41 return str(duration)
42
43 def correctIncorrectMeasuresArray(self,omr,incorrectMeasures):
44 '''
45 Iteractive method that detects if some measures have been wrong flagged as an error.
46 It is under development
47 '''
48 measures=omr.parts[0].getElementsByClass(stream.Measure)
49 if 0 in incorrectMeasures:
50 incorrectMeasures.remove(0)#Anacrusis
51
52 for barNumber in incorrectMeasures:
53 if barNumber<len(measures)-1:
54 measure=measures[barNumber]
55 measureNext=measures[barNumber+1]
56 duration=self.getDuration(measure)
57
58 if(float(duration)<=0):
59 incorrectMeasures.remove(barNumber)
60 self.correctIncorrectMeasuresArray(omr,incorrectMeasures)
61
62 if(measureNext!=None):
63 durationNext=self.getDuration(measureNext)
64 if(float(duration)+float(durationNext)==4):
65 try:
66 incorrectMeasures.remove(barNumber)
67 incorrectMeasures.remove(barNumber+1)
68 except:
69 pass
70 self.correctIncorrectMeasuresArray(omr,incorrectMeasures)
71 return incorrectMeasures
72
73
74 def filterExtraMeasures(self,omr):
75 '''
76 Function that removes empty measures
77 '''
78 sco=stream.Score()
79 s=stream.Part()
80 for measure in omr.parts[0].getElementsByClass(stream.Measure):
81 if measure.flat.duration.quarterLength>0:
82 s.append(measure)
83 sco.append(s)
84 return sco
85
86
87 def getIncorrectMeasureIndices(self,omr):
88 '''
89 Method that detect wrong measures using transitions
90 '''
91 arrChunks= self.getTransitions(omr)
92 measures=omr.parts[0].getElementsByClass(stream.Measure)
93 indexBar=0
94 FlagErrors=[]
95 barFrom=0
96 barTo=0
97 for chunk in arrChunks:
98 indexC=arrChunks.index(chunk)
99 chunkBefore=arrChunks[indexC-1]
100 if(indexC==0):
101 barFrom=0
102 else:
103 barFrom+=chunkBefore[1]
104 barTo=chunk[1]+barFrom
105 chunkMeasures=measures[barFrom:barTo]
106 quarterChunk=round(chunk[0]/2)
107 for measure in chunkMeasures:
108 if measure.duration.quarterLength!=quarterChunk:
109 FlagErrors.append(indexBar)
110 indexBar+=1
111 return FlagErrors
112
113 def _filterTransitions(self,arrMeasureIndex):
114 '''
115 Inner method that removes false transitions in measures for detecting missing time signatures
116 '''
117 arrMeasureIndex2=[]
118 arrMeasureIndex.insert(0,0)
119 arrMeasureIndex.append(-1)
120 for mes in arrMeasureIndex:
121 indexM=arrMeasureIndex.index(mes)
122 if indexM>0:
123 bars=arrMeasureIndex[indexM]-arrMeasureIndex[indexM-1]
124 if (bars>9):
125 arrMeasureIndex2.append(mes)
126 arrMeasureIndex2.insert(0,0)
127 arrMeasureIndex2.append(-1)
128 return arrMeasureIndex2
129
130 def getTransitions(self,omr):
131 '''
132 Returns transitions in a omr
133 '''
134 MeasuresLength=[]
135 arrMeasureIndex_before=self._getTransitionBar(omr)
136
137 arrMeasureIndex=self._filterTransitions(arrMeasureIndex_before)
138 for i in range(len(arrMeasureIndex)-1):
139 arrMeasureslength= self._getAverageQuavers(omr,arrMeasureIndex[i],arrMeasureIndex[i+1],False)
140 MeasuresLength.append(arrMeasureslength)
141
142 return MeasuresLength
143
144 def _getTransitionBar(self,omr):
145 '''
146 Inner function for getting transitions
147 '''
148 arrOut=[]
149 measures=omr.parts[0].getElementsByClass(stream.Measure)
150 barsNumber=len(measures)
151 for barIndex in range(5,barsNumber):
152 averageBefore=self._getAverageQuavers(omr,barIndex-4,barIndex,isUntilTS=False)
153 averageAfter=self._getAverageQuavers(omr,barIndex,barIndex+2,isUntilTS=False)
154 if(abs(averageBefore[0]-averageAfter[0])>1.5):
155 if(measures[barIndex].duration.quarterLength<averageBefore[0]*2):#rest bars or missing bars
156 arrOut.append(barIndex)
157 return arrOut
158
159 def _getAverageQuavers(self,myStream,measureIndex,measureIndexEnd,isUntilTS):
160 '''
161 Inner function for getting the average quavers per measure in a group of measures
162 '''
163 quavers=0
164 barNumbers=0
165 averageQuavers=0
166 measures=myStream.parts[0].getElementsByClass(stream.Measure)
167 for bar in measures[measureIndex:measureIndexEnd]:
168 duration=bar.duration.quarterLength*2
169 barNumbers+=1
170 if(duration>0 and duration<10):
171 quavers+=duration
172 if isUntilTS:
173 if (len(bar.getElementsByClass(meter.TimeSignature))>0):
174 break
175 if barNumbers>0:
176 averageQuavers=quavers/barNumbers
177 return averageQuavers,barNumbers
178
179 #**************************************************
180 def getPossibleBeamsErrors(self,omr):
181 '''
182 Function that returns measure errors based on non conventional beaming
183 '''
184 arrErrors=[]
185 measures=omr.parts[0].getElementsByClass(stream.Measure)
186 barsNumber=len(measures)
187 for i in range(barsNumber):
188 notes=measures[i].getElementsByClass(note.Note)
189 count=0
190 state=0
191 for n in notes:
192 if n.duration.quarterLength==0.25:
193 bs=n.beams.getNumbers()
194 if(len(bs)>0):
195 b=n.beams.getByNumber(bs[0])
196 if b.type=='start':
197 count=1
198 state=1
199 if b.type=='continue':
200 if(state==1 or state==2):
201 count+=1
202 state=2
203 if b.type=='stop':
204 if(state==1 or state==2):
205 count+=1
206 if count==3:
207 arrErrors.append(i)
208 arrErrors=list(set(arrErrors))
209 return arrErrors
210
211 def getPossibleLastNoteErrors(self,omr):
212 '''
213 Function that returns measure errors based on the last notes rhythm
214 '''
215 arrErrors=[]
216 measures=omr.parts[0].getElementsByClass(stream.Measure)
217 barsNumber=len(measures)
218 for i in range(barsNumber):
219 notes=measures[i].getElementsByClass(note.GeneralNote)
220 if(len(notes)>0):
221 lastNote=notes[len(notes)-1]
222 if lastNote.duration.quarterLength<=0.25:
223 if(lastNote.isRest):
224 arrErrors.append(i)
225 else:
226 bs=lastNote.beams.getNumbers()
227
228 if(len(bs)==0):
229 if(len(notes)>2):
230 noteBefore=notes[len(notes)-2]
231 if noteBefore.duration.quarterLength+lastNote.duration.quarterLength!=1:
232 arrErrors.append(i)
233
234
235 return arrErrors
236
237 def flagIncorrectMeasures(self,omr2):
238 '''
239 flag the incorrect measures from a omr using different methods
240 '''
241 mf=MeasureFunctions()
242 sc=omr.correctors.ScoreCorrector(omr2)
243 part=sc.getSinglePart(0)
244
245 arrErrors=[]
246 im=part.getIncorrectMeasureIndices(runFast=False)
247
248 im1= mf.getIncorrectMeasureIndices(omr2)
249
250 im2= mf.getPossibleBeamsErrors(omr2)
251 im3= mf.getPossibleLastNoteErrors(omr2)
252
253
254 arrErrors.append(im)
255 arrErrors.append(im1)
256 arrErrors.append(im2)
257 arrErrors.append(im3)
258
259 if(len(im)>15):
260 if(len(im1)<len(im)):
261 im=im1
262 imSum=list(set(im)|set(im2)|set(im3))
263 imSum=sorted(imSum)
264 imOK=mf.correctIncorrectMeasuresArray(omr2,imSum)
265 print im
266 return imOK,arrErrors
267
268 def getSlurs(self,part):
269 '''
270 Returns the slurs from one part
271 '''
272 slurs=part.flat.getElementsByClass(spanner.Spanner)
273 return slurs
274
275 def reconstructScore(self,part,hashPart):
276 '''
277 This function include rest measures in one part based on the hash part aligned
278 '''
279 partReconstructed=stream.Part()
280 barNumber=1
281 gaps=[]
282 for i in range(len(hashPart)):
283 if hashPart[i]!="*":
284 m=part.getElementsByClass(stream.Measure)[barNumber-1]
285 partReconstructed.append(m)
286 barNumber+=1
287 else:
288 m=stream.Measure()
289 partReconstructed.append(m)
290 gaps.append(i)
291 slurs=self.getSlurs(part)
292 partReconstructed.append(slurs)
293 myStream=self.reorderMeasures(partReconstructed)
294 return myStream,gaps
295
296 def reorderMeasures(self,omr):
297 '''
298 Returns the correct measures number
299 '''
300 slurs=self.getSlurs(omr)
301 s=stream.Part()
302 barNumber=1
303 for measure in omr.getElementsByClass(stream.Measure):
304 measure.number=barNumber
305 s.append(measure)
306 barNumber+=1
307 s.append(slurs)
308 return s
309
310 def getNoteByOffset(self,voice,offset):
311 '''
312 Returns one note in one voice according to the offset
313 '''
314 for n in voice.getElementsByClass(note.Note):
315 if n.offset==offset:
316 return n
317 for n in voice.getElementsByClass(chord.Chord):
318 if n.offset==offset:
319 return n
320
321 def convertVoicesToChord(self,omr):
322 '''
323 Function that converts voices with the same rhythm to chords
324 '''
325 for part in omr.getElementsByClass(stream.Part):
326 for measure in part.getElementsByClass(stream.Measure):
327 measure.show('text')
328 voices=measure.getElementsByClass(stream.Voice)
329 if len(voices)>=2:
330 for element1 in voices[0].getElementsByClass(note.GeneralNote):
331 offset1= element1.offset
332 for voice in voices:
333 if voice!=voices[0]:
334 element2=self.getNoteByOffset( voice, offset1)
335 if element2 is not None and element1.duration.quarterLength==element2.duration.quarterLength and not isinstance(element1,note.Rest):
336 mychord=self.mergeChords(element1,element2)
337 mychord.duration.quarterLength=element1.quarterLength
338 voices[0].replace(element1,mychord)
339 myrest=note.Rest()
340 myrest.duration.quarterLength=element1.quarterLength
341 voice.replace(element2,myrest)
342
343 omr=self.removeRestVoice(omr)
344 return omr
345 def removeRestVoice(self,omr):
346 '''
347 This option removes the rest voices (without notes)
348 '''
349 for part in omr.getElementsByClass(stream.Part):
350 for measure in part.getElementsByClass(stream.Measure):
351 voices=measure.getElementsByClass(stream.Voice)
352 for voice in voices:
353 myvoice = copy.deepcopy(voice)
354 myvoice.removeByClass('Rest')
355 if len(myvoice)==0:
356 measure.remove(voice)
357
358 return omr
359
360 def mergeChords(self,element1,element2):
361 '''
362 Returns one chord made by two elements, notes or chords
363 '''
364 notes1=[]
365 notes2=[]
366 notes=[]
367 if isinstance(element1,chord.Chord):
368 notes1=element1.pitches
369 else:
370 notes1.append(element1.pitch)
371
372 if isinstance(element2,chord.Chord):
373 notes2=element2.pitches
374 else:
375 notes2.append(element2.pitch)
376
377 for n in notes1:
378 notes.append(n)
379 for n in notes2:
380 notes.append(n)
381
382 mychord = chord.Chord(notes)
383 return mychord
384
385
386 def convertBeamsToTriplets(self,omr):
387 '''
388 This option search for measures where the rhythm is bigger than the time signature
389 and tries to convert 3 note beams in triplets
390 '''
391 tsDefault=omr.flat.getElementsByClass('TimeSignature')[0]
392 idPart=0
393 for part in omr.getElementsByClass(stream.Part):
394 idPart+=1
395 idMeasure=0
396 for measure in part.getElementsByClass(stream.Measure):
397 idMeasure+=1
398 if self._IsMeasureHigherTS(idMeasure, part,tsDefault):
399 print idMeasure
400 measure=self.beamsToTriplets(measure)
401 return omr
402
403
404 def _IsMeasureHigherTS(self,idMeasure,part,ts):
405 '''
406 Inner function that detects if one measure is bigger than the time signature
407 '''
408 idCountMeasure=0
409 for measure in part.getElementsByClass(stream.Measure):
410 idCountMeasure+=1
411 newts=measure.flat.getElementsByClass('TimeSignature')
412 if len(newts)>0:
413 ts=newts[0]
414 if idMeasure==idCountMeasure:
415 voices=measure.getElementsByClass(stream.Voice)
416 totalDuration=0
417 if len(voices)>=1:
418 for voice in voices:
419 totalDuration=0
420 for element in voice.getElementsByClass(note.GeneralNote):
421 totalDuration+=element.duration.quarterLength;
422 else:
423 for element in measure.getElementsByClass(note.GeneralNote):
424 totalDuration+=element.duration.quarterLength;
425 quartersBymeasure=ts.numerator*(4.0/ts.denominator)
426 if totalDuration>quartersBymeasure:
427 return True
428 else:
429 return False
430 def beamsToTriplets(self,measure):
431 '''
432 This function returns one measure changing 3 notes beamed by triplets
433 '''
434 voices=measure.getElementsByClass(stream.Voice)
435 if len(voices)>=1:
436 for voice in voices:
437 notes=voice.getElementsByClass(note.NotRest)
438 notes=self._notesToTriplets(notes)
439
440 else:
441 notes=measure.getElementsByClass(note.NotRest)
442 notes=self._notesToTriplets(notes)
443
444 return measure
445
446
447 def _notesToTriplets(self,notes):
448 '''
449 Gets one note arrays and tries to convert to triplets
450 '''
451
452 for i in range(len(notes)):
453 self._removeSlurs(notes[i])
454 beams1=notes[i].beams.beamsList
455
456 try:
457 beams2=notes[i+1].beams.beamsList
458 beams3=notes[i+2].beams.beamsList
459 if len(beams1)>0 and len(beams2)>0 and len(beams3)>0:
460 if beams1[0].type=='start' and beams2[0].type=='continue' and beams3[0].type=='stop':
461 if notes[i].duration.quarterLength==notes[i+1].duration.quarterLength==notes[i+2].duration.quarterLength:
462 mytype=notes[i].duration.type
463 duration=note.duration.convertTypeToQuarterLength(mytype)
464 realDuration=duration*2.0/3.0
465 notes[i].duration.quarterLength=realDuration
466 notes[i+1].duration.quarterLength=realDuration
467 notes[i+2].duration.quarterLength=realDuration
468
469 except:
470 pass
471
472 return notes
473
474 def _removeSlurs(self,n):
475 '''
476 Removes the slur in one note
477 '''
478 for element in n.getSites():
479 if isinstance(element,stream.SpannerStorage):
480 for e in element.elements:
481 element.pop(0)
482
483 def removesEmptyVoices(self,omr):
484 '''
485 Removes empty voices (just rests)
486 '''
487 for part in omr.getElementsByClass(stream.Part):
488 for measure in part.getElementsByClass(stream.Measure):
489 print measure
490 voices=measure.getElementsByClass(stream.Voice)
491 if len(voices)>=1:
492 print len(voices)
493 for voice in voices:
494 notes=voice.getElementsByClass(note.NotRest)
495 if len(notes)==0:
496 index=measure.index(voice)
497 measure.pop(index)
498 voices=measure.getElementsByClass(stream.Voice)
499 if len(voices)==1:
500 measure.flattenUnnecessaryVoices()
501 return omr
502
503 def removesGaps(self,omr):
504 '''
505 This option changes gaps by rests in voices
506 '''
507 for part in omr.getElementsByClass(stream.Part):
508 for measure in part.getElementsByClass(stream.Measure):
509 voices=measure.getElementsByClass(stream.Voice)
510 if len(voices)>=1:
511 for voice in voices:
512 v=voice.findGaps()
513 if v!=None:
514 nextOffset=1000
515 for element in voice.getElementsByClass(note.GeneralNote):
516 offset= element.offset
517 if offset>nextOffset:
518 rest=note.Rest()
519 rest.duration.quarterLength=offset-nextOffset
520 voice.insert(nextOffset,rest)
521 nextOffset=1000
522 else:
523 duration=element.duration.quarterLength
524 nextOffset=offset+duration
525 return omr
526
527