christopher@45: ''' christopher@45: Author: Chunyang Song christopher@45: Institution: Centre for Digital Music, Queen Mary University of London christopher@45: christopher@45: ''' christopher@45: christopher@45: from basic_functions import get_H, ceiling, velocity_sequence_to_min_timespan, get_rhythm_category christopher@45: from parameter_setter import are_parameters_valid christopher@45: christopher@45: # The get_metricity function calculates the metricity for a binary sequence with given sequence of metrical weights in a certain metrical level. christopher@45: def get_metricity(binarySequence, H): christopher@45: metricity = 0 christopher@45: for m in range(len(binarySequence)): christopher@45: metricity = metricity + binarySequence[m]*H[m] christopher@45: return metricity christopher@45: christopher@45: # The get_max_metricity function calculates the maximum metricity for the same number of notes in a binary sequence. christopher@45: def get_max_metricity(binarySequence, H): christopher@45: maxMetricity = 0 christopher@45: H.sort(reverse=True) # Sort the metrical weight sequence from large to small christopher@45: for i in range(sum(binarySequence)): christopher@45: maxMetricity = maxMetricity+H[i] christopher@45: return maxMetricity christopher@45: christopher@45: # find the metrical level L that contains the same number of metrical positions as the length of the binary sequence christopher@45: # if the given Lmax is not big enough to analyse the given sequence, request a bigger Lmax christopher@45: def find_L(rhythmSequence, Lmax, weightSequence, subdivisionSequence): christopher@45: L = Lmax christopher@45: christopher@45: # initially assuming the Lmax is not big enough christopher@45: needBiggerLmax = True christopher@45: christopher@45: # from the lowest metrical level (Lmax) to the highest, find the matching metrical level that christopher@45: # has the same length as the length of binary sequence christopher@45: while L >= 0: christopher@45: if len(get_H(weightSequence,subdivisionSequence, L)) == len(rhythmSequence): christopher@45: needBiggerLmax = False christopher@45: break christopher@45: else: christopher@45: L = L - 1 christopher@45: christopher@45: # if need a bigger Lmax, print error message and return None; otherwise return the matching metrical level L christopher@45: if needBiggerLmax: christopher@45: print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.' christopher@45: L = None christopher@45: christopher@45: return L christopher@45: christopher@45: # The get_syncopation function calculates the syncopation value of the given sequence for TMC model. christopher@45: #def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category): christopher@45: def get_syncopation(bar, parameters = None): christopher@45: syncopation = None christopher@45: binarySequence = bar.get_binary_sequence() christopher@45: subdivisionSequence = bar.get_subdivision_sequence() christopher@45: christopher@45: if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly': christopher@45: print 'Warning: TMC model detects polyrhythms so returning None.' christopher@45: else: christopher@45: christopher@45: # set the defaults christopher@45: Lmax = 5 christopher@45: weightSequence = range(Lmax+1,0,-1) # i.e. [6,5,4,3,2,1] christopher@45: christopher@45: if parameters!= None: christopher@45: if 'Lmax' in parameters: christopher@45: Lmax = parameters['Lmax'] christopher@45: if 'W' in parameters: christopher@45: weightSequence = parameters['W'] christopher@45: christopher@45: if not are_parameters_valid(Lmax, weightSequence, subdivisionSequence): christopher@45: print 'Error: the given parameters are not valid.' christopher@45: else: christopher@45: binarySequence = velocity_sequence_to_min_timespan(binarySequence) # converting to the minimum time-span format christopher@45: L = find_L(binarySequence, Lmax, weightSequence, subdivisionSequence) christopher@45: if L != None: christopher@45: #? generate the metrical weights of the lowest level, christopher@45: #? using the last matching_level number of elements in the weightSequence, to make sure the last element is 1 christopher@45: H = get_H (weightSequence[-(L+1):], subdivisionSequence, L) christopher@45: metricity = get_metricity(binarySequence, H) # converting to binary sequence then calculate metricity christopher@45: maxMetricity = get_max_metricity(binarySequence, H) christopher@45: christopher@45: syncopation = maxMetricity - metricity christopher@45: christopher@45: return syncopation christopher@45: