Mercurial > hg > syncopation-dataset
view Syncopation models/TOB_ts.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 |
line wrap: on
line source
''' Author: Chunyang Song Institution: Centre for Digital Music, Queen Mary University of London ** Offbeat-ness Model - adopt time-span ** Algorithm: Calculate the representation of timeSpan for each rhythm: 1) Count both onset and metronome as events, and generate new binary digits (24-bit per bar) representing combined events; 2) Among all divisors of 24 - [1,2,3,4,6,8,12,24], Find the minimum divisor N to divide a bar into (24/N) segments, so that all onsets are on the first digit of any segement; Take rhythm 100010001000100010000000 for example, divisor 6 gives 4-digit a segment (1000-1000-1000-1000-1000-0000), all onsets fall on the first digit of segment 1,2,3,4,5; 3) Represent the rhythm sequence in timeSpan format (e.g. the rhythm above becomes 11112) 4) Create N-unit metrical circle. The rest is the same as the implemention version 1: Calculate on-beat positions in the metrical circle: positions can divide the circle equally with divisor(s), 1< divisor < N Define the onset not locating the on-beat positions the off-beat note; Syncopation is the total number of off-beat onsets. ''' from MeterStructure import MeterStructure # To transcribe rhythm into time span representation and then calcualte the length of circle def timeSpan(rhythm, time_sig, bar): ms = MeterStructure(time_sig) beatPos = ms.getBeats(bar) metronome = [0]*len(rhythm) if len(beatPos) !=0: for b in beatPos: metronome[b] = 1 event = [] # To combine onsets and metronome events, 1 - event, 0 - no event for i in range(len(rhythm)): event.append(rhythm[i] or metronome[i]) # logic 'or' is to do combination: 1 or 0 = 1, 1 or 1 = 1, 0 or 0 = 0. # To find out how many digits are big enough to represent the note with the shorted duration. length = 0 divisors = [] # all the divisors of the length of the rhythm in one bar l = len(rhythm)/bar for i in range(1,l): if l%i ==0: divisors.append(i) for d in divisors: sampleStep = (len(rhythm)/bar)/d template = (([1] + [0]*(sampleStep-1) )*d )*bar sampled = [] for i in range(len(event)): sampled.append(event[i] and template[i]) if event == sampled: length = d break return length def metricalCircle(length): circle = [] if length != 0: circle = range(length) else: print 'Invalid length of metrical circle' return circle def onBeatPos(circle): l = len(circle) divisors = [] for i in range(2,l): if l%i ==0: divisors.append(i) onBeat = [] for d in divisors: for pos in circle: if pos%d == 0: onBeat.append(pos) return onBeat def offBeatNess(rhythm, time_sig, category, bar): N = timeSpan(rhythm, time_sig, bar) circle = metricalCircle(N) * bar if len(circle)!=0: # Calculate on-beat positions within one bar onBeat = onBeatPos(metricalCircle(N)) onBeat.sort() # sort the on-beat positions. This list will have duplicate numbers, but it doesn't affect the following algorithm # Calculate how many onsets are off-beat, which represent syncopation syncopation = 0 l = len(rhythm) for i in range(l): if rhythm[i] == 1: # onset detected pos = (float(i)/l)*len(circle) # looking for the metrical position where this note locates if pos >= len(circle)/2: pos = pos- len(circle)/2 if pos in onBeat: continue else: # if the onset is not on-beat, then syncopation increase by 1 syncopation = syncopation + 1 return syncopation else: return -1 # Retrieve the stimuli #f = file('stimuli.txt') f = file('stimuli_34only.txt') #Calculate syncopation for each rhythm pattern while True: line = f.readline().split(';') if len(line) == 1: break else: sti_name = line[0] rhythmString = line[1].split() time_sig = line[2] category = line[3] bar = int(line[4]) rhythm = map(int,rhythmString[0].split(',')) print sti_name, offBeatNess(rhythm, time_sig, category, bar)