csong@0: ''' csong@0: Author: Chunyang Song csong@0: Institution: Centre for Digital Music, Queen Mary University of London csong@0: csong@0: ''' csong@0: csong@2: from BasicFuncs import get_H, get_min_timeSpan csong@0: csong@1: def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category): csong@1: syncopation = None csong@1: if rhythm_category == 'poly': csong@1: print 'Error: SG model cannot deal with polyrhythms.' csong@0: else: csong@1: csong@1: seq = get_min_timeSpan(seq) # converting to the minimum time-span format csong@1: csong@1: # checking whether the given L_max is enough to analyse the given sequence, if not, request a bigger L_max csong@1: new_L_max = True csong@1: matching_level = L_max csong@1: while matching_level >= 0: csong@1: if len(get_H(weight_seq,subdivision_seq, matching_level)) == len(seq): csong@1: new_L_max = False csong@1: break csong@1: else: csong@1: matching_level = matching_level - 1 csong@0: csong@1: if new_L_max == True: csong@1: print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.' csong@0: csong@1: else: csong@1: syncopation = 0 csong@1: # generate the metrical weights of the lowest level csong@1: H = get_H(weight_seq,subdivision_seq, matching_level) csong@0: csong@1: # The aveDif_neighbours function calculates the (weighted) average of the difference between the note at a certain index and its neighbours in a certain metrical level csong@1: def aveDif_neighbours(index, level): csong@0: averages = [] csong@1: parameter_garma = 0.8 csong@0: csong@1: # The findPre function is to calculate the index of the previous neighbour at a certain metrical level. csong@1: def findPre(index, level): csong@1: pre_index = (index - 1)%len(H) csong@1: while(H[pre_index] > level): csong@1: pre_index = (pre_index - 1)%len(H) csong@0: return pre_index csong@0: csong@1: # The findPost function is to calculate the index of the next neighbour at a certain metrical level. csong@1: def findPost(index, level): csong@1: post_index = (index + 1)%len(H) csong@1: while(H[post_index] > level): csong@1: post_index = (post_index + 1)%len(H) csong@0: return post_index csong@0: csong@1: # The dif function is to calculate a difference level factor between two notes (at note position index1 and index 2) in velocity sequence csong@0: def dif(index1,index2): csong@0: parameter_beta = 0.5 csong@1: dif_v = seq[index1]-seq[index2] csong@1: dif_h = abs(H[index1]-H[index2]) csong@0: dif = dif_v*(parameter_beta*dif_h/4+1-parameter_beta) csong@0: return dif csong@0: csong@1: # From the highest to the lowest metrical levels where the current note resides, calculate the difference between the note and its neighbours at that level csong@1: for l in range(level, max(H)+1): csong@1: ave = ( parameter_garma*dif(index,findPre(index,l))+dif(index,findPost(index,l)) )/(1+parameter_garma) csong@0: averages.append(ave) csong@0: return averages csong@0: csong@1: # Calculate the syncopation value for each note csong@1: for index in range(len(seq)): csong@1: if seq[index] != 0: # Onset detected csong@1: h = H[index] csong@1: potential = 1 - pow(0.5,h) # Syncopation potential according to its metrical level, which is equal to the metrical weight csong@1: level = h # Metrical weight happens to be equal to its metrical level csong@1: syncopation += min(aveDif_neighbours(index, h))*potential csong@1: csong@1: return syncopation