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@12
|
6 from basic_functions import concatenate, repeat, subdivide, ceiling, get_rhythm_category
|
csong@0
|
7
|
csong@12
|
8 terminalNodes = [] # 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@12
|
12 def __init__(self,nodeType,metricalWeight):
|
csong@12
|
13 self.nodeType = nodeType
|
csong@12
|
14 self.metricalWeight = metricalWeight
|
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@12
|
17 def recursive_tree(binarySequence, subdivisionSequence, weightSequence, metricalWeight, level):
|
csong@12
|
18 # If matching to a Note type, add to terminal nodes
|
csong@12
|
19 if binarySequence == concatenate([1],repeat([0],len(binarySequence)-1)):
|
csong@12
|
20 terminalNodes.append(Node('N',metricalWeight))
|
csong@0
|
21
|
csong@12
|
22 # If matching to a Rest type, add to terminal nodes
|
csong@12
|
23 elif binarySequence == repeat([0],len(binarySequence)):
|
csong@12
|
24 terminalNodes.append(Node('R',metricalWeight))
|
csong@0
|
25
|
csong@12
|
26 # Keep subdividing by the subdivisor of the next level
|
csong@12
|
27 else:
|
csong@12
|
28 subBinarySequences = subdivide(binarySequence, subdivisionSequence[level+1])
|
csong@12
|
29 subWeightSequences = concatenate([metricalWeight],repeat([weightSequence[level+1]],subdivisionSequence[level+1]-1))
|
csong@12
|
30 for a in range(len(subBinarySequences)):
|
csong@12
|
31 recursive_tree(subBinarySequences[a], subdivisionSequence, weightSequence, subWeightSequences[a], level+1)
|
csong@0
|
32
|
csong@20
|
33 #!!!! needs fixing
|
csong@19
|
34 def are_parameters_valid(parameters):
|
csong@20
|
35 areValid = True
|
csong@20
|
36 # if 'Lmax' not in parameters :
|
csong@19
|
37
|
csong@19
|
38 def get_syncopation(bar, parameters = None):
|
csong@20
|
39 del terminalNodes[:]
|
csong@1
|
40 syncopation = None
|
csong@12
|
41
|
csong@12
|
42 binarySequence = bar.get_binary_sequence()
|
csong@12
|
43 subdivisionSequence = bar.get_subdivision_sequence()
|
csong@12
|
44
|
csong@12
|
45 if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly':
|
csong@12
|
46 print 'Warning: LHL model detects polyrhythms so returning None.'
|
csong@1
|
47 else:
|
csong@19
|
48 # If the parameters are not given, use the default settings
|
csong@19
|
49 if parameters == None:
|
csong@19
|
50 Lmax = 5
|
csong@20
|
51 weightSequence = range(0,-Lmax,-1) # i.e. [0,-1,-2,-3,-4]
|
csong@19
|
52 else:
|
csong@19
|
53 if are_parameters_valid(parameters):
|
csong@19
|
54 Lmax = parameters['Lmax']
|
csong@19
|
55 weightSequence = parameters['W']
|
csong@19
|
56 else:
|
csong@19
|
57 pass
|
csong@19
|
58 #raise InvalidParameterError
|
csong@12
|
59
|
csong@1
|
60 # If there is rhythm in previous bar, process its tree structure
|
csong@12
|
61 if bar.get_previous_bar() != None:
|
csong@12
|
62 prebarBinarySequence = bar.get_previous_bar().get_binary_sequence()
|
csong@12
|
63 recursive_tree(ceiling(prebarBinarySequence), subdivisionSequence, weightSequence, weightSequence[0],0)
|
csong@1
|
64
|
csong@1
|
65 # Only keep the last note-type node
|
csong@20
|
66 while terminalNodes[-1].nodeType != 'N':
|
csong@12
|
67 del terminalNodes[-1]
|
csong@12
|
68 del terminalNodes[0:-1]
|
csong@0
|
69
|
csong@1
|
70 # For the rhythm in the current bar, process its tree structure and store the terminal nodes
|
csong@12
|
71 recursive_tree(ceiling(binarySequence), subdivisionSequence, weightSequence, weightSequence[0],0)
|
csong@1
|
72
|
csong@12
|
73 # for t in terminalNodes:
|
csong@20
|
74 # print '<', t.nodeType, t.metricalWeight, '>'
|
csong@0
|
75
|
csong@12
|
76 # Search for the NR pairs that contribute to syncopation,
|
csong@12
|
77 # then add the weight-difference to the NRpairSyncopation list
|
csong@12
|
78 NRpairSyncopation = []
|
csong@12
|
79 for i in range(len(terminalNodes)-1,0,-1):
|
csong@20
|
80 if terminalNodes[i].nodeType == 'R':
|
csong@1
|
81 for j in range(i-1, -1, -1):
|
csong@20
|
82 if (terminalNodes[j].nodeType == 'N') & (terminalNodes[i].metricalWeight >= terminalNodes[j].metricalWeight):
|
csong@12
|
83 NRpairSyncopation.append(terminalNodes[i].metricalWeight - terminalNodes[j].metricalWeight)
|
csong@1
|
84 break
|
csong@12
|
85 #print NRpairSyncopation
|
csong@0
|
86
|
csong@12
|
87 # If there are syncopation, sum all the local syncopation values stored in NRpairSyncopation list
|
csong@12
|
88 if len(NRpairSyncopation) != 0:
|
csong@12
|
89 syncopation = sum(NRpairSyncopation)
|
csong@12
|
90 # If no syncopation, the value is -1;
|
csong@12
|
91 elif len(terminalNodes) != 0:
|
csong@1
|
92 syncopation = -1
|
csong@0
|
93
|
csong@1
|
94 return syncopation
|
csong@12
|
95
|
csong@12
|
96
|