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 ** Weighted Note-to-Beat Distance Model (WNBD) **
|
csong@0
|
6
|
csong@0
|
7 Algorithm:
|
csong@0
|
8
|
csong@0
|
9 Calculate the distance (d) of onset to the nearest strong beat;
|
csong@0
|
10 Calculate the WNBD measure (w) of each onset;
|
csong@0
|
11 Syncopation is the sum of WNBD measures divided by the number of onsets.
|
csong@0
|
12
|
csong@0
|
13 '''
|
csong@0
|
14
|
csong@0
|
15 from MeterStructure import MeterStructure
|
csong@0
|
16
|
csong@0
|
17 def WNBD(rhythm, time_sig, category, bar):
|
csong@0
|
18
|
csong@0
|
19 ms = MeterStructure(time_sig)
|
csong@0
|
20 beat = ms.getBeats(bar) + [len(rhythm)] # the "beat" array include all "strong-beat" positions across all bars, and the downbeat position of the following bar
|
csong@0
|
21 if len(beat)!=0:
|
csong@0
|
22 unit = beat[1]-beat[0] # how many digits to represent the length of one beat
|
csong@0
|
23
|
csong@0
|
24 onsetPos = [] # The onset positions
|
csong@0
|
25 d = [] # The distances of each onset to its nearest strong beat
|
csong@0
|
26 beatToLeft = [] # The beat indexes of the beats that are to the left of or coincide with each onset
|
csong@0
|
27 wnbdMeasures = [] # the un-normalized measures of wnbd
|
csong@0
|
28
|
csong@0
|
29 # Calculate the distance of each onset to the nearest strong beat
|
csong@0
|
30 l = len(rhythm)
|
csong@0
|
31 for i in range(l):
|
csong@0
|
32 if rhythm[i] == 1: # onset detected
|
csong@0
|
33 onsetPos.append(i)
|
csong@0
|
34
|
csong@0
|
35 # find its distance to the nearest strong beat
|
csong@0
|
36 for j in range(len(beat)-1):
|
csong@0
|
37 if beat[j]<= i < beat[j+1]:
|
csong@0
|
38 d1 = abs(i-beat[j])
|
csong@0
|
39 d2 = abs(i-beat[j+1])
|
csong@0
|
40 d.append( min(d1,d2)/float(unit) ) # Normalize the distance to beat-level
|
csong@0
|
41 beatToLeft.append(j)
|
csong@0
|
42 else:
|
csong@0
|
43 continue
|
csong@0
|
44
|
csong@0
|
45 #Calculate the WNBD measure of each onset
|
csong@0
|
46 n = len(onsetPos)
|
csong@0
|
47 for i in range(n):
|
csong@0
|
48 if d[i] ==0: # If on-beat, measure is 0
|
csong@0
|
49 wnbdMeasures.append(0)
|
csong@0
|
50 else:
|
csong@0
|
51 if i == n-1: # The last note, then
|
csong@0
|
52 if beatToLeft[i] == beat[-3]/unit: # if the last note has more than 1- but less than 2-beat distance to the next bar
|
csong@0
|
53 wnbdMeasures.append(2.0/d[i]) # measure is 2/d, else 1/d
|
csong@0
|
54 else:
|
csong@0
|
55 wnbdMeasures.append(1.0/d[i])
|
csong@0
|
56
|
csong@0
|
57 else: # if its not the last note
|
csong@0
|
58 if beatToLeft[i+1] == beatToLeft[i] or (beatToLeft[i+1]-beatToLeft[i]==1 and d[i+1]==0): # if this note is no more than 1-beat distance away from the next note
|
csong@0
|
59 wnbdMeasures.append(1.0/d[i])
|
csong@0
|
60 elif beatToLeft[i+1] - beatToLeft[i] == 1 or (beatToLeft[i+1]-beatToLeft[i]==2 and d[i+1]==0): # if this note is no more than 2-beat distance away from the next
|
csong@0
|
61 wnbdMeasures.append(2.0/d[i])
|
csong@0
|
62 else: # if the note is more than 2-beat distance away from the next note
|
csong@0
|
63 wnbdMeasures.append(1.0/d[i])
|
csong@0
|
64
|
csong@0
|
65 syncopation = sum(wnbdMeasures)/float(n)
|
csong@0
|
66 return syncopation
|
csong@0
|
67
|
csong@0
|
68 else:
|
csong@0
|
69 return -1
|
csong@0
|
70
|
csong@0
|
71
|
csong@0
|
72 # Retrieve the stimuli
|
csong@0
|
73 f = file('stimuli.txt')
|
csong@0
|
74 #f = file('stimuli_34only.txt')
|
csong@0
|
75
|
csong@0
|
76 #Calculate syncopation for each rhythm pattern
|
csong@0
|
77 while True:
|
csong@0
|
78 line = f.readline().split(';')
|
csong@0
|
79 if len(line) == 1:
|
csong@0
|
80 break
|
csong@0
|
81 else:
|
csong@0
|
82 sti_name = line[0]
|
csong@0
|
83 rhythmString = line[1].split()
|
csong@0
|
84 time_sig = line[2]
|
csong@0
|
85 category = line[3]
|
csong@0
|
86 bar = int(line[4])
|
csong@0
|
87
|
csong@0
|
88 rhythm = map(int,rhythmString[0].split(','))
|
csong@0
|
89
|
csong@0
|
90 print sti_name, WNBD(rhythm, time_sig, category, bar)
|