csong@0
|
1 '''
|
csong@0
|
2 Author: Chunyang Song
|
csong@0
|
3 Institution: Centre for Digital Music, Queen Mary University of London
|
csong@1
|
4 '''
|
csong@0
|
5
|
csong@1
|
6 from basic_functions import concatenate, repeat, subdivide, ceiling
|
csong@0
|
7
|
csong@1
|
8 terminal_nodes = [] # Global variable, storing all the terminal nodes from recursive tree structure in time order
|
csong@0
|
9
|
csong@1
|
10 # Each terminnal node contains two properties: its node type (note or rest) and its metrical weight.
|
csong@1
|
11 class Node:
|
csong@1
|
12 def __init__(self,node_type,metrical_weight):
|
csong@1
|
13 self.node_type = node_type
|
csong@1
|
14 self.metrical_weight = metrical_weight
|
csong@0
|
15
|
csong@1
|
16 # This function will recurse the tree for a binary sequence and return a sequence containing the terminal nodes in time order.
|
csong@1
|
17 def recursive_tree(seq, subdivision_seq, weight_seq, metrical_weight, level):
|
csong@1
|
18 if seq == concatenate([1],repeat([0],len(seq)-1)): # If matching to a Note type, add to terminal nodes
|
csong@1
|
19 terminal_nodes.append(Node('N',metrical_weight))
|
csong@0
|
20
|
csong@1
|
21 elif seq == repeat([0],len(seq)): # If matching to a Rest type, add to terminal nodes
|
csong@1
|
22 terminal_nodes.append(Node('R',metrical_weight))
|
csong@0
|
23
|
csong@1
|
24 else: # Keep subdividing by the subdivisor of the next level
|
csong@1
|
25 sub_seq = subdivide(seq, subdivision_seq[level+1])
|
csong@1
|
26 sub_weight_seq = concatenate([metrical_weight],repeat([weight_seq[level+1]],subdivision_seq[level+1]-1))
|
csong@1
|
27 for a in range(len(sub_seq)):
|
csong@1
|
28 recursive_tree(sub_seq[a], subdivision_seq, weight_seq, sub_weight_seq[a], level+1)
|
csong@0
|
29
|
csong@1
|
30 # This function calculate syncoaption score for LHL model.
|
csong@1
|
31 def get_syncopation(seq, subdivision_seq, weight_seq, prebar_seq, rhythm_category):
|
csong@1
|
32 syncopation = None
|
csong@1
|
33 if rhythm_category == 'poly':
|
csong@1
|
34 print 'Error: LHL model cannot deal with polyrhythms.'
|
csong@1
|
35 else:
|
csong@1
|
36 # If there is rhythm in previous bar, process its tree structure
|
csong@1
|
37 if prebar_seq != None:
|
csong@1
|
38 recursive_tree(ceiling(prebar_seq), subdivision_seq, weight_seq, weight_seq[0],0)
|
csong@1
|
39
|
csong@1
|
40 # Only keep the last note-type node
|
csong@1
|
41 while terminal_nodes[-1].node_type != 'N':
|
csong@1
|
42 del terminal_nodes[-1]
|
csong@1
|
43 del terminal_nodes[0:-1]
|
csong@0
|
44
|
csong@1
|
45 # For the rhythm in the current bar, process its tree structure and store the terminal nodes
|
csong@1
|
46 recursive_tree(ceiling(seq), subdivision_seq, weight_seq, weight_seq[0],0)
|
csong@1
|
47
|
csong@1
|
48 # for t in terminal_nodes:
|
csong@1
|
49 # print '<', t.node_type, t.metrical_weight, '>'
|
csong@0
|
50
|
csong@1
|
51 # Search for the NR pairs that contribute to syncopation, add the weight-difference to the NR_pair_syncopation list
|
csong@1
|
52 NR_pair_syncopation = []
|
csong@1
|
53 for i in range(len(terminal_nodes)-1,0,-1):
|
csong@1
|
54 if terminal_nodes[i].node_type == 'R':
|
csong@1
|
55 for j in range(i-1, -1, -1):
|
csong@1
|
56 if (terminal_nodes[j].node_type == 'N') & (terminal_nodes[i].metrical_weight >= terminal_nodes[j].metrical_weight):
|
csong@1
|
57 NR_pair_syncopation.append(terminal_nodes[i].metrical_weight - terminal_nodes[j].metrical_weight)
|
csong@1
|
58 break
|
csong@1
|
59 #print NR_pair_syncopation
|
csong@0
|
60
|
csong@1
|
61 # If no syncopation, the value is -1; otherwise, sum all the local syncopation values stored in NR_pair_syncopation list
|
csong@1
|
62 if len(NR_pair_syncopation) != 0:
|
csong@1
|
63 syncopation = sum(NR_pair_syncopation)
|
csong@1
|
64 elif len(terminal_nodes) != 0:
|
csong@1
|
65 syncopation = -1
|
csong@0
|
66
|
csong@1
|
67 return syncopation
|