Mercurial > hg > multiomr
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Functions/MeasureFunctions.py Mon May 04 22:53:31 2015 +0200 @@ -0,0 +1,527 @@ +''' +@organization: Lancaster University & University of Leeds +@version: 1.0 +Created on 11/12/2014 + +@author: Victor Padilla +@contact: v.padilla@lancaster.ac.uk + +Functions to manipulate measures + + +''' + +from music21 import stream +from music21 import note +from music21 import meter +from music21 import omr +from music21 import spanner +from music21 import chord +import copy + + +class MeasureFunctions: + + def getDuration(self,bar): + ''' + Returns the duration of a single measure + ''' + duration=0 + try: + for event in bar: + try: + if(event.isNote): + duration+=event.duration.quarterLength + if(event.isRest): + duration+=event.duration.quarterLength + except: + pass + except: + pass + return str(duration) + + def correctIncorrectMeasuresArray(self,omr,incorrectMeasures): + ''' + Iteractive method that detects if some measures have been wrong flagged as an error. + It is under development + ''' + measures=omr.parts[0].getElementsByClass(stream.Measure) + if 0 in incorrectMeasures: + incorrectMeasures.remove(0)#Anacrusis + + for barNumber in incorrectMeasures: + if barNumber<len(measures)-1: + measure=measures[barNumber] + measureNext=measures[barNumber+1] + duration=self.getDuration(measure) + + if(float(duration)<=0): + incorrectMeasures.remove(barNumber) + self.correctIncorrectMeasuresArray(omr,incorrectMeasures) + + if(measureNext!=None): + durationNext=self.getDuration(measureNext) + if(float(duration)+float(durationNext)==4): + try: + incorrectMeasures.remove(barNumber) + incorrectMeasures.remove(barNumber+1) + except: + pass + self.correctIncorrectMeasuresArray(omr,incorrectMeasures) + return incorrectMeasures + + + def filterExtraMeasures(self,omr): + ''' + Function that removes empty measures + ''' + sco=stream.Score() + s=stream.Part() + for measure in omr.parts[0].getElementsByClass(stream.Measure): + if measure.flat.duration.quarterLength>0: + s.append(measure) + sco.append(s) + return sco + + + def getIncorrectMeasureIndices(self,omr): + ''' + Method that detect wrong measures using transitions + ''' + arrChunks= self.getTransitions(omr) + measures=omr.parts[0].getElementsByClass(stream.Measure) + indexBar=0 + FlagErrors=[] + barFrom=0 + barTo=0 + for chunk in arrChunks: + indexC=arrChunks.index(chunk) + chunkBefore=arrChunks[indexC-1] + if(indexC==0): + barFrom=0 + else: + barFrom+=chunkBefore[1] + barTo=chunk[1]+barFrom + chunkMeasures=measures[barFrom:barTo] + quarterChunk=round(chunk[0]/2) + for measure in chunkMeasures: + if measure.duration.quarterLength!=quarterChunk: + FlagErrors.append(indexBar) + indexBar+=1 + return FlagErrors + + def _filterTransitions(self,arrMeasureIndex): + ''' + Inner method that removes false transitions in measures for detecting missing time signatures + ''' + arrMeasureIndex2=[] + arrMeasureIndex.insert(0,0) + arrMeasureIndex.append(-1) + for mes in arrMeasureIndex: + indexM=arrMeasureIndex.index(mes) + if indexM>0: + bars=arrMeasureIndex[indexM]-arrMeasureIndex[indexM-1] + if (bars>9): + arrMeasureIndex2.append(mes) + arrMeasureIndex2.insert(0,0) + arrMeasureIndex2.append(-1) + return arrMeasureIndex2 + + def getTransitions(self,omr): + ''' + Returns transitions in a omr + ''' + MeasuresLength=[] + arrMeasureIndex_before=self._getTransitionBar(omr) + + arrMeasureIndex=self._filterTransitions(arrMeasureIndex_before) + for i in range(len(arrMeasureIndex)-1): + arrMeasureslength= self._getAverageQuavers(omr,arrMeasureIndex[i],arrMeasureIndex[i+1],False) + MeasuresLength.append(arrMeasureslength) + + return MeasuresLength + + def _getTransitionBar(self,omr): + ''' + Inner function for getting transitions + ''' + arrOut=[] + measures=omr.parts[0].getElementsByClass(stream.Measure) + barsNumber=len(measures) + for barIndex in range(5,barsNumber): + averageBefore=self._getAverageQuavers(omr,barIndex-4,barIndex,isUntilTS=False) + averageAfter=self._getAverageQuavers(omr,barIndex,barIndex+2,isUntilTS=False) + if(abs(averageBefore[0]-averageAfter[0])>1.5): + if(measures[barIndex].duration.quarterLength<averageBefore[0]*2):#rest bars or missing bars + arrOut.append(barIndex) + return arrOut + + def _getAverageQuavers(self,myStream,measureIndex,measureIndexEnd,isUntilTS): + ''' + Inner function for getting the average quavers per measure in a group of measures + ''' + quavers=0 + barNumbers=0 + averageQuavers=0 + measures=myStream.parts[0].getElementsByClass(stream.Measure) + for bar in measures[measureIndex:measureIndexEnd]: + duration=bar.duration.quarterLength*2 + barNumbers+=1 + if(duration>0 and duration<10): + quavers+=duration + if isUntilTS: + if (len(bar.getElementsByClass(meter.TimeSignature))>0): + break + if barNumbers>0: + averageQuavers=quavers/barNumbers + return averageQuavers,barNumbers + +#************************************************** + def getPossibleBeamsErrors(self,omr): + ''' + Function that returns measure errors based on non conventional beaming + ''' + arrErrors=[] + measures=omr.parts[0].getElementsByClass(stream.Measure) + barsNumber=len(measures) + for i in range(barsNumber): + notes=measures[i].getElementsByClass(note.Note) + count=0 + state=0 + for n in notes: + if n.duration.quarterLength==0.25: + bs=n.beams.getNumbers() + if(len(bs)>0): + b=n.beams.getByNumber(bs[0]) + if b.type=='start': + count=1 + state=1 + if b.type=='continue': + if(state==1 or state==2): + count+=1 + state=2 + if b.type=='stop': + if(state==1 or state==2): + count+=1 + if count==3: + arrErrors.append(i) + arrErrors=list(set(arrErrors)) + return arrErrors + + def getPossibleLastNoteErrors(self,omr): + ''' + Function that returns measure errors based on the last notes rhythm + ''' + arrErrors=[] + measures=omr.parts[0].getElementsByClass(stream.Measure) + barsNumber=len(measures) + for i in range(barsNumber): + notes=measures[i].getElementsByClass(note.GeneralNote) + if(len(notes)>0): + lastNote=notes[len(notes)-1] + if lastNote.duration.quarterLength<=0.25: + if(lastNote.isRest): + arrErrors.append(i) + else: + bs=lastNote.beams.getNumbers() + + if(len(bs)==0): + if(len(notes)>2): + noteBefore=notes[len(notes)-2] + if noteBefore.duration.quarterLength+lastNote.duration.quarterLength!=1: + arrErrors.append(i) + + + return arrErrors + + def flagIncorrectMeasures(self,omr2): + ''' + flag the incorrect measures from a omr using different methods + ''' + mf=MeasureFunctions() + sc=omr.correctors.ScoreCorrector(omr2) + part=sc.getSinglePart(0) + + arrErrors=[] + im=part.getIncorrectMeasureIndices(runFast=False) + + im1= mf.getIncorrectMeasureIndices(omr2) + + im2= mf.getPossibleBeamsErrors(omr2) + im3= mf.getPossibleLastNoteErrors(omr2) + + + arrErrors.append(im) + arrErrors.append(im1) + arrErrors.append(im2) + arrErrors.append(im3) + + if(len(im)>15): + if(len(im1)<len(im)): + im=im1 + imSum=list(set(im)|set(im2)|set(im3)) + imSum=sorted(imSum) + imOK=mf.correctIncorrectMeasuresArray(omr2,imSum) + print im + return imOK,arrErrors + + def getSlurs(self,part): + ''' + Returns the slurs from one part + ''' + slurs=part.flat.getElementsByClass(spanner.Spanner) + return slurs + + def reconstructScore(self,part,hashPart): + ''' + This function include rest measures in one part based on the hash part aligned + ''' + partReconstructed=stream.Part() + barNumber=1 + gaps=[] + for i in range(len(hashPart)): + if hashPart[i]!="*": + m=part.getElementsByClass(stream.Measure)[barNumber-1] + partReconstructed.append(m) + barNumber+=1 + else: + m=stream.Measure() + partReconstructed.append(m) + gaps.append(i) + slurs=self.getSlurs(part) + partReconstructed.append(slurs) + myStream=self.reorderMeasures(partReconstructed) + return myStream,gaps + + def reorderMeasures(self,omr): + ''' + Returns the correct measures number + ''' + slurs=self.getSlurs(omr) + s=stream.Part() + barNumber=1 + for measure in omr.getElementsByClass(stream.Measure): + measure.number=barNumber + s.append(measure) + barNumber+=1 + s.append(slurs) + return s + + def getNoteByOffset(self,voice,offset): + ''' + Returns one note in one voice according to the offset + ''' + for n in voice.getElementsByClass(note.Note): + if n.offset==offset: + return n + for n in voice.getElementsByClass(chord.Chord): + if n.offset==offset: + return n + + def convertVoicesToChord(self,omr): + ''' + Function that converts voices with the same rhythm to chords + ''' + for part in omr.getElementsByClass(stream.Part): + for measure in part.getElementsByClass(stream.Measure): + measure.show('text') + voices=measure.getElementsByClass(stream.Voice) + if len(voices)>=2: + for element1 in voices[0].getElementsByClass(note.GeneralNote): + offset1= element1.offset + for voice in voices: + if voice!=voices[0]: + element2=self.getNoteByOffset( voice, offset1) + if element2 is not None and element1.duration.quarterLength==element2.duration.quarterLength and not isinstance(element1,note.Rest): + mychord=self.mergeChords(element1,element2) + mychord.duration.quarterLength=element1.quarterLength + voices[0].replace(element1,mychord) + myrest=note.Rest() + myrest.duration.quarterLength=element1.quarterLength + voice.replace(element2,myrest) + + omr=self.removeRestVoice(omr) + return omr + def removeRestVoice(self,omr): + ''' + This option removes the rest voices (without notes) + ''' + for part in omr.getElementsByClass(stream.Part): + for measure in part.getElementsByClass(stream.Measure): + voices=measure.getElementsByClass(stream.Voice) + for voice in voices: + myvoice = copy.deepcopy(voice) + myvoice.removeByClass('Rest') + if len(myvoice)==0: + measure.remove(voice) + + return omr + + def mergeChords(self,element1,element2): + ''' + Returns one chord made by two elements, notes or chords + ''' + notes1=[] + notes2=[] + notes=[] + if isinstance(element1,chord.Chord): + notes1=element1.pitches + else: + notes1.append(element1.pitch) + + if isinstance(element2,chord.Chord): + notes2=element2.pitches + else: + notes2.append(element2.pitch) + + for n in notes1: + notes.append(n) + for n in notes2: + notes.append(n) + + mychord = chord.Chord(notes) + return mychord + + + def convertBeamsToTriplets(self,omr): + ''' + This option search for measures where the rhythm is bigger than the time signature + and tries to convert 3 note beams in triplets + ''' + tsDefault=omr.flat.getElementsByClass('TimeSignature')[0] + idPart=0 + for part in omr.getElementsByClass(stream.Part): + idPart+=1 + idMeasure=0 + for measure in part.getElementsByClass(stream.Measure): + idMeasure+=1 + if self._IsMeasureHigherTS(idMeasure, part,tsDefault): + print idMeasure + measure=self.beamsToTriplets(measure) + return omr + + + def _IsMeasureHigherTS(self,idMeasure,part,ts): + ''' + Inner function that detects if one measure is bigger than the time signature + ''' + idCountMeasure=0 + for measure in part.getElementsByClass(stream.Measure): + idCountMeasure+=1 + newts=measure.flat.getElementsByClass('TimeSignature') + if len(newts)>0: + ts=newts[0] + if idMeasure==idCountMeasure: + voices=measure.getElementsByClass(stream.Voice) + totalDuration=0 + if len(voices)>=1: + for voice in voices: + totalDuration=0 + for element in voice.getElementsByClass(note.GeneralNote): + totalDuration+=element.duration.quarterLength; + else: + for element in measure.getElementsByClass(note.GeneralNote): + totalDuration+=element.duration.quarterLength; + quartersBymeasure=ts.numerator*(4.0/ts.denominator) + if totalDuration>quartersBymeasure: + return True + else: + return False + def beamsToTriplets(self,measure): + ''' + This function returns one measure changing 3 notes beamed by triplets + ''' + voices=measure.getElementsByClass(stream.Voice) + if len(voices)>=1: + for voice in voices: + notes=voice.getElementsByClass(note.NotRest) + notes=self._notesToTriplets(notes) + + else: + notes=measure.getElementsByClass(note.NotRest) + notes=self._notesToTriplets(notes) + + return measure + + + def _notesToTriplets(self,notes): + ''' + Gets one note arrays and tries to convert to triplets + ''' + + for i in range(len(notes)): + self._removeSlurs(notes[i]) + beams1=notes[i].beams.beamsList + + try: + beams2=notes[i+1].beams.beamsList + beams3=notes[i+2].beams.beamsList + if len(beams1)>0 and len(beams2)>0 and len(beams3)>0: + if beams1[0].type=='start' and beams2[0].type=='continue' and beams3[0].type=='stop': + if notes[i].duration.quarterLength==notes[i+1].duration.quarterLength==notes[i+2].duration.quarterLength: + mytype=notes[i].duration.type + duration=note.duration.convertTypeToQuarterLength(mytype) + realDuration=duration*2.0/3.0 + notes[i].duration.quarterLength=realDuration + notes[i+1].duration.quarterLength=realDuration + notes[i+2].duration.quarterLength=realDuration + + except: + pass + + return notes + + def _removeSlurs(self,n): + ''' + Removes the slur in one note + ''' + for element in n.getSites(): + if isinstance(element,stream.SpannerStorage): + for e in element.elements: + element.pop(0) + + def removesEmptyVoices(self,omr): + ''' + Removes empty voices (just rests) + ''' + for part in omr.getElementsByClass(stream.Part): + for measure in part.getElementsByClass(stream.Measure): + print measure + voices=measure.getElementsByClass(stream.Voice) + if len(voices)>=1: + print len(voices) + for voice in voices: + notes=voice.getElementsByClass(note.NotRest) + if len(notes)==0: + index=measure.index(voice) + measure.pop(index) + voices=measure.getElementsByClass(stream.Voice) + if len(voices)==1: + measure.flattenUnnecessaryVoices() + return omr + + def removesGaps(self,omr): + ''' + This option changes gaps by rests in voices + ''' + for part in omr.getElementsByClass(stream.Part): + for measure in part.getElementsByClass(stream.Measure): + voices=measure.getElementsByClass(stream.Voice) + if len(voices)>=1: + for voice in voices: + v=voice.findGaps() + if v!=None: + nextOffset=1000 + for element in voice.getElementsByClass(note.GeneralNote): + offset= element.offset + if offset>nextOffset: + rest=note.Rest() + rest.duration.quarterLength=offset-nextOffset + voice.insert(nextOffset,rest) + nextOffset=1000 + else: + duration=element.duration.quarterLength + nextOffset=offset+duration + return omr + +