view Syncopation models/SG.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
line wrap: on
line source
'''
Author: Chunyang Song
Institution: Centre for Digital Music, Queen Mary University of London

** Sioros and Guedes's Model **

Algorithm:

Only applicable to monorhythms.

This version of implementation follows the description in authors' 2011 ISMIR paper and assumes each bar of rhythm is looped.
Therefore each bar is calculated seperatedly in a loop-mode and summed up in the end as the total syncopation. 

'''

from MeterStructure import MeterStructure
from math import pow

def subdivide(sequence, segments_num):
	subSeq = []
	if len(sequence) % segments_num != 0:
		print 'Error: rhythm segment cannot be equally subdivided '
	else:
		n = len(sequence) / segments_num
		start , end = 0, n
		for i in range(segments_num):
			subSeq.append(sequence[start : end])
			start = end
			end = end + n
	
	return subSeq


def sgModel(rhythm, time_sig, category, bar):
	ms = MeterStructure(time_sig)
	mWeights = ms.getLHLWeights(1)
	#print "hierarchy", mWeights
	num_onsets = 0
	h_min = min(mWeights)

	syncAbsolute = 0
	syncNormM = 0
	syncNormE = 0
	
	if 'poly' in category:
		syncAbsolute = -1
	else:
		# segment rhythm into bars
		rhythm_byBar = subdivide (rhythm, bar)

		def measurePerBar(rhythm):
			syncopation = 0
			
			def aveNeighbours(index, h):
				averages = []
				parameter_k = 0.8
				
				def findPre(h_hat):
					pre_index = index - 1
					while(mWeights[pre_index] < h_hat):
						pre_index = (pre_index - 1)%len(mWeights)
					#print "h_hat and pre", h_hat, pre_index
					return pre_index

				def findPost(h_hat):
					post_index = (index + 1)%len(mWeights)
					while(mWeights[post_index] < h_hat):
						post_index = (post_index + 1)%len(mWeights)
					#print "h_hat and post", h_hat, post_index
					return post_index
				
				def dif(index1,index2):
					parameter_beta = 0.5
					pos1 = int(float(index1)/len(mWeights)*48)
					pos2 = int(float(index2)/len(mWeights)*48)
					dif_v = rhythm[pos1]-rhythm[pos2]
					dif_h = abs(mWeights[index1]-mWeights[index2])
					dif = dif_v*(parameter_beta*dif_h/4+1-parameter_beta)
					#print 'dif', dif
					return dif

				for h_hat in range(h_min,h+1):
					ave = ( parameter_k*dif(index,findPre(h_hat))+dif(index,findPost(h_hat)) )/(1+parameter_k)
					#print 'ave', ave
					averages.append(ave)

				return averages

			for pos in range(len(rhythm)):
				if rhythm[pos] != 0: # Onset detected
					#num_onsets += 1
					i = int((pos/48.0*len(mWeights)))
					h = mWeights[i]
					potential = 1 - pow(0.5,(-h))
					#print "intermediate", min(aveNeighbours(i,h))
					syncopation += min(aveNeighbours(i, h))*potential

			return syncopation

		for r in rhythm_byBar:
			syncAbsolute = syncAbsolute + measurePerBar(r)
		#syncAbsolute /= 2.0

	return syncAbsolute

# Retrieve the stimuli
#f = file('stimuli.txt')
#f = file('stimuli_34only.txt')
f = file('clave.txt')


#Calculate syncopation for each rhythm pattern
#while True:
for i in range(1):
	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, sgModel(rhythm, time_sig, category, bar)