Mercurial > hg > syncopation-dataset
comparison Syncopation models/WNBD.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 ** Weighted Note-to-Beat Distance Model (WNBD) ** | 5 ''' |
6 from basic_functions import repeat, get_note_indices | |
6 | 7 |
7 Algorithm: | 8 def cumu_multiply(numbers): |
9 product = 1 | |
10 for n in numbers: | |
11 product = product*n | |
12 return product | |
8 | 13 |
9 Calculate the distance (d) of onset to the nearest strong beat; | 14 def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq): |
10 Calculate the WNBD measure (w) of each onset; | 15 syncopation = None |
11 Syncopation is the sum of WNBD measures divided by the number of onsets. | 16 |
17 num_beats = cumu_multiply(subdivision_seq[0:strong_beat_level+1]) # num_beats is the number of strong beats | |
18 if len(seq)%num_beats != 0: | |
19 print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision_seq.' | |
20 else: | |
21 # Find the indices of all the strong-beats | |
22 beat_indices = [] | |
23 beat_interval = len(seq)/num_beats | |
24 for i in range(num_beats+1): | |
25 beat_indices.append(i*beat_interval) | |
26 if postbar_seq != None: # if there is a postbar_seq, add another two beats index for later calculation | |
27 beat_indices += [len(seq)+beat_interval, len(seq)+ 2* beat_interval] | |
12 | 28 |
13 ''' | 29 note_indices = get_note_indices(seq) # all the notes |
14 | 30 |
15 from MeterStructure import MeterStructure | 31 # Calculate the WNBD measure for each note |
32 def measure_pernote(note_index, nextnote_index): | |
33 # Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) | |
34 j = 0 | |
35 while note_index < beat_indices[j] or note_index >= beat_indices[j+1]: | |
36 j = j + 1 | |
37 | |
38 # The distance of note to nearest beat normalised by the beat interval | |
39 distance_nearest_beat = min(abs(note_index - beat_indices[j]), abs(note_index - beat_indices[j+1]))/float(beat_interval) | |
16 | 40 |
17 def WNBD(rhythm, time_sig, category, bar): | 41 # if this note is on-beat |
42 if distance_nearest_beat == 0: | |
43 measure = 0 | |
44 # or if this note is held on past the following beat, but ends on or before the later beat | |
45 elif beat_indices[j+1] < nextnote_index <= beat_indices[j+2]: | |
46 measure = float(2)/distance_nearest_beat | |
47 else: | |
48 measure = float(1)/distance_nearest_beat | |
18 | 49 |
19 ms = MeterStructure(time_sig) | 50 return measure |
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 | |
21 if len(beat)!=0: | |
22 unit = beat[1]-beat[0] # how many digits to represent the length of one beat | |
23 | 51 |
24 onsetPos = [] # The onset positions | 52 total = 0 |
25 d = [] # The distances of each onset to its nearest strong beat | 53 for i in range(len(note_indices)): |
26 beatToLeft = [] # The beat indexes of the beats that are to the left of or coincide with each onset | 54 if i == len(note_indices)-1:# if this is the last note, end_time is the index of the following note in the next bar |
27 wnbdMeasures = [] # the un-normalized measures of wnbd | 55 if postbar_seq != None and postbar_seq != repeat([0],len(postbar_seq)): |
28 | 56 nextnote_index = get_note_indices(postbar_seq)[0]+len(seq) |
29 # Calculate the distance of each onset to the nearest strong beat | 57 else: # or if the next bar is none or full rest, end_time is the end of this sequence. |
30 l = len(rhythm) | 58 nextnote_index = len(seq) |
31 for i in range(l): | |
32 if rhythm[i] == 1: # onset detected | |
33 onsetPos.append(i) | |
34 | |
35 # find its distance to the nearest strong beat | |
36 for j in range(len(beat)-1): | |
37 if beat[j]<= i < beat[j+1]: | |
38 d1 = abs(i-beat[j]) | |
39 d2 = abs(i-beat[j+1]) | |
40 d.append( min(d1,d2)/float(unit) ) # Normalize the distance to beat-level | |
41 beatToLeft.append(j) | |
42 else: | |
43 continue | |
44 | |
45 #Calculate the WNBD measure of each onset | |
46 n = len(onsetPos) | |
47 for i in range(n): | |
48 if d[i] ==0: # If on-beat, measure is 0 | |
49 wnbdMeasures.append(0) | |
50 else: | 59 else: |
51 if i == n-1: # The last note, then | 60 nextnote_index = note_indices[i+1] |
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 | 61 total += measure_pernote(note_indices[i],nextnote_index) |
53 wnbdMeasures.append(2.0/d[i]) # measure is 2/d, else 1/d | |
54 else: | |
55 wnbdMeasures.append(1.0/d[i]) | |
56 | 62 |
57 else: # if its not the last note | 63 syncopation = float(total) / len(note_indices) |
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 | |
59 wnbdMeasures.append(1.0/d[i]) | |
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 | |
61 wnbdMeasures.append(2.0/d[i]) | |
62 else: # if the note is more than 2-beat distance away from the next note | |
63 wnbdMeasures.append(1.0/d[i]) | |
64 | 64 |
65 syncopation = sum(wnbdMeasures)/float(n) | 65 return syncopation |
66 return syncopation | |
67 | |
68 else: | |
69 return -1 | |
70 | |
71 | |
72 # Retrieve the stimuli | |
73 f = file('stimuli.txt') | |
74 #f = file('stimuli_34only.txt') | |
75 | |
76 #Calculate syncopation for each rhythm pattern | |
77 while True: | |
78 line = f.readline().split(';') | |
79 if len(line) == 1: | |
80 break | |
81 else: | |
82 sti_name = line[0] | |
83 rhythmString = line[1].split() | |
84 time_sig = line[2] | |
85 category = line[3] | |
86 bar = int(line[4]) | |
87 | |
88 rhythm = map(int,rhythmString[0].split(',')) | |
89 | |
90 print sti_name, WNBD(rhythm, time_sig, category, bar) |