annotate Functions/MeasureFunctions.py @ 2:46fb79167a61 tip

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