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