view Syncopation models/SG.py @ 26:d9d22e6f396d

fixed SG!
author csong <csong@eecs.qmul.ac.uk>
date Sun, 12 Apr 2015 15:53:58 +0100
parents df1e7c378ee0
children 5de1cb45c145
line wrap: on
line source
'''
Author: Chunyang Song
Institution: Centre for Digital Music, Queen Mary University of London

'''

from basic_functions import get_H, velocity_sequence_to_min_timespan, get_rhythm_category, upsample_velocity_sequence
from TMC import find_L

#def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
def get_syncopation(bar, parameters = None):
	syncopation = None
	velocitySequence = bar.get_velocity_sequence()
	subdivisionSequence = bar.get_subdivision_sequence()

	if get_rhythm_category(velocitySequence, subdivisionSequence) == 'poly':
		print 'Warning: SG model detects polyrhythms so returning None.'
	else:
		#velocitySequence = velocity_sequence_to_min_timespan(velocitySequence)	# converting to the minimum time-span format

		# If the parameters are not given, use the default settings
		if parameters == None:
			Lmax  = 5
			weightSequence = range(Lmax+1) # i.e. [0,1,2,3,4,5]
		else:
			if are_parameters_valid(parameters):
				Lmax = parameters['Lmax']
				weightSequence = parameters['W']
			else:
				pass
				#raise InvalidParameterError

		syncopation = 0
		# generate the metrical weights of level Lmax, and upsample(stretch) the velocity sequence to match the length of H
		H = get_H(weightSequence,subdivisionSequence, Lmax)
		velocitySequence = upsample_velocity_sequence(velocitySequence, len(H))

		# The ave_dif_neighbours function calculates the (weighted) average of the difference between the note at a certain index and its neighbours in a certain metrical level
		def ave_dif_neighbours(index, level):

			averages = []
			parameterGarma = 0.8
			
			# The findPre function is to calculate the index of the previous neighbour at a certain metrical level.
			def find_pre(index, level):
				preIndex = (index - 1)%len(H)	# using % is to restrict the index varies within range(0, len(H))
				while(H[preIndex] > level):
					preIndex = (preIndex - 1)%len(H)
				#print 'preIndex', preIndex
				return preIndex

			# The findPost function is to calculate the index of the next neighbour at a certain metrical level.
			def find_post(index, level):
				postIndex = (index + 1)%len(H)
				while(H[postIndex] > level):
					postIndex = (postIndex + 1)%len(H)
				#print 'postIndex', postIndex
				return postIndex
			
			# The dif function is to calculate a difference level factor between two notes (at note position index1 and index 2) in velocity sequence
			def dif(index1,index2):
				parameterBeta = 0.5
				dif_v = velocitySequence[index1]-velocitySequence[index2]
				dif_h = abs(H[index1]-H[index2])
				dif = dif_v*(parameterBeta*dif_h/4+1-parameterBeta)
				#print 'dif', dif
				return dif

			# From the highest to the lowest metrical levels where the current note resides, calculate the difference between the note and its neighbours at that level
			for l in range(level, max(H)+1):
				ave = (parameterGarma*dif(index,find_pre(index,l))+dif(index,find_post(index,l)) )/(1+parameterGarma)
				averages.append(ave)
			#print 'averages', averages
			return averages

		# Calculate the syncopation value for each note
		for index in range(len(velocitySequence)):
			if velocitySequence[index] != 0: # Onset detected
				h = H[index] 
				# Syncopation potential according to its metrical level, which is equal to the metrical weight
				potential = 1 - pow(0.5,h)
				level = h 		# Metrical weight is equal to its metrical level
				syncopation += min(ave_dif_neighbours(index, level))*potential
		
	return syncopation