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

from BasicFuncs import concatenate, repeat, subdivide, ceiling

terminal_nodes = []		# 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,node_type,metrical_weight):
		self.node_type = node_type
		self.metrical_weight = metrical_weight

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

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

	else:													# Keep subdividing by the subdivisor of the next level
		sub_seq = subdivide(seq, subdivision_seq[level+1])	
		sub_weight_seq = concatenate([metrical_weight],repeat([weight_seq[level+1]],subdivision_seq[level+1]-1))
		for a in range(len(sub_seq)):
			recursive_tree(sub_seq[a], subdivision_seq, weight_seq, sub_weight_seq[a], level+1)

# This function calculate syncoaption score for LHL model. 
def get_syncopation(seq, subdivision_seq, weight_seq, prebar_seq, rhythm_category):
	syncopation = None
	if rhythm_category == 'poly':
		print 'Error: LHL model cannot deal with polyrhythms.'
	else:
		# If there is rhythm in previous bar, process its tree structure
		if prebar_seq != None:		
			recursive_tree(ceiling(prebar_seq), subdivision_seq, weight_seq, weight_seq[0],0)
			
			# Only keep the last note-type node
			while terminal_nodes[-1].node_type != 'N':
				del terminal_nodes[-1]
			del terminal_nodes[0:-1]

		# For the rhythm in the current bar, process its tree structure and store the terminal nodes 
		recursive_tree(ceiling(seq), subdivision_seq, weight_seq, weight_seq[0],0)
		
		# for t in terminal_nodes:
		# 	print '<', t.node_type, t.metrical_weight, '>'

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

		# If no syncopation, the value is -1; otherwise, sum all the local syncopation values stored in NR_pair_syncopation list	
		if len(NR_pair_syncopation) != 0:
			syncopation = sum(NR_pair_syncopation)	
		elif len(terminal_nodes) != 0:
			syncopation = -1

	return syncopation