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