Mercurial > hg > multiomr
view 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 |
line wrap: on
line source
''' @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