csong@0
|
1 '''
|
csong@0
|
2 Author: Chunyang Song
|
csong@0
|
3 Institution: Centre for Digital Music, Queen Mary University of London
|
csong@0
|
4
|
csong@0
|
5 '''
|
csong@0
|
6
|
csong@1
|
7 from basic_functions import get_H, get_min_timeSpan
|
csong@0
|
8
|
csong@1
|
9 def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
|
csong@1
|
10 syncopation = None
|
csong@1
|
11 if rhythm_category == 'poly':
|
csong@1
|
12 print 'Error: SG model cannot deal with polyrhythms.'
|
csong@0
|
13 else:
|
csong@1
|
14
|
csong@1
|
15 seq = get_min_timeSpan(seq) # converting to the minimum time-span format
|
csong@1
|
16
|
csong@1
|
17 # checking whether the given L_max is enough to analyse the given sequence, if not, request a bigger L_max
|
csong@1
|
18 new_L_max = True
|
csong@1
|
19 matching_level = L_max
|
csong@1
|
20 while matching_level >= 0:
|
csong@1
|
21 if len(get_H(weight_seq,subdivision_seq, matching_level)) == len(seq):
|
csong@1
|
22 new_L_max = False
|
csong@1
|
23 break
|
csong@1
|
24 else:
|
csong@1
|
25 matching_level = matching_level - 1
|
csong@0
|
26
|
csong@1
|
27 if new_L_max == True:
|
csong@1
|
28 print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.'
|
csong@0
|
29
|
csong@1
|
30 else:
|
csong@1
|
31 syncopation = 0
|
csong@1
|
32 # generate the metrical weights of the lowest level
|
csong@1
|
33 H = get_H(weight_seq,subdivision_seq, matching_level)
|
csong@0
|
34
|
csong@1
|
35 # The aveDif_neighbours function calculates the (weighted) average of the difference between the note at a certain index and its neighbours in a certain metrical level
|
csong@1
|
36 def aveDif_neighbours(index, level):
|
csong@0
|
37 averages = []
|
csong@1
|
38 parameter_garma = 0.8
|
csong@0
|
39
|
csong@1
|
40 # The findPre function is to calculate the index of the previous neighbour at a certain metrical level.
|
csong@1
|
41 def findPre(index, level):
|
csong@1
|
42 pre_index = (index - 1)%len(H)
|
csong@1
|
43 while(H[pre_index] > level):
|
csong@1
|
44 pre_index = (pre_index - 1)%len(H)
|
csong@0
|
45 return pre_index
|
csong@0
|
46
|
csong@1
|
47 # The findPost function is to calculate the index of the next neighbour at a certain metrical level.
|
csong@1
|
48 def findPost(index, level):
|
csong@1
|
49 post_index = (index + 1)%len(H)
|
csong@1
|
50 while(H[post_index] > level):
|
csong@1
|
51 post_index = (post_index + 1)%len(H)
|
csong@0
|
52 return post_index
|
csong@0
|
53
|
csong@1
|
54 # The dif function is to calculate a difference level factor between two notes (at note position index1 and index 2) in velocity sequence
|
csong@0
|
55 def dif(index1,index2):
|
csong@0
|
56 parameter_beta = 0.5
|
csong@1
|
57 dif_v = seq[index1]-seq[index2]
|
csong@1
|
58 dif_h = abs(H[index1]-H[index2])
|
csong@0
|
59 dif = dif_v*(parameter_beta*dif_h/4+1-parameter_beta)
|
csong@0
|
60 return dif
|
csong@0
|
61
|
csong@1
|
62 # From the highest to the lowest metrical levels where the current note resides, calculate the difference between the note and its neighbours at that level
|
csong@1
|
63 for l in range(level, max(H)+1):
|
csong@1
|
64 ave = ( parameter_garma*dif(index,findPre(index,l))+dif(index,findPost(index,l)) )/(1+parameter_garma)
|
csong@0
|
65 averages.append(ave)
|
csong@0
|
66 return averages
|
csong@0
|
67
|
csong@1
|
68 # Calculate the syncopation value for each note
|
csong@1
|
69 for index in range(len(seq)):
|
csong@1
|
70 if seq[index] != 0: # Onset detected
|
csong@1
|
71 h = H[index]
|
csong@1
|
72 potential = 1 - pow(0.5,h) # Syncopation potential according to its metrical level, which is equal to the metrical weight
|
csong@1
|
73 level = h # Metrical weight happens to be equal to its metrical level
|
csong@1
|
74 syncopation += min(aveDif_neighbours(index, h))*potential
|
csong@1
|
75
|
csong@1
|
76 return syncopation
|