comparison Syncopation models/SG.py @ 1:b2da092dc2e0

The consolidated syncopation software. Have finished individual model and basic functions. Need to revise the coding in main.py, and add rhythm-input interface.
author Chunyang Song <csong@eecs.qmul.ac.uk>
date Sun, 05 Oct 2014 21:52:41 +0100
parents 76ce27beba95
children 031e2ccb1fb6
comparison
equal deleted inserted replaced
0:76ce27beba95 1:b2da092dc2e0
1 ''' 1 '''
2 Author: Chunyang Song 2 Author: Chunyang Song
3 Institution: Centre for Digital Music, Queen Mary University of London 3 Institution: Centre for Digital Music, Queen Mary University of London
4 4
5 ** Sioros and Guedes's Model **
6
7 Algorithm:
8
9 Only applicable to monorhythms.
10
11 This version of implementation follows the description in authors' 2011 ISMIR paper and assumes each bar of rhythm is looped.
12 Therefore each bar is calculated seperatedly in a loop-mode and summed up in the end as the total syncopation.
13
14 ''' 5 '''
15 6
16 from MeterStructure import MeterStructure 7 from basic_functions import get_H, get_min_timeSpan
17 from math import pow
18 8
19 def subdivide(sequence, segments_num): 9 def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
20 subSeq = [] 10 syncopation = None
21 if len(sequence) % segments_num != 0: 11 if rhythm_category == 'poly':
22 print 'Error: rhythm segment cannot be equally subdivided ' 12 print 'Error: SG model cannot deal with polyrhythms.'
23 else: 13 else:
24 n = len(sequence) / segments_num 14
25 start , end = 0, n 15 seq = get_min_timeSpan(seq) # converting to the minimum time-span format
26 for i in range(segments_num): 16
27 subSeq.append(sequence[start : end]) 17 # checking whether the given L_max is enough to analyse the given sequence, if not, request a bigger L_max
28 start = end 18 new_L_max = True
29 end = end + n 19 matching_level = L_max
30 20 while matching_level >= 0:
31 return subSeq 21 if len(get_H(weight_seq,subdivision_seq, matching_level)) == len(seq):
22 new_L_max = False
23 break
24 else:
25 matching_level = matching_level - 1
32 26
27 if new_L_max == True:
28 print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.'
33 29
34 def sgModel(rhythm, time_sig, category, bar): 30 else:
35 ms = MeterStructure(time_sig) 31 syncopation = 0
36 mWeights = ms.getLHLWeights(1) 32 # generate the metrical weights of the lowest level
37 #print "hierarchy", mWeights 33 H = get_H(weight_seq,subdivision_seq, matching_level)
38 num_onsets = 0
39 h_min = min(mWeights)
40 34
41 syncAbsolute = 0 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
42 syncNormM = 0 36 def aveDif_neighbours(index, level):
43 syncNormE = 0
44
45 if 'poly' in category:
46 syncAbsolute = -1
47 else:
48 # segment rhythm into bars
49 rhythm_byBar = subdivide (rhythm, bar)
50
51 def measurePerBar(rhythm):
52 syncopation = 0
53
54 def aveNeighbours(index, h):
55 averages = [] 37 averages = []
56 parameter_k = 0.8 38 parameter_garma = 0.8
57 39
58 def findPre(h_hat): 40 # The findPre function is to calculate the index of the previous neighbour at a certain metrical level.
59 pre_index = index - 1 41 def findPre(index, level):
60 while(mWeights[pre_index] < h_hat): 42 pre_index = (index - 1)%len(H)
61 pre_index = (pre_index - 1)%len(mWeights) 43 while(H[pre_index] > level):
62 #print "h_hat and pre", h_hat, pre_index 44 pre_index = (pre_index - 1)%len(H)
63 return pre_index 45 return pre_index
64 46
65 def findPost(h_hat): 47 # The findPost function is to calculate the index of the next neighbour at a certain metrical level.
66 post_index = (index + 1)%len(mWeights) 48 def findPost(index, level):
67 while(mWeights[post_index] < h_hat): 49 post_index = (index + 1)%len(H)
68 post_index = (post_index + 1)%len(mWeights) 50 while(H[post_index] > level):
69 #print "h_hat and post", h_hat, post_index 51 post_index = (post_index + 1)%len(H)
70 return post_index 52 return post_index
71 53
54 # The dif function is to calculate a difference level factor between two notes (at note position index1 and index 2) in velocity sequence
72 def dif(index1,index2): 55 def dif(index1,index2):
73 parameter_beta = 0.5 56 parameter_beta = 0.5
74 pos1 = int(float(index1)/len(mWeights)*48) 57 dif_v = seq[index1]-seq[index2]
75 pos2 = int(float(index2)/len(mWeights)*48) 58 dif_h = abs(H[index1]-H[index2])
76 dif_v = rhythm[pos1]-rhythm[pos2]
77 dif_h = abs(mWeights[index1]-mWeights[index2])
78 dif = dif_v*(parameter_beta*dif_h/4+1-parameter_beta) 59 dif = dif_v*(parameter_beta*dif_h/4+1-parameter_beta)
79 #print 'dif', dif
80 return dif 60 return dif
81 61
82 for h_hat in range(h_min,h+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
83 ave = ( parameter_k*dif(index,findPre(h_hat))+dif(index,findPost(h_hat)) )/(1+parameter_k) 63 for l in range(level, max(H)+1):
84 #print 'ave', ave 64 ave = ( parameter_garma*dif(index,findPre(index,l))+dif(index,findPost(index,l)) )/(1+parameter_garma)
85 averages.append(ave) 65 averages.append(ave)
86
87 return averages 66 return averages
88 67
89 for pos in range(len(rhythm)): 68 # Calculate the syncopation value for each note
90 if rhythm[pos] != 0: # Onset detected 69 for index in range(len(seq)):
91 #num_onsets += 1 70 if seq[index] != 0: # Onset detected
92 i = int((pos/48.0*len(mWeights))) 71 h = H[index]
93 h = mWeights[i] 72 potential = 1 - pow(0.5,h) # Syncopation potential according to its metrical level, which is equal to the metrical weight
94 potential = 1 - pow(0.5,(-h)) 73 level = h # Metrical weight happens to be equal to its metrical level
95 #print "intermediate", min(aveNeighbours(i,h)) 74 syncopation += min(aveDif_neighbours(index, h))*potential
96 syncopation += min(aveNeighbours(i, h))*potential 75
97 76 return syncopation
98 return syncopation
99
100 for r in rhythm_byBar:
101 syncAbsolute = syncAbsolute + measurePerBar(r)
102 #syncAbsolute /= 2.0
103
104 return syncAbsolute
105
106 # Retrieve the stimuli
107 #f = file('stimuli.txt')
108 #f = file('stimuli_34only.txt')
109 f = file('clave.txt')
110
111
112 #Calculate syncopation for each rhythm pattern
113 #while True:
114 for i in range(1):
115 line = f.readline().split(';')
116 if len(line) == 1:
117 break
118 else:
119 sti_name = line[0]
120 rhythmString = line[1].split()
121 time_sig = line[2]
122 category = line[3]
123 bar = int(line[4])
124
125 rhythm = map(int,rhythmString[0].split(','))
126 print sti_name, sgModel(rhythm, time_sig, category, bar)