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@26: from basic_functions import get_H, velocity_sequence_to_min_timespan, get_rhythm_category, upsample_velocity_sequence csong@32: from parameter_setter import are_parameters_valid csong@0: csong@20: def get_syncopation(bar, parameters = None): csong@1: syncopation = None csong@20: velocitySequence = bar.get_velocity_sequence() csong@20: subdivisionSequence = bar.get_subdivision_sequence() csong@20: csong@20: if get_rhythm_category(velocitySequence, subdivisionSequence) == 'poly': csong@20: print 'Warning: SG model detects polyrhythms so returning None.' csong@0: else: csong@26: #velocitySequence = velocity_sequence_to_min_timespan(velocitySequence) # converting to the minimum time-span format csong@20: csong@28: # set the defaults csong@28: Lmax = 5 csong@28: weightSequence = range(Lmax+1) # i.e. [0,1,2,3,4,5] csong@28: if parameters!= None: csong@28: if 'Lmax' in parameters: csong@28: Lmax = parameters['Lmax'] csong@28: if 'W' in parameters: csong@28: weightSequence = parameters['W'] csong@28: csong@28: if not are_parameters_valid(Lmax, weightSequence, subdivisionSequence): csong@28: print 'Error: the given parameters are not valid.' csong@20: else: csong@28: # generate the metrical weights of level Lmax, and upsample(stretch) the velocity sequence to match the length of H csong@28: H = get_H(weightSequence,subdivisionSequence, Lmax) csong@33: csong@28: velocitySequence = upsample_velocity_sequence(velocitySequence, len(H)) csong@0: csong@28: # The ave_dif_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@28: def ave_dif_neighbours(index, level): csong@0: csong@28: averages = [] csong@28: parameterGarma = 0.8 csong@28: csong@28: # The findPre function is to calculate the index of the previous neighbour at a certain metrical level. csong@28: def find_pre(index, level): csong@28: preIndex = (index - 1)%len(H) # using % is to restrict the index varies within range(0, len(H)) csong@28: while(H[preIndex] > level): csong@28: preIndex = (preIndex - 1)%len(H) csong@28: #print 'preIndex', preIndex csong@28: return preIndex csong@20: csong@28: # The findPost function is to calculate the index of the next neighbour at a certain metrical level. csong@28: def find_post(index, level): csong@28: postIndex = (index + 1)%len(H) csong@28: while(H[postIndex] > level): csong@28: postIndex = (postIndex + 1)%len(H) csong@28: #print 'postIndex', postIndex csong@28: return postIndex csong@28: csong@28: # The dif function is to calculate a difference level factor between two notes (at note position index1 and index 2) in velocity sequence csong@28: def dif(index1,index2): csong@28: parameterBeta = 0.5 csong@28: dif_v = velocitySequence[index1]-velocitySequence[index2] csong@28: dif_h = abs(H[index1]-H[index2]) csong@28: dif = dif_v*(parameterBeta*dif_h/4+1-parameterBeta) csong@28: #print 'dif', dif csong@28: return dif csong@28: csong@28: # 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@28: for l in range(level, max(H)+1): csong@28: ave = (parameterGarma*dif(index,find_pre(index,l))+dif(index,find_post(index,l)) )/(1+parameterGarma) csong@28: averages.append(ave) csong@28: #print 'averages', averages csong@28: return averages csong@28: csong@28: # if the upsampling was successfully done csong@28: if velocitySequence != None: csong@28: syncopation = 0 csong@28: # Calculate the syncopation value for each note csong@28: for index in range(len(velocitySequence)): csong@28: if velocitySequence[index] != 0: # Onset detected csong@28: h = H[index] csong@28: # Syncopation potential according to its metrical level, which is equal to the metrical weight csong@28: potential = 1 - pow(0.5,h) csong@28: level = h # Metrical weight is equal to its metrical level csong@28: syncopation += min(ave_dif_neighbours(index, level))*potential csong@32: else: csong@32: print 'Try giving a bigger Lmax so that the rhythm sequence can be measured by the matching metrical weights sequence (H).' csong@1: return syncopation