Mercurial > hg > syncopation-dataset
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) |