changeset 28:5de1cb45c145

Parameters setting implemented.
author csong <csong@eecs.qmul.ac.uk>
date Sun, 12 Apr 2015 22:34:35 +0100
parents ed29ed80635c
children 7a1730bbf15a
files Syncopation models/LHL.py Syncopation models/SG.py Syncopation models/TMC.py Syncopation models/basic_functions.py Syncopation models/parameter_setter.py Syncopation models/syncopation.py
diffstat 6 files changed, 177 insertions(+), 174 deletions(-) [+]
line wrap: on
line diff
--- a/Syncopation models/LHL.py	Sun Apr 12 15:55:12 2015 +0100
+++ b/Syncopation models/LHL.py	Sun Apr 12 22:34:35 2015 +0100
@@ -4,6 +4,7 @@
 '''
 
 from basic_functions import concatenate, repeat, subdivide, ceiling, get_rhythm_category
+from parameter_setter import are_parameters_valid
 
 terminalNodes = []		# Global variable, storing all the terminal nodes from recursive tree structure in time order
 
@@ -30,10 +31,6 @@
 		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[:]
@@ -42,54 +39,55 @@
 	binarySequence = bar.get_binary_sequence()
 	subdivisionSequence = bar.get_subdivision_sequence()
 
+	# LHL can only measure monorhythms
 	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]
+		# set defaults
+		Lmax = 5
+		weightSequence = range(0,-Lmax,-1)
+		# if parameters are specified by users, check their validities and update parameters if valid		
+		if parameters!= None:
+			if 'Lmax' in parameters:
+				Lmax = parameters['Lmax']				
+			if 'W' in parameters:
+				weightSequence = parameters['W']
+
+		if not are_parameters_valid(Lmax, weightSequence, subdivisionSequence):
+			print 'Error: the given parameters are not valid.'
 		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]
 
-		# 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)
+			# 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)
 			
-			# Only keep the last note-type node
-			while terminalNodes[-1].nodeType != 'N':
-				del terminalNodes[-1]
-			del terminalNodes[0:-1]
+			# for t in terminalNodes:
+			# 	print '<', t.nodeType, t.metricalWeight, '>'
 
-		# 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
 
-		# 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
+			# 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
 
--- a/Syncopation models/SG.py	Sun Apr 12 15:55:12 2015 +0100
+++ b/Syncopation models/SG.py	Sun Apr 12 22:34:35 2015 +0100
@@ -5,9 +5,7 @@
 '''
 
 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()
@@ -18,68 +16,70 @@
 	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]
+		# set the defaults
+		Lmax  = 5
+		weightSequence = range(Lmax+1) # i.e. [0,1,2,3,4,5]
+		if parameters!= None:
+			if 'Lmax' in parameters:
+				Lmax = parameters['Lmax']				
+			if 'W' in parameters:
+				weightSequence = parameters['W']
+
+		if not are_parameters_valid(Lmax, weightSequence, subdivisionSequence):
+			print 'Error: the given parameters are not valid.'
 		else:
-			if are_parameters_valid(parameters):
-				Lmax = parameters['Lmax']
-				weightSequence = parameters['W']
-			else:
-				pass
-				#raise InvalidParameterError
+			# 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))
 
-		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):
 
-		# 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
 
-			averages = []
-			parameterGarma = 0.8
+				# 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
+
+			# if the upsampling was successfully done
+			if velocitySequence != None:
+				syncopation = 0			
+				# 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
 			
-			# 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
--- a/Syncopation models/TMC.py	Sun Apr 12 15:55:12 2015 +0100
+++ b/Syncopation models/TMC.py	Sun Apr 12 22:34:35 2015 +0100
@@ -5,6 +5,7 @@
 '''
 
 from basic_functions import get_H, ceiling, velocity_sequence_to_min_timespan, get_rhythm_category
+from parameter_setter import are_parameters_valid
 
 # The get_metricity function calculates the metricity for a binary sequence with given sequence of metrical weights in a certain metrical level.
 def get_metricity(binarySequence, H):
@@ -55,30 +56,31 @@
 	if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly':
 		print 'Warning: TMC model detects polyrhythms so returning None.'
 	else:
-		binarySequence = velocity_sequence_to_min_timespan(binarySequence)	# converting to the minimum time-span format
+		
+		# set the defaults
+		Lmax  = 5
+		weightSequence = range(Lmax+1,0,-1) # i.e. [6,5,4,3,2,1]
+		
+		if parameters!= None:
+			if 'Lmax' in parameters:
+				Lmax = parameters['Lmax']				
+			if 'W' in parameters:
+				weightSequence = parameters['W']
 
-		# If the parameters are not given, use the default settings
-		if parameters == None:
-			Lmax  = 5
-			weightSequence = range(Lmax+1,0,-1) # i.e. [6,5,4,3,2,1]
+		if not are_parameters_valid(Lmax, weightSequence, subdivisionSequence):
+			print 'Error: the given parameters are not valid.'
 		else:
-			if are_parameters_valid(parameters):
-				Lmax = parameters['Lmax']
-				weightSequence = parameters['W']
-			else:
-				pass
-				#raise InvalidParameterError
+			binarySequence = velocity_sequence_to_min_timespan(binarySequence)	# converting to the minimum time-span format
+			L = find_L(binarySequence, Lmax, weightSequence, subdivisionSequence) 
+			if L != None:
+				#? generate the metrical weights of the lowest level, 
+				#? using the last matching_level number of elements in the weightSequence, to make sure the last element is 1
+				H = get_H (weightSequence[-(L+1):], subdivisionSequence, L)
+				
+				metricity = get_metricity(binarySequence, H)	# converting to binary sequence then calculate metricity
+				maxMetricity = get_max_metricity(binarySequence, H)
 
-		L = find_L(binarySequence, Lmax, weightSequence, subdivisionSequence) 
-		if L != None:
-			#? generate the metrical weights of the lowest level, 
-			#? using the last matching_level number of elements in the weightSequence, to make sure the last element is 1
-			H = get_H (weightSequence[-(L+1):], subdivisionSequence, L)
-			
-			metricity = get_metricity(binarySequence, H)	# converting to binary sequence then calculate metricity
-			maxMetricity = get_max_metricity(binarySequence, H)
-
-			syncopation = maxMetricity - metricity
+				syncopation = maxMetricity - metricity
 
 	return syncopation
 
--- a/Syncopation models/basic_functions.py	Sun Apr 12 15:55:12 2015 +0100
+++ b/Syncopation models/basic_functions.py	Sun Apr 12 22:34:35 2015 +0100
@@ -81,10 +81,11 @@
 
 # upsample a velocity sequence to certain length, e.g. [1,1] to [1,0,0,0,1,0,0,0]
 def upsample_velocity_sequence(velocitySequence, length):
-	upsampledVelocitySequence = [0]*length
+	upsampledVelocitySequence = None
 	if length%len(velocitySequence) != 0:
 		print 'Error: the velocity sequence can only be upsampled to the interger times of its length.'
 	else:
+		upsampledVelocitySequence = [0]*length
 		scalingFactor = length/len(velocitySequence)
 		for index in range(len(velocitySequence)):
 			upsampledVelocitySequence[index*scalingFactor] = velocitySequence[index]
@@ -214,35 +215,3 @@
 	
 # 	return subdivision_seq
 
-
- # The split_by_bar function seperates the score representation of rhythm by bar lines, 
- # resulting in a list representingbar-by-bar rhythm sequence,
- # e.g. rhythm = ['|',[ts1,td1,v1], [ts2,td2,v2], '|',[ts3,td3,v3],'|'...]
- # rhythm_bybar = [ [ [ts1,td1,v1], [ts2,td2,v2] ], [ [ts3,td3,v3] ], [...]]
-# def split_by_bar(rhythm):
-# 	rhythm_bybar = []
-# 	bar_index = []
-# 	for index in range(len(rhythm)):
-# 		if rhythm[index] == '|':
-
-# 	return rhythm_bybar
-
-# def yseq_to_vseq(yseq):
-# 	vseq = []
-
-# 	return vseq
-
-
-# # testing
-# print find_prime_factors(10)
-# print find_prime_factors(2)
-# print find_prime_factors(12)
-
-
-# print is_prime(1)       # False
-# print is_prime(2)       # True
-# print is_prime(3)       # True
-# print is_prime(29)      # True
-# print is_prime(345)     # False
-# print is_prime(999979)  # True
-# print is_prime(999981)  # False
\ No newline at end of file
--- a/Syncopation models/parameter_setter.py	Sun Apr 12 15:55:12 2015 +0100
+++ b/Syncopation models/parameter_setter.py	Sun Apr 12 22:34:35 2015 +0100
@@ -3,10 +3,12 @@
 Institution: Centre for Digital Music, Queen Mary University of London
 '''
 
-# Set the parameters: subdivision_seq, weight_seq, L_max, strong_beat_level
+# Set the parameters: time-signature, subdivision-sequence, strong-beat-level; Lmax; weight-sequence
+# Important condition: Lmax needs to be no less than the length of subdivision-sequence and the length of weight-sequence
+
 
 # {'key': time-signature} :  
-# {'value': [subdivision_seq, theoretical beat-level represented by index in the subdivision_seq list]}
+# {'value': [subdivision-sequence, theoretical beat-level represented by index in the subdivision-sequence list]}
 timeSignatureBase = {
 	'2/2': [[1,2,2,2,2,2],1],
 	'3/2': [[1,3,2,2,2,2],1],
@@ -23,6 +25,7 @@
 	'12/8':[[1,2,2,3,2,2],2],	
 }
 
+
 def add_time_signature(timeSignature, subdivisionSequence, beatLevel):
 	if is_time_signature_valid(timeSignature,subdivisionSequence,beatLevel):
 		if timeSignature in timesigBase:
@@ -69,16 +72,44 @@
 	for timeSignature, settings in data.items():
 		print timeSignature, settings
 
-# def get_subdivision_seq(timeSignature):
-# 	if timeSignature in readTimesig():
-# 		return timesigBase[timesig][0]
-# 	else:
-# 		print 'Error: the subdivision sequence for this time-signature is not defined.'
-# 		return None
 
-# def get_beat_level(timesig):
-# 	if timesig in readTimesig():
-# 		return timesigBase[timesig][1]
-# 	else:
-# 		print 'Error: the subdivision sequence for this time-signature is not defined.'
-# 		return None
+def are_parameters_valid(Lmax, weightSequence, subdivisionSequence):
+
+	# is_Lmax_valid() checks:
+	# 1. if Lmax is a non-negative integer
+	# 2. if Lmax is higher than the length of weightSequence and subdivisionSequence 
+	def is_Lmax_valid():
+		isValid = False
+		if isinstance(Lmax,int) and Lmax > 0:
+			if Lmax <= len(subdivisionSequence):
+				if Lmax <= len(weightSequence):
+					isValid = True
+				else:
+					print """Error: Lmax exceeds the length of subdivision-sequence. 
+					Either reduce Lmax, or extend subdivision-sequence through updating time-signature (refer to update_time_signature function). """
+			else:
+				print """Error: Lmax exceeds the length of weight-sequence. Either reduce Lmax, or provide a new weight-sequence
+				whose length is greater or equal to Lmax."""
+		else:	
+			print 'Error: Lmax needs to be a positive integer.'
+		return isValid
+
+	# is_weight_sequence_valid() checks:
+	# 1. weightSequence is a list of numbers
+	# 2. the length of weightSequence is no less than Lmax
+	def is_weight_sequence_valid():
+		isValid = False
+		if isinstance(weightSequence,list) and weightSequence == [i for i in weightSequence if isinstance(i,int)]:
+			if len(weightSequence) >= Lmax:
+				isValid = True
+			else:
+				print 'Error: the length of weight-sequence needs to be greater or equal to Lmax.'
+		else:
+			print 'Error: the weight-sequence needs to be a list of integers.'
+		return isValid
+
+
+	if is_Lmax_valid() and is_weight_sequence_valid():
+		return True
+	else:
+		return False
--- a/Syncopation models/syncopation.py	Sun Apr 12 15:55:12 2015 +0100
+++ b/Syncopation models/syncopation.py	Sun Apr 12 22:34:35 2015 +0100
@@ -7,9 +7,12 @@
 def sync_perbar_permodel (model, bar, parameters):
 	return model.get_syncopation(bar, parameters)
 
- def syncopation_barlist_permodel(model, barlist, parameters):
+ def syncopation_barlist_permodel(model, source, parameters):
  	total = 0
  	numberOfNotes = 0
+
+ 	
+
  	for bar in barlist:
  		if sync_perbar_permodel(model, bar, parameters) != None:
  			total += sync_perbar_permodel(model, bar, parameters)