Mercurial > hg > multiomr
diff Process/SymbolConversion.py @ 2:46fb79167a61 tip
Main Code
author | Victor Padilla <victor.padilla.mc@gmail.com> |
---|---|
date | Mon, 04 May 2015 22:56:18 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Process/SymbolConversion.py Mon May 04 22:56:18 2015 +0200 @@ -0,0 +1,575 @@ +''' +Created on 10/11/2014 + +@organization: Lancaster University & University of Leeds +@version: 1.0 +Created on 11/12/2014 + +@author: Victor Padilla +@contact: v.padilla@lancaster.ac.uk + +Functions related to convert music21 output +to symbols for aligning and voting +''' +from music21 import note +from music21 import chord +from music21 import meter +from music21 import key +from music21 import stream +from music21 import tie +from music21 import bar +from music21 import harmony +from music21 import clef +class SymbolConversion: + + + def __getSymbolMesure(self,symbol): + ''' + Detects if it is a barline symbol and gets the first element + ''' + if isinstance(symbol,list): + if symbol[0].find('!')!=-1: + return symbol + + def convertM21(self,symbolArray): + ''' + Converts one Array of symbols to music21 + + usage: + + outVote=vote.vote(omr_symbolsAlign) + #apply voices if it is needed + + outVote=sc.setVoices(outVote) + #convert to music21 + resultS2=sc.convertM21(outVote) + + ''' + + sOut=stream.Score() + sPart=stream.Part() + measureIndex=1 + measure=stream.Measure() + measure.number=measureIndex + voicesLength=8 + for v in range(voicesLength): + voice=stream.Voice() + voice.id=v + measure.append(voice) + indexS=0 + + for symbol in symbolArray: + mytie="" + realDuration=None + s=symbol + + if isinstance(s,list): + s=s[0] + if s.find('TS:')!=-1: + ts=meter.TimeSignature(s[3:]) + + measure.append(ts) + if s.find('KS:')!=-1: + k=key.KeySignature(int(s[3:])) + + measure.append(k) + if s.find('CL:')!=-1: + c=clef.clefFromString(str(s[3:])) + + measure.append(c) + if s.find('N:')!=-1: + try: + if isinstance(symbol,list): + realDuration=symbol[1] + mytie=symbol[2] + + sep=s.index("_") + duration=s[sep+1:] + #************************************* + #duration vs realDuration for triplets + if realDuration!=None: + duration=realDuration + #************************************* + if(float(duration)>0): + n=note.Note(s[2:sep],quarterLength=float(duration)) + if symbol[5]!=None: + n.color=symbol[5] + if mytie!="": + n.tie=tie.Tie(mytie) + if len(symbol)>6:#voices + measure.voices[symbol[6]].append(n) + else: + measure.append(n) + except: + print "error"+s + + if s.find('R:')!=-1: + try: + if isinstance(symbol,list): + realDuration=symbol[1] + mytie=symbol[2] + duration=s[2:] + #************************************* + #duration vs realDuration for triplets + if realDuration!=None: + duration=realDuration + #************************************* + n=note.Rest(quarterLength=float(duration)) + if symbol[5]!=None: + n.color=symbol[5] + if len(symbol)>6:#voices + measure.voices[symbol[6]].append(n) + else: + measure.append(n) + except: + print "error"+s + + if s.find('C:')!=-1: + notes=s.split("[:") + cPitch=[] + for n in notes: + if n!='C:': + sep=n.index("_") + duration=n[sep+1:] + pitch= n[0:sep] + cPitch.append(pitch) + c=chord.Chord(cPitch) + c.duration.quarterLength=float(duration) + if symbol[5]!=None: + c.color=symbol[5] + if mytie!="": + c.tie=tie.Tie(mytie) + + if len(symbol)>6:#voices + measure.voices[symbol[6]].append(c) + else: + measure.append(c) + + if s.find('!')!=-1: + if isinstance(symbol,list): + barType= symbol[1] + barRepeat= symbol[2] + if barType!="": + try: + mybartype=bar.styleToMusicXMLBarStyle(barType) + myBar=bar.Barline(style=mybartype) + measure.rightBarline=myBar + except: + print "error barline" + + if barRepeat!="": + try: + myBar=bar.Repeat(direction=barRepeat) + if barRepeat=="start": + measure.leftBarline=myBar + if barRepeat=="end": + measure.rightBarline=myBar + except: + print "error barline" + sPart.append(measure) + measureIndex+=1 + measure=stream.Measure() + measure.number=measureIndex + for v in range(voicesLength): + voice=stream.Voice() + voice.id=v + measure.append(voice) + + indexS+=1 + + sOut.append(sPart) + return sOut + + + + + def __orderChord(self,mychord): + ''' + Private function that returns a chord ordered + in a string from lower to higher + ''' + midi=[] + midi2=[] + orderC=[] + for n in mychord: + if isinstance(n,note.Note): + midi.append(n.midi) + midi2.append(n.midi) + + while len(midi)>0: + indexMin=midi2.index(min(midi)) + indexPop=midi.index(min(midi)) + orderC.append(mychord[indexMin]) + midi.pop(indexPop) + + myOrderChord=chord.Chord(orderC) + myOrderChord.duration.quarterLength=mychord.duration.quarterLength + return myOrderChord + + + def __getKeyOffset(self,item): + ''' + Private function that returns the offset of one item + ''' + return item['offset'] + + def __removeDuplicates(self,input_raw): + ''' + Private function to remove duplicates in the string input + ''' + seen = set() + new_l = [] + for d in input_raw: + t = tuple(d.items()) + if t not in seen: + seen.add(t) + new_l.append(d) + + return new_l + + + def getElementsFromMeasureOrderByOffset(self,measure): + ''' + Returns the symbols of a measure ordered by offset + ''' + offset=measure.flat.offsetMap + offset_n=self.__removeDuplicates(offset) + sorted_offset=sorted(offset_n,key=self.__getKeyOffset) + return sorted_offset + + def __swapSymbols(self,s1,s2): + ''' + Swap the s1,s2 symbols + ''' + inter=s1 + s1=s2 + s2=inter + return s1,s2 + + def orderSymbolsByPitch(self,symbols): + ''' + Orders the symbol string from lower to high if they have + the same offset + ''' + for i in range(len(symbols)-1): + for j in range(i,i+2): + pitch_i=0 + pitch_j=0 + if(symbols[i]['offset']==symbols[j]['offset']): + if isinstance(symbols[i]['element'],note.Note): + pitch_i=symbols[i]['element'].pitch.midi + if isinstance(symbols[j]['element'],note.Note): + pitch_j=symbols[j]['element'].pitch.midi + + if pitch_j>pitch_i:#higher pitch first + symbols[i],symbols[j]=self.__swapSymbols(symbols[i],symbols[j]) + + if pitch_j==pitch_i and pitch_j>0:#longest first if equal pitch + if symbols[i]['element'].duration<symbols[j]['element'].duration: + symbols[i],symbols[j]=self.__swapSymbols(symbols[i],symbols[j]) + + if isinstance(symbols[j]['element'],note.Rest):#rest first + symbols[i],symbols[j]=self.__swapSymbols(symbols[i],symbols[j]) + + return symbols + + def getRepetitionMarks(self,measure): + ''' + Returns the repetition marks symbol from a measure + ''' + symbols=[] + for barline in measure.getElementsByClass(bar.Barline): + symbol={'voiceIndex': None, 'element': barline, 'endTime': 0.0, 'offset': barline.offset} + symbols.append(symbol) + return symbols + + def getTS(self,measure): + ''' + Returns the time signature symbol from a measure + ''' + symbols=[] + for ts in measure.getElementsByClass(meter.TimeSignature): + symbol={'voiceIndex': None, 'element': ts, 'endTime': 0.0, 'offset': ts.offset} + symbols.append(symbol) + return symbols + + def removeTS(self,symbols): + ''' + Removes the time signature from the symbols array + ''' + for symbol in symbols: + s=symbol['element'] + if 'TimeSignature' in s.classes: + symbols.pop(symbols.index(symbol)) + break + return symbols + + def filterOMR(self,omr,idPart): + ''' + Removes elements from the music21 input as text, slurs and expressions (p, mp, f...) + + Returns the omr filtered and the symbols array converted + + ''' + omr_filtered=stream.Stream() + omr_symbols=[] + #for single parts + print "Partes=",len(omr.parts) + if len(omr.parts)==1: + idPart=0 + if len(omr.parts)==0: + idPart=0 + mypart=omr + if len(omr.parts)>=1: + if(int(idPart)>=len(omr.parts)): + print "error in OMR" + return [],[] + mypart=omr.parts[int(idPart)] + + + + isFirstClef=True + indexMes=0 + print idPart + for measure in mypart.getElementsByClass(stream.Measure): + indexMes+=1 + symbols=self.getElementsFromMeasureOrderByOffset(measure.semiFlat)#key signature time signature and clef are missing if not flat + symbols=self.orderSymbolsByPitch(symbols) + symbolsRepetition=self.getRepetitionMarks(measure) + symbolsTS=self.getTS(measure) + symbols=self.removeTS(symbols) + symbols=symbolsTS+symbols+symbolsRepetition + + newMeasure=stream.Measure() + styleBarline="" + directionRepeat="" + strClef="" + + for symbol in symbols: + #symbol={'voiceIndex': 0, 'element': <music21.note.Note C>, 'endTime': 1.0, 'offset': 0.0} + s=symbol['element'] + if 'TimeSignature' in s.classes: + newMeasure.append(s) + str0="TS:"+str(s.numerator)+"/"+str(s.denominator) + omr_symbols.append([str0,None,None,symbol['offset'],symbol['endTime'],None]) + + elif 'KeySignature' in s.classes: + newMeasure.append(s) + str0="KS:"+str(s.sharps) + omr_symbols.append([str0,None,None,symbol['offset'],symbol['endTime'],None]) + + elif 'Clef' in s.classes: + newMeasure.append(s) + strClef="CL:"+str(s.sign) + if isFirstClef: + omr_symbols.append([strClef,None,None,symbol['offset'],symbol['endTime'],None]) + strClef="" + isFirstClef=False + + elif 'Note' in s.classes: + newMeasure.append(s) + mytype=s.duration.type + duration=note.duration.convertTypeToQuarterLength(mytype) + realDuration=s.duration.quarterLength + if realDuration==duration+duration/2: #dot case + duration=realDuration + n="N:"+s.pitch.nameWithOctave+"_"+str(duration) + # Ties case + mytie="" + if s.tie!=None: + mytie=s.tie.type + + if float(realDuration)>0: + omr_symbols.append([n,realDuration,mytie,symbol['offset'],symbol['endTime'],s.color] ) + + elif 'Rest' in s.classes: + newMeasure.append(s) + mytype=s.duration.type + if mytype!="complex": + duration=note.duration.convertTypeToQuarterLength(mytype) + else: + duration=s.duration.quarterLength + realDuration=s.duration.quarterLength + if realDuration==duration+duration/2: #dot case + duration=realDuration + n="R:"+str(duration) +# realDuration=s.duration.quarterLength + omr_symbols.append([n,realDuration,False,symbol['offset'],symbol['endTime'],s.color]) + + elif 'Chord' in s.classes: + if type(s) is not harmony.ChordSymbol: + newMeasure.append(s) + mytype=s.duration.type + duration=note.duration.convertTypeToQuarterLength(mytype) + realDuration=s.duration.quarterLength + if realDuration==duration+duration/2: #dot case + duration=realDuration + chord="C:" + sOrder=self.__orderChord(s) + for n in sOrder: + chord+="[:"+n.pitch.nameWithOctave+"_"+str(duration) + + + + # Ties case + mytie="" + if s.tie!=None: + mytie=s.tie.type + omr_symbols.append([chord,realDuration,mytie,symbol['offset'],symbol['endTime'],s.color]) + + elif 'Barline' in s.classes: + styleBarline=s.style + try: + directionRepeat=s.direction + except: + directionRepeat="" + + + omr_symbols.append(['!',styleBarline,directionRepeat,symbol['offset'],symbol['endTime'],None]) + if strClef!="": + omr_symbols.append([strClef,None,None,0,0,None]) + omr_filtered.append(newMeasure) + return omr_filtered,omr_symbols + + def removeExtraTies(self,arraySymbols): + ''' + Removes the non logical ties from the symbol array + + ''' + for s in arraySymbols: + if isinstance(s,list): + mainSymbol=s[0] + if mainSymbol.find('N:')!=-1: + tie1=s[2] + if arraySymbols.index(s)<len(arraySymbols): + sNext=arraySymbols[arraySymbols.index(s)+1] + if isinstance(sNext,list): + mainSymbol2=sNext[0] + if mainSymbol2.find('N:')!=-1: + tie2=sNext[2] + if tie1=='start' and tie2!='end': + s[2]='' + + return arraySymbols + + + def setVoices(self,strSymbols): + ''' + Divides each measure in voices, if it is necessary. + + The output is the symbol string using voices + + ''' + quarters=self.__getTimeSignatureQuarters(strSymbols) + measures=self.__chopMeasures(strSymbols) + newString=[] + for measure in measures: + newMeasure=self.divideMeasureinVoices(measure,quarters) + for s in newMeasure: + newString.append(s) + return newString + + def __getTimeSignatureQuarters(self,strSymbols): + ''' + returns the number of quarters of the first time signature found + ''' + quarters=4 #4/4 by default + for s in strSymbols: + if isinstance(s,list): + s=s[0] + if s.find('TS:')!=-1: + ts=s[3:] + timeSig=ts.split("/") + num=int(timeSig[0]) + den=int(timeSig[1]) + quarters=(4/den)*num + return quarters + + def __chopMeasures(self,strSymbols): + ''' + Divides the symbol string in measures + ''' + strMeasures=[] + strMeasure=[] + for s in strSymbols: + strMeasure.append(s) + bar=self.__getSymbolMesure(s) + if bar!=None: + strMeasures.append(strMeasure) + strMeasure=[] + return strMeasures + + + def isNoteRest(self,symbol): + ''' + Returns true if the symbol is note, rest or chord + ''' + s=symbol + if isinstance(s,list): + if s[0].find('N:')!=-1: + return True + if s[0].find('R:')!=-1: + return True + if s[0].find('C:')!=-1: + return True + return False + return False + + def divideMeasureinVoices(self,strMeasure,quarters): + ''' + If the length of each measure in the string is higher than quarters, + adjusting to voices + + ''' + newMeasure=[] + duration=0 + #if duration is higher than time signatura + for s in strMeasure: + if self.isNoteRest(s): + if s[1]!=None: + try: + duration+=s[1] + except: + pass + # voices only if needed + if duration<=quarters: + return strMeasure + + + for s in strMeasure: + if not self.isNoteRest(s): + if self.__getSymbolMesure(s)==None: + newMeasure.append(s) + voiceslength=8 + for v in range(voiceslength-1): + offset=0.0 + firstNote=False + for s in strMeasure: + if self.isNoteRest(s): + if len(s)<7:#not voice yet + offsetNote=s[3] + if firstNote==False: + offset=offsetNote + firstNote=True + if offsetNote>0: + rest=[] + rest.append("R:"+str(offsetNote)) + rest.append(offsetNote) + rest.append(False) + rest.append(0) + rest.append(offsetNote) + rest.append(None) + rest.append(v) + newMeasure.append(rest) + if(offset==offsetNote): + s.append(v) + newMeasure.append(s) + offset=s[4] + + + for s in strMeasure: + if self.__getSymbolMesure(s)!=None: + newMeasure.append(s) + + + return newMeasure \ No newline at end of file