csong@0: ''' csong@0: Author: Chunyang Song csong@0: Institution: Centre for Digital Music, Queen Mary University of London csong@1: ''' csong@0: csong@1: from basic_functions import concatenate, repeat, subdivide, ceiling csong@0: csong@1: terminal_nodes = [] # 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@1: def __init__(self,node_type,metrical_weight): csong@1: self.node_type = node_type csong@1: self.metrical_weight = metrical_weight 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@1: def recursive_tree(seq, subdivision_seq, weight_seq, metrical_weight, level): csong@1: if seq == concatenate([1],repeat([0],len(seq)-1)): # If matching to a Note type, add to terminal nodes csong@1: terminal_nodes.append(Node('N',metrical_weight)) csong@0: csong@1: elif seq == repeat([0],len(seq)): # If matching to a Rest type, add to terminal nodes csong@1: terminal_nodes.append(Node('R',metrical_weight)) csong@0: csong@1: else: # Keep subdividing by the subdivisor of the next level csong@1: sub_seq = subdivide(seq, subdivision_seq[level+1]) csong@1: sub_weight_seq = concatenate([metrical_weight],repeat([weight_seq[level+1]],subdivision_seq[level+1]-1)) csong@1: for a in range(len(sub_seq)): csong@1: recursive_tree(sub_seq[a], subdivision_seq, weight_seq, sub_weight_seq[a], level+1) csong@0: csong@1: # This function calculate syncoaption score for LHL model. csong@1: def get_syncopation(seq, subdivision_seq, weight_seq, prebar_seq, rhythm_category): csong@1: syncopation = None csong@1: if rhythm_category == 'poly': csong@1: print 'Error: LHL model cannot deal with polyrhythms.' csong@1: else: csong@1: # If there is rhythm in previous bar, process its tree structure csong@1: if prebar_seq != None: csong@1: recursive_tree(ceiling(prebar_seq), subdivision_seq, weight_seq, weight_seq[0],0) csong@1: csong@1: # Only keep the last note-type node csong@1: while terminal_nodes[-1].node_type != 'N': csong@1: del terminal_nodes[-1] csong@1: del terminal_nodes[0:-1] csong@0: csong@1: # For the rhythm in the current bar, process its tree structure and store the terminal nodes csong@1: recursive_tree(ceiling(seq), subdivision_seq, weight_seq, weight_seq[0],0) csong@1: csong@1: # for t in terminal_nodes: csong@1: # print '<', t.node_type, t.metrical_weight, '>' csong@0: csong@1: # Search for the NR pairs that contribute to syncopation, add the weight-difference to the NR_pair_syncopation list csong@1: NR_pair_syncopation = [] csong@1: for i in range(len(terminal_nodes)-1,0,-1): csong@1: if terminal_nodes[i].node_type == 'R': csong@1: for j in range(i-1, -1, -1): csong@1: if (terminal_nodes[j].node_type == 'N') & (terminal_nodes[i].metrical_weight >= terminal_nodes[j].metrical_weight): csong@1: NR_pair_syncopation.append(terminal_nodes[i].metrical_weight - terminal_nodes[j].metrical_weight) csong@1: break csong@1: #print NR_pair_syncopation csong@0: csong@1: # If no syncopation, the value is -1; otherwise, sum all the local syncopation values stored in NR_pair_syncopation list csong@1: if len(NR_pair_syncopation) != 0: csong@1: syncopation = sum(NR_pair_syncopation) csong@1: elif len(terminal_nodes) != 0: csong@1: syncopation = -1 csong@0: csong@1: return syncopation