view Syncopation models/TOB_ts.py @ 2:031e2ccb1fb6

Added rhythm parser and parameter setter
author Chunyang Song <csong@eecs.qmul.ac.uk>
date Fri, 20 Mar 2015 18:28:08 +0000
parents 76ce27beba95
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)