csong@0: ''' csong@0: Author: Chunyang Song csong@0: Institution: Centre for Digital Music, Queen Mary University of London csong@1: ''' csong@0: csong@12: from basic_functions import concatenate, repeat, subdivide, ceiling, get_rhythm_category csong@0: csong@12: terminalNodes = [] # Global variable, storing all the terminal nodes from recursive tree structure in time order csong@0: csong@1: # Each terminnal node contains two properties: its node type (note or rest) and its metrical weight. csong@1: class Node: csong@12: def __init__(self,nodeType,metricalWeight): csong@12: self.nodeType = nodeType csong@12: self.metricalWeight = metricalWeight csong@0: csong@1: # This function will recurse the tree for a binary sequence and return a sequence containing the terminal nodes in time order. csong@12: def recursive_tree(binarySequence, subdivisionSequence, weightSequence, metricalWeight, level): csong@12: # If matching to a Note type, add to terminal nodes csong@12: if binarySequence == concatenate([1],repeat([0],len(binarySequence)-1)): csong@12: terminalNodes.append(Node('N',metricalWeight)) csong@0: csong@12: # If matching to a Rest type, add to terminal nodes csong@12: elif binarySequence == repeat([0],len(binarySequence)): csong@12: terminalNodes.append(Node('R',metricalWeight)) csong@0: csong@12: # Keep subdividing by the subdivisor of the next level csong@12: else: csong@12: subBinarySequences = subdivide(binarySequence, subdivisionSequence[level+1]) csong@12: subWeightSequences = concatenate([metricalWeight],repeat([weightSequence[level+1]],subdivisionSequence[level+1]-1)) csong@12: for a in range(len(subBinarySequences)): csong@12: recursive_tree(subBinarySequences[a], subdivisionSequence, weightSequence, subWeightSequences[a], level+1) csong@0: csong@19: def are_parameters_valid(parameters): csong@19: areValid = False csong@19: if 'Lmax' not in parameters : csong@19: csong@19: def get_syncopation(bar, parameters = None): csong@12: ''' csong@12: The get_syncopation function calculates syncopation value . csong@12: ''' csong@12: #def get_syncopation(seq, subdivision_seq, weight_seq, prebar_seq, rhythm_category): csong@1: syncopation = None csong@12: csong@12: binarySequence = bar.get_binary_sequence() csong@12: subdivisionSequence = bar.get_subdivision_sequence() csong@12: csong@12: if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly': csong@12: print 'Warning: LHL model detects polyrhythms so returning None.' csong@1: else: csong@19: # If the parameters are not given, use the default settings csong@19: if parameters == None: csong@19: Lmax = 5 csong@19: weightSequence = range(0,-Lmax,-1) csong@19: else: csong@19: if are_parameters_valid(parameters): csong@19: Lmax = parameters['Lmax'] csong@19: weightSequence = parameters['W'] csong@19: else: csong@19: pass csong@19: #raise InvalidParameterError csong@12: csong@1: # If there is rhythm in previous bar, process its tree structure csong@12: if bar.get_previous_bar() != None: csong@12: prebarBinarySequence = bar.get_previous_bar().get_binary_sequence() csong@12: recursive_tree(ceiling(prebarBinarySequence), subdivisionSequence, weightSequence, weightSequence[0],0) csong@1: csong@1: # Only keep the last note-type node csong@12: while terminalNodes[-1].node_type != 'N': csong@12: del terminalNodes[-1] csong@12: del terminalNodes[0:-1] csong@0: csong@1: # For the rhythm in the current bar, process its tree structure and store the terminal nodes csong@12: recursive_tree(ceiling(binarySequence), subdivisionSequence, weightSequence, weightSequence[0],0) csong@1: csong@12: # for t in terminalNodes: csong@1: # print '<', t.node_type, t.metrical_weight, '>' csong@0: csong@12: # Search for the NR pairs that contribute to syncopation, csong@12: # then add the weight-difference to the NRpairSyncopation list csong@12: NRpairSyncopation = [] csong@12: for i in range(len(terminalNodes)-1,0,-1): csong@12: if terminalNodes[i].node_type == 'R': csong@1: for j in range(i-1, -1, -1): csong@12: if (terminalNodes[j].node_type == 'N') & (terminalNodes[i].metricalWeight >= terminalNodes[j].metricalWeight): csong@12: NRpairSyncopation.append(terminalNodes[i].metricalWeight - terminalNodes[j].metricalWeight) csong@1: break csong@12: #print NRpairSyncopation csong@0: csong@12: # If there are syncopation, sum all the local syncopation values stored in NRpairSyncopation list csong@12: if len(NRpairSyncopation) != 0: csong@12: syncopation = sum(NRpairSyncopation) csong@12: # If no syncopation, the value is -1; csong@12: elif len(terminalNodes) != 0: csong@1: syncopation = -1 csong@0: csong@1: return syncopation csong@12: csong@12: