annotate Syncopation models/WNBD.py @ 0:76ce27beba95

Have uploaded the syncopation dataset and audio wavefies.
author Chunyang Song <csong@eecs.qmul.ac.uk>
date Fri, 21 Mar 2014 15:49:46 +0000
parents
children b2da092dc2e0
rev   line source
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)