view Syncopation models/LHL.py @ 20:b959c2acb927

Refactored all models except for KTH, all past testing except for SG.
author csong <csong@eecs.qmul.ac.uk>
date Tue, 07 Apr 2015 19:05:07 +0100
parents 9030967a05f8
children 5de1cb45c145
line wrap: on
line source
'''
Author: Chunyang Song
Institution: Centre for Digital Music, Queen Mary University of London
'''

from basic_functions import concatenate, repeat, subdivide, ceiling, get_rhythm_category

terminalNodes = []		# Global variable, storing all the terminal nodes from recursive tree structure in time order

# Each terminnal node contains two properties: its node type (note or rest) and its metrical weight.
class Node:
	def __init__(self,nodeType,metricalWeight):
		self.nodeType = nodeType
		self.metricalWeight = metricalWeight

# This function will recurse the tree for a binary sequence and return a sequence containing the terminal nodes in time order.
def recursive_tree(binarySequence, subdivisionSequence, weightSequence, metricalWeight, level):
	# If matching to a Note type, add to terminal nodes
	if binarySequence == concatenate([1],repeat([0],len(binarySequence)-1)):	
		terminalNodes.append(Node('N',metricalWeight))

	# If matching to a Rest type, add to terminal nodes
	elif binarySequence == repeat([0],len(binarySequence)):					
		terminalNodes.append(Node('R',metricalWeight))

	# Keep subdividing by the subdivisor of the next level
	else:													
		subBinarySequences = subdivide(binarySequence, subdivisionSequence[level+1])	
		subWeightSequences = concatenate([metricalWeight],repeat([weightSequence[level+1]],subdivisionSequence[level+1]-1))
		for a in range(len(subBinarySequences)):
			recursive_tree(subBinarySequences[a], subdivisionSequence, weightSequence, subWeightSequences[a], level+1)

#!!!! needs fixing
def are_parameters_valid(parameters):
	areValid = True
#	if 'Lmax' not in parameters :

def get_syncopation(bar, parameters = None):
	del terminalNodes[:]
	syncopation = None

	binarySequence = bar.get_binary_sequence()
	subdivisionSequence = bar.get_subdivision_sequence()

	if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly':
		print 'Warning: LHL model detects polyrhythms so returning None.'
	else:
		# If the parameters are not given, use the default settings
		if parameters == None:
			Lmax  = 5
			weightSequence = range(0,-Lmax,-1) # i.e. [0,-1,-2,-3,-4]
		else:
			if are_parameters_valid(parameters):
				Lmax = parameters['Lmax']
				weightSequence = parameters['W']
			else:
				pass
				#raise InvalidParameterError

		# If there is rhythm in previous bar, process its tree structure
		if bar.get_previous_bar() != None:
			prebarBinarySequence = bar.get_previous_bar().get_binary_sequence()
			recursive_tree(ceiling(prebarBinarySequence), subdivisionSequence, weightSequence, weightSequence[0],0)
			
			# Only keep the last note-type node
			while terminalNodes[-1].nodeType != 'N':
				del terminalNodes[-1]
			del terminalNodes[0:-1]

		# For the rhythm in the current bar, process its tree structure and store the terminal nodes 
		recursive_tree(ceiling(binarySequence), subdivisionSequence, weightSequence, weightSequence[0],0)
		
		# for t in terminalNodes:
		# 	print '<', t.nodeType, t.metricalWeight, '>'

		# Search for the NR pairs that contribute to syncopation, 
		# then add the weight-difference to the NRpairSyncopation list
		NRpairSyncopation = []
		for i in range(len(terminalNodes)-1,0,-1):
			if terminalNodes[i].nodeType == 'R':
				for j in range(i-1, -1, -1):
					if (terminalNodes[j].nodeType == 'N') & (terminalNodes[i].metricalWeight >= terminalNodes[j].metricalWeight):
						NRpairSyncopation.append(terminalNodes[i].metricalWeight - terminalNodes[j].metricalWeight)
						break
		#print NRpairSyncopation

		# If there are syncopation, sum all the local syncopation values stored in NRpairSyncopation list	
		if len(NRpairSyncopation) != 0:
			syncopation = sum(NRpairSyncopation)
		# If no syncopation, the value is -1; 	
		elif len(terminalNodes) != 0:
			syncopation = -1

	return syncopation