changeset 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 b6daddeefda9
files Syncopation models/KTH.py Syncopation models/LHL.py Syncopation models/PRS.py Syncopation models/SG.py Syncopation models/TMC.py Syncopation models/TOB.py Syncopation models/TimeSignature.pkl Syncopation models/WNBD.py Syncopation models/basic_functions.py Syncopation models/music_objects.py Syncopation models/parameter_setter.py Syncopation models/rhythm_parser.py
diffstat 12 files changed, 301 insertions(+), 196 deletions(-) [+]
line wrap: on
line diff
--- a/Syncopation models/KTH.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/KTH.py	Tue Apr 07 19:05:07 2015 +0100
@@ -5,10 +5,10 @@
 '''
 ## Problems! Note indices, post bar
 
-from BasicFuncs import get_min_timeSpan, get_note_indices, repeat
+from basic_functions import get_min_timeSpan, get_note_indices, repeat
 
 # To find the nearest power of 2 equal to or less than the given number
-def roundDownPower2(number):
+def round_down_power_2(number):
 	i = 0
 	if number > 0:
 		while pow(2,i) > number or number >= pow(2,i+1):
@@ -20,39 +20,40 @@
 	return power2
 
 # To find the nearest power of 2 equal to or more than the given number
-def roundUpPower2(number):
+def round_up_power_2(number):
 	i = 0
 	while pow(2,i) < number:
 		i = i + 1
 	return pow(2,i)
 
 # To examine whether start_time is 'off-beat'
-def start(start_time, c_n):
+def start(startTime, c_n):
 	s = 0
-	if start_time % c_n != 0:
+	if startTime % c_n != 0:
 		s = 2
 	return s
 
 # To examine whether end_time is 'off-beat'
-def end(end_time, c_n):
+def end(endTime, c_n):
 	s = 0
-	if end_time % c_n != 0:
+	if endTime % c_n != 0:
 		s = 1
 	return s
 
 # To calculate syncopation value of the sequence in the given time-signature.
-def get_syncopation(seq, timesig, postbar_seq):
+#def get_syncopation(seq, timesig, postbar_seq):
+def get_syncopation(bar, parameter = None):
 	syncopation = 0
 
 	numerator = int(timesig.split("/")[0])
-	if numerator == roundDownPower2(numerator):	# if is a binary time-signature
+	if numerator == round_down_power_2(numerator):	# if is a binary time-signature
 		# converting to minimum time-span format
 		seq = get_min_timeSpan(seq)	
 		if postbar_seq != None:
 			postbar_seq = get_min_timeSpan(postbar_seq)
 
 		# sf is a stretching factor matching rhythm sequence and meter, as Keith defines the note duration as a multiple of 1/(2^d) beats where d is number of metrical level
-		sf = roundUpPower2(len(seq))
+		sf = round_up_power_2(len(seq))
 		
 		# retrieve all the indices of all the notes in this sequence
 		note_indices = get_note_indices(seq)
@@ -71,7 +72,7 @@
 				end_time = note_indices[i+1]*sf/float(len(seq))
 
 			duration = end_time - start_time
-			c_n = roundDownPower2(duration)
+			c_n = round_down_power_2(duration)
 			syncopation = syncopation + start(start_time,c_n) + end(end_time,c_n)
 	else: 
 		print 'Error: KTH model can only deal with binary time-signature, e.g. 2/4 and 4/4. '
--- a/Syncopation models/LHL.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/LHL.py	Tue Apr 07 19:05:07 2015 +0100
@@ -30,15 +30,13 @@
 		for a in range(len(subBinarySequences)):
 			recursive_tree(subBinarySequences[a], subdivisionSequence, weightSequence, subWeightSequences[a], level+1)
 
+#!!!! needs fixing
 def are_parameters_valid(parameters):
-	areValid = False
-	if 'Lmax' not in parameters :
+	areValid = True
+#	if 'Lmax' not in parameters :
 
 def get_syncopation(bar, parameters = None):
-	'''
-	The get_syncopation function calculates syncopation value . 
-	'''
-#def get_syncopation(seq, subdivision_seq, weight_seq, prebar_seq, rhythm_category):
+	del terminalNodes[:]
 	syncopation = None
 
 	binarySequence = bar.get_binary_sequence()
@@ -50,7 +48,7 @@
 		# If the parameters are not given, use the default settings
 		if parameters == None:
 			Lmax  = 5
-			weightSequence = range(0,-Lmax,-1)
+			weightSequence = range(0,-Lmax,-1) # i.e. [0,-1,-2,-3,-4]
 		else:
 			if are_parameters_valid(parameters):
 				Lmax = parameters['Lmax']
@@ -65,7 +63,7 @@
 			recursive_tree(ceiling(prebarBinarySequence), subdivisionSequence, weightSequence, weightSequence[0],0)
 			
 			# Only keep the last note-type node
-			while terminalNodes[-1].node_type != 'N':
+			while terminalNodes[-1].nodeType != 'N':
 				del terminalNodes[-1]
 			del terminalNodes[0:-1]
 
@@ -73,15 +71,15 @@
 		recursive_tree(ceiling(binarySequence), subdivisionSequence, weightSequence, weightSequence[0],0)
 		
 		# for t in terminalNodes:
-		# 	print '<', t.node_type, t.metrical_weight, '>'
+		# 	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].node_type == 'R':
+			if terminalNodes[i].nodeType == 'R':
 				for j in range(i-1, -1, -1):
-					if (terminalNodes[j].node_type == 'N') & (terminalNodes[i].metricalWeight >= terminalNodes[j].metricalWeight):
+					if (terminalNodes[j].nodeType == 'N') & (terminalNodes[i].metricalWeight >= terminalNodes[j].metricalWeight):
 						NRpairSyncopation.append(terminalNodes[i].metricalWeight - terminalNodes[j].metricalWeight)
 						break
 		#print NRpairSyncopation
--- a/Syncopation models/PRS.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/PRS.py	Tue Apr 07 19:05:07 2015 +0100
@@ -14,9 +14,9 @@
 		cost = 1
 	elif sequence[0] == 1 and sequence[-1] == 0:			# run1 prototype
 		cost = 2
-	elif sequence[0] == 1 and (nextSequence == None or nenextSequencext_seq[0] == 0):	# run2 prototype
+	elif sequence[0] == 1 and (nextSequence == None or nextSequence[0] == 0):	# run2 prototype
 		cost = 2
-	elif sequence[0] == 1 and sequence[-1] == 1 and nextSequence != None and nextSequence[0] == 1:		# upbeat prototype
+	elif sequence[-1] == 1 and nextSequence != None and nextSequence[0] == 1:		# upbeat prototype
 		cost = 3
 	elif sequence[0] == 0:							# syncopated prototype
 		cost = 5
@@ -25,44 +25,47 @@
 
 # This function calculates the syncopation value (cost) for the sequence with the postbar_seq for a certain level. 
 def syncopation_perlevel(subSequences):
+	print 'subSequences', subSequences
 	total = 0
 	for l in range(len(subSequences)-1):
+		print 'cost', get_cost(subSequences[l], subSequences[l+1])
 		total = total + get_cost(subSequences[l], subSequences[l+1])
+	print 'total this level', total
 	normalised = float(total)/(len(subSequences)-1)
 	
 	return normalised
 
-#def get_syncopation(seq,subdivision_seq, postbar_seq, rhythm_category):
 def get_syncopation(bar, parameters = None):
-	'''
-	The get_syncopation function calculates the overall syncopation value for a bar of sequence.
-	'''
 	syncopation = None
 
 	binarySequence = bar.get_binary_sequence()
 	subdivisionSequence = bar.get_subdivision_sequence()
 
+	# PRS does not handle polyrhythms
 	if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly':
 		print 'Warning: PRS model detects polyrhythms so returning None.'
 	else:
 		syncopation = 0
 
+		# retrieve the binary sequence in the next bar
 		if bar.get_next_bar() != None:
 			nextbarBinarySequence = bar.get_next_bar().get_binary_sequence()
 		else:
 			nextbarBinarySequence = None
 
-		# the initial number of sub-sequences at a certain metrical level
+		# numberOfSubSeqs is the number of sub-sequences at a certain metrical level, initialised to be 1 (at the bar level)
 		numberOfSubSeqs = 1	
 		for subdivisor in subdivisionSequence:
-			# the number of sub-sequence at the current level is product of all the subdivisors up to the current level
+			# numberOfSubSeqs is product of all the subdivisors up to the current level
 			numberOfSubSeqs = numberOfSubSeqs * subdivisor
+			
 			# recursion stops when the length of sub-sequence is less than 2
 			if len(binarySequence)/numberOfSubSeqs >= 2:		
 				# generate sub-sequences and append the next bar sequence
 				subSequences = subdivide(ceiling(binarySequence), numberOfSubSeqs)	
 				subSequences.append(nextbarBinarySequence)
 				# adding syncopation at each metrical level to the total syncopation
+				#print 'per level', syncopation_perlevel(subSequences)
 				syncopation += syncopation_perlevel(subSequences)	
 			else:
 				break
--- a/Syncopation models/SG.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/SG.py	Tue Apr 07 19:05:07 2015 +0100
@@ -4,73 +4,85 @@
 
 '''
 
-from BasicFuncs import get_H, get_min_timeSpan
+from basic_functions import get_H, get_min_timeSpan, get_rhythm_category
+from TMC import find_L
 
-def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
+#def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
+def get_syncopation(bar, parameters = None):
 	syncopation = None
-	if rhythm_category == 'poly':
-		print 'Error: SG model cannot deal with polyrhythms.'
+	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:
-		
-		seq = get_min_timeSpan(seq)	# converting to the minimum time-span format
-		
-		# checking whether the given L_max is enough to analyse the given sequence, if not, request a bigger L_max
-		new_L_max = True
-		matching_level = L_max
-		while matching_level >= 0:
-			if len(get_H(weight_seq,subdivision_seq, matching_level)) == len(seq):
-				new_L_max = False
-				break
+		velocitySequence = get_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:
-				matching_level = matching_level - 1
+				pass
+				#raise InvalidParameterError
 
-		if new_L_max == True:
-			print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.'
-
-		else:
+		L = find_L(velocitySequence, Lmax, weightSequence, subdivisionSequence) 
+		print 'L', L
+		if L != None:
 			syncopation = 0
 			# generate the metrical weights of the lowest level
-			H = get_H(weight_seq,subdivision_seq, matching_level)
+			H = get_H(weightSequence,subdivisionSequence, L)
+			print 'H', H
 
-			# The aveDif_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 aveDif_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 = []
-				parameter_garma = 0.8
+				parameterGarma = 0.8
 				
 				# The findPre function is to calculate the index of the previous neighbour at a certain metrical level.
-				def findPre(index, level):
-					pre_index = (index - 1)%len(H)
-					while(H[pre_index] > level):
-						pre_index = (pre_index - 1)%len(H)
-					return pre_index
+				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 findPost(index, level):
-					post_index = (index + 1)%len(H)
-					while(H[post_index] > level):
-						post_index = (post_index + 1)%len(H)
-					return post_index
+				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):
-					parameter_beta = 0.5
-					dif_v = seq[index1]-seq[index2]
+					parameterBeta = 0.5
+					dif_v = velocitySequence[index1]-velocitySequence[index2]
 					dif_h = abs(H[index1]-H[index2])
-					dif = dif_v*(parameter_beta*dif_h/4+1-parameter_beta)
+					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 = ( parameter_garma*dif(index,findPre(index,l))+dif(index,findPost(index,l)) )/(1+parameter_garma)
+					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(seq)):
-				if seq[index] != 0: # Onset detected
-					h = H[index]
-					potential = 1 - pow(0.5,h) # Syncopation potential according to its metrical level, which is equal to the metrical weight
-					level = h 		# Metrical weight happens to be equal to its metrical level
-					syncopation += min(aveDif_neighbours(index, h))*potential
+			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	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/TMC.py	Tue Apr 07 19:05:07 2015 +0100
@@ -4,52 +4,81 @@
 
 '''
 
-from BasicFuncs import get_H, ceiling, get_min_timeSpan
+from basic_functions import get_H, ceiling, get_min_timeSpan, get_rhythm_category
 
 # 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(seq, H):
+def get_metricity(binarySequence, H):
 	metricity = 0
-	for m in range(len(seq)):
-		metricity = metricity + seq[m]*H[m]
+	for m in range(len(binarySequence)):
+		metricity = metricity + binarySequence[m]*H[m]
 	return metricity
 
 # The get_max_metricity function calculates the maximum metricity for the same number of notes in a binary sequence.
-def get_max_metricity(seq, H):
-	max_metricity = 0
+def get_max_metricity(binarySequence, H):
+	maxMetricity = 0
 	H.sort(reverse=True) # Sort the metrical weight sequence from large to small
-	for i in range(sum(seq)):
-		max_metricity = max_metricity+H[i]
-	return max_metricity
+	for i in range(sum(binarySequence)):
+		maxMetricity = maxMetricity+H[i]
+	return maxMetricity
+
+# find the metrical level L that contains the same number of metrical positions as the length of the binary sequence
+# if the given Lmax is not big enough to analyse the given sequence, request a bigger Lmax
+def find_L(rhythmSequence, Lmax, weightSequence, subdivisionSequence):
+	L = Lmax
+
+	# initially assuming the Lmax is not big enough
+	needBiggerLmax = True 
+	
+	# from the lowest metrical level (Lmax) to the highest, find the matching metrical level that 
+	# has the same length as the length of binary sequence  
+	while L >= 0:
+		if len(get_H(weightSequence,subdivisionSequence, L)) == len(rhythmSequence):
+			needBiggerLmax = False
+			break
+		else:
+			L = L - 1
+
+	# if need a bigger Lmax, print error message and return None; otherwise return the matching metrical level L
+	if needBiggerLmax:
+		print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.'
+		L = None
+	
+	return L
 
 # The get_syncopation function calculates the syncopation value of the given sequence for TMC model. 
-def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
+#def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
+def get_syncopation(bar, parameters = None):
 	syncopation = None
-	if rhythm_category == 'poly':
-		print 'Error: TMC model cannot deal with polyrhythms.'
+	binarySequence = bar.get_binary_sequence()
+	subdivisionSequence = bar.get_subdivision_sequence()
+
+	if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly':
+		print 'Warning: TMC model detects polyrhythms so returning None.'
 	else:
-		seq = get_min_timeSpan(seq)	# converting to the minimum time-span format
+		binarySequence = get_min_timeSpan(binarySequence)	# converting to the minimum time-span format
 
-		# checking whether the given L_max is enough to analyse the given sequence, if not, request a bigger L_max
-		new_L_max = True
-		matching_level = L_max
-		while matching_level >= 0:
-			if len(get_H(weight_seq,subdivision_seq, matching_level)) == len(seq):
-				new_L_max = False
-				break
+		# 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]
+		else:
+			if are_parameters_valid(parameters):
+				Lmax = parameters['Lmax']
+				weightSequence = parameters['W']
 			else:
-				matching_level = matching_level - 1
+				pass
+				#raise InvalidParameterError
 
-		if new_L_max == True:
-			print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.'
-		
-		else:
-			# generate the metrical weights of the lowest level, 
-			# using the last matching_level number of elements in the weight_seq list, to make sure the last element is 1
-			H = get_H (weight_seq[-(matching_level+1):], subdivision_seq, matching_level)
+		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(ceiling(seq), H)	# converting to binary sequence then calculate metricity
-			max_metricity = get_max_metricity(ceiling(seq), H)
+			metricity = get_metricity(binarySequence, H)	# converting to binary sequence then calculate metricity
+			maxMetricity = get_max_metricity(binarySequence, H)
 
-			syncopation = max_metricity - metricity
+			syncopation = maxMetricity - metricity
+
 	return syncopation
 
--- a/Syncopation models/TOB.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/TOB.py	Tue Apr 07 19:05:07 2015 +0100
@@ -4,21 +4,32 @@
 
 '''
 
-from BasicFuncs import ceiling, find_divisor, get_min_timeSpan
+from basic_functions import ceiling, find_divisor, is_prime, get_min_timeSpan
 
-# This function calculates the syncopation value for TOB model.
-def get_syncopation(seq):
+def get_syncopation(bar, parameter = None):
+	binarySequence = bar.get_binary_sequence()
+	sequenceLength = len(binarySequence)
+
 	syncopation = 0
-	bseq_ts = get_min_timeSpan(ceiling(seq))	# converting to binary and mininum time-span sequence
-	divisors = find_divisor(len(bseq_ts))		# find all the divisors other than 1 and the length of this sequence
-	del divisors[0]
-	del divisors[-1]
 
-	offbeatness = [1]*len(bseq_ts)			
-	for index in range(len(bseq_ts)):
-		for d in divisors:
-			if index % d == 0:
-				offbeatness[index] = 0
-				break
-		syncopation += bseq_ts[index]*offbeatness[index]
+	# if the length of b_sequence is 1 or a prime number, syncopation is 0;
+	# otherwise the syncopation is calculated by adding up the number of off-beat notes
+	if not ( (sequenceLength == 1) or (is_prime(sequenceLength)) ):
+		# find all the divisors other than 1 and the length of this sequence
+		divisors = find_divisor(sequenceLength)		
+		del divisors[0]
+		del divisors[-1]
+
+		# the on-beat/off-beat positions are the ones that can/cannot be subdivided by the sequenceLength;
+		# the on-beat positions are set to be 0, off-beat positions are set to be 1
+		offbeatness = [1]*sequenceLength			
+		for index in range(sequenceLength):
+			for d in divisors:
+				if index % d == 0:
+					offbeatness[index] = 0
+					break
+			#print 'offbeatness', offbeatness
+			# syncopation is the sum of the hadamard-product of the rhythm binary-sequence and the off-beatness 
+			syncopation += binarySequence[index]*offbeatness[index]
+	
 	return syncopation
--- a/Syncopation models/TimeSignature.pkl	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/TimeSignature.pkl	Tue Apr 07 19:05:07 2015 +0100
@@ -8,6 +8,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'3/2'
 p5
@@ -18,6 +19,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'3/4'
 p8
@@ -28,6 +30,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'12/8'
 p11
@@ -38,6 +41,7 @@
 aI2
 aI3
 aI2
+aI2
 aaI2
 asS'3/8'
 p14
@@ -48,6 +52,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'2/2'
 p17
@@ -58,6 +63,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'6/8'
 p20
@@ -68,6 +74,7 @@
 aI3
 aI2
 aI2
+aI2
 aaI1
 asS'5/4'
 p23
@@ -78,6 +85,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'4/2'
 p26
@@ -88,6 +96,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'7/4'
 p29
@@ -98,6 +107,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'5/8'
 p32
@@ -108,6 +118,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI1
 asS'4/4'
 p35
@@ -118,6 +129,7 @@
 aI2
 aI2
 aI2
+aI2
 aaI2
 asS'9/8'
 p38
@@ -128,5 +140,6 @@
 aI3
 aI2
 aI2
+aI2
 aaI1
 as.
\ No newline at end of file
--- a/Syncopation models/WNBD.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/WNBD.py	Tue Apr 07 19:05:07 2015 +0100
@@ -3,7 +3,7 @@
 Institution: Centre for Digital Music, Queen Mary University of London
 
 '''
-from BasicFuncs import repeat, get_note_indices
+from basic_functions import repeat, get_note_indices
 
 # To find the product of multiple numbers
 def cumu_multiply(numbers):
@@ -12,56 +12,72 @@
 		product = product*n
 	return product
 
-def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq):
+#def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq):
+def get_syncopation(bar, parameters = None):
 	syncopation = None
 	
-	num_beats = cumu_multiply(subdivision_seq[0:strong_beat_level+1])	# num_beats is the number of strong beats
+	binarySequence = bar.get_binary_sequence()
+	sequenceLength = len(binarySequence)
+	subdivisionSequence = bar.get_subdivision_sequence()
+	strongBeatLevel = bar.get_beat_level()
+	nextbarBinarySequence = None
+
+	if bar.get_next_bar() != None:
+		nextbarBinarySequence = bar.get_next_bar().get_binary_sequence()
+
+	numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1])	# numberOfBeats is the number of strong beats
 	
-	if len(seq)%num_beats != 0:
-		print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision_seq.'
+	if sequenceLength % numberOfBeats != 0:
+		print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision sequence.'
 	else:
 		# Find the indices of all the strong-beats
-		beat_indices = []
-		beat_interval = len(seq)/num_beats
-		for i in range(num_beats+1):
-			beat_indices.append(i*beat_interval)
-		if postbar_seq != None:		# if there is a postbar_seq, add another two beats index for later calculation
-			beat_indices += [len(seq)+beat_interval, len(seq)+ 2* beat_interval]
+		beatIndices = []
+		beatInterval = sequenceLength / numberOfBeats
+		for i in range(numberOfBeats+1):
+			beatIndices.append(i*beatInterval)
+		if nextbarBinarySequence != None:		# if there is a postbar_seq, add another two beats index for later calculation
+			beatIndices += [sequenceLength+beatInterval, sequenceLength+ 2* beatInterval]
 
-		note_indices = get_note_indices(seq)	# all the notes
+		noteIndices = get_note_indices(binarySequence)	# all the notes
 
 		# Calculate the WNBD measure for each note
-		def measure_pernote(note_index, nextnote_index):
+		def measure_pernote(noteIndices, nextNoteIndex):
 			# Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) 
 			j = 0
-			while note_index < beat_indices[j] or note_index >= beat_indices[j+1]:
+			while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]:
 				j = j + 1
 			
 			# The distance of note to nearest beat normalised by the beat interval
-			distance_nearest_beat = min(abs(note_index - beat_indices[j]), abs(note_index - beat_indices[j+1]))/float(beat_interval)
+			distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval)
 
 			# if this note is on-beat
-			if distance_nearest_beat == 0:	
+			if distanceToNearestBeat == 0:	
 				measure = 0
 			# or if this note is held on past the following beat, but ends on or before the later beat  
-			elif beat_indices[j+1] < nextnote_index <= beat_indices[j+2]:
-				measure = float(2)/distance_nearest_beat
+			elif beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]:
+				measure = float(2)/distanceToNearestBeat
 			else:
-				measure = float(1)/distance_nearest_beat
-
+				measure = float(1)/distanceToNearestBeat
 			return measure
 
 		total = 0
-		for i in range(len(note_indices)):
-			if i == len(note_indices)-1:# if this is the last note, end_time is the index of the following note in the next bar
-				if postbar_seq != None and postbar_seq != repeat([0],len(postbar_seq)):
-					nextnote_index = get_note_indices(postbar_seq)[0]+len(seq)
-				else:	# or if the next bar is none or full rest, end_time is the end of this sequence.
-					nextnote_index = len(seq)
+		for i in range(len(noteIndices)):
+			# if this is the last note, end_time is the index of the following note in the next bar
+			if i == len(noteIndices)-1:
+				# if the next bar is not none or a bar of full rest, 
+				# the nextNoteIndex is the sum of sequence length in the current bar and the noteIndex in the next bar
+				if nextbarBinarySequence != None and nextbarBinarySequence != repeat([0],len(nextbarBinarySequence)):
+					nextNoteIndex = get_note_indices(nextbarBinarySequence)[0]+sequenceLength
+				# else when the next bar is none or full rest, end_time is the end of this sequence.
+				else:
+					nextNoteIndex = sequenceLength
+			# else this is not the last note, the nextNoteIndex is the following element in the noteIndices list
 			else:
-				nextnote_index = note_indices[i+1]
-			total += measure_pernote(note_indices[i],nextnote_index)
+				nextNoteIndex = noteIndices[i+1]
+			# sum up the syncopation value for individual note at noteIndices[i]
+			total += measure_pernote(noteIndices[i],nextNoteIndex)
 
 		#syncopation = float(total) / len(note_indices)
 
+	# return the total value, leave the normalisation done in the end
 	return total
--- a/Syncopation models/basic_functions.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/basic_functions.py	Tue Apr 07 19:05:07 2015 +0100
@@ -49,47 +49,58 @@
 
 # The find_divisor function returns a list of all possible divisors for a length of sequence.
 def find_prime_factors(number):
-	prime_factors = find_divisor(number)
+	primeFactors = find_divisor(number)
 	
-	def is_prime(num):
-		if num < 2:
-			return False
-		if num == 2:
-			return True
-		else:
-			for div in range(2,num):
-				if num % div == 0:
-					return False
-		return True
+	# remove 1 because 1 is not prime number
+	del primeFactors[0]
 
-	for i in range(len(prime_factors)-1,0,-1):
-		if is_prime(prime_factors[i]) == False:
-			del prime_factors[i]
+	# reversely traverse all the divisors list and once find a non-prime then delete
+	for i in range(len(primeFactors)-1,0,-1):
+	#	print primeFactors[i], is_prime(primeFactors[i])
+		if not is_prime(primeFactors[i]):
+			del primeFactors[i]
 
-	return prime_factors
+	return primeFactors
+
+def is_prime(number):
+	isPrime = True
+	# 0 or 1 is not prime numbers
+	if number < 2:
+		isPrime = False
+	# 2 is the only even prime number
+	elif number == 2:
+		pass
+	# all the other even numbers are non-prime
+	elif number % 2 == 0:
+		isPrime = False
+	else:
+		for odd in range(3, int(math.sqrt(number) + 1), 2):
+			if number % odd == 0:
+				isPrime = False
+	return isPrime
 
 # The min_timeSpan function searches for the shortest possible time-span representation for a sequence.
 def get_min_timeSpan(seq):
-	min_ts = [1]
+	minTimeSpan = [1]
 	for d in find_divisor(len(seq)):
 		segments = subdivide(seq,d)
 		if len(segments)!=0:
-			del min_ts[:]
+			del minTimeSpan[:]
 			for s in segments:
-				min_ts.append(s[0])
-			if sum(min_ts) == sum(seq):
+				minTimeSpan.append(s[0])
+			if sum(minTimeSpan) == sum(seq):
 				break
-	return min_ts
+	return minTimeSpan
 
 # get_note_indices returns all the indices of all the notes in this sequence
-def get_note_indices(seq):
-	note_indices = []
+def get_note_indices(sequence):
+	noteIndices = []
 
-	for index in range(len(seq)):
-		if seq[index] != 0:
-			note_indices.append(index)
+	for index in range(len(sequence)):
+		if sequence[index] != 0:
+			noteIndices.append(index)
 
-	return note_indices
+	return noteIndices
 
 # The get_H returns a sequence of metrical weight for a certain metrical level (horizontal),
 # given the sequence of metrical weights in a hierarchy (vertical) and a sequence of subdivisions.
@@ -179,4 +190,15 @@
 
 
 # # testing
-# print find_prime_factors(10)
\ No newline at end of file
+# 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/music_objects.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/music_objects.py	Tue Apr 07 19:05:07 2015 +0100
@@ -96,7 +96,7 @@
 		return self.timeSignature.get_subdivision_sequence()
 
 	def get_beat_level(self):
-		return ParameterSetter.get_beat_level(self.timeSignature)
+		return self.timeSignature.get_beat_level()
 
 	def get_time_signature(self):
 		return self.timeSignature
@@ -107,17 +107,17 @@
 
 class TimeSignature():
 	def __init__(self, inputString):
-		if inputString in parameter_setter.readTimesig():
+		if inputString in parameter_setter.read_time_signature():
 			self.tsString = inputString
 		else:
 			print "Error: undefined time-signature ", inputString
 			raise NullTimeSignatureError
 
 	def get_subdivision_sequence(self):
-		return parameter_setter.timesigBase[self.tsString][0]
+		return parameter_setter.timeSignatureBase[self.tsString][0]
 	
 	def get_beat_level(self):
-		return parameter_setter.timesigBase[self.tsString][1]
+		return parameter_setter.timeSignatureBase[self.tsString][1]
 
 	def get_numerator(self):
 		return int(self.tsString.split('/')[0])
--- a/Syncopation models/parameter_setter.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/parameter_setter.py	Tue Apr 07 19:05:07 2015 +0100
@@ -8,19 +8,19 @@
 # {'key': time-signature} :  
 # {'value': [subdivision_seq, theoretical beat-level represented by index in the subdivision_seq list]}
 timeSignatureBase = {
-	'2/2': [[1,2,2,2,2],1],
-	'3/2': [[1,3,2,2,2],1],
-	'4/2': [[1,2,2,2,2],1],
-	'2/4': [[1,2,2,2,2],1],
-	'3/4': [[1,3,2,2,2],1],
-	'4/4': [[1,2,2,2,2],2],
-	'5/4': [[1,5,2,2,2],1],
-	'7/4': [[1,7,2,2,2],1],
-	'3/8': [[1,3,2,2,2],1],
-	'5/8': [[1,5,2,2,2],1],
-	'6/8': [[1,2,3,2,2],1],
-	'9/8': [[1,3,3,2,2],1],
-	'12/8':[[1,2,2,3,2],2],	
+	'2/2': [[1,2,2,2,2,2],1],
+	'3/2': [[1,3,2,2,2,2],1],
+	'4/2': [[1,2,2,2,2,2],1],
+	'2/4': [[1,2,2,2,2,2],1],
+	'3/4': [[1,3,2,2,2,2],1],
+	'4/4': [[1,2,2,2,2,2],2],
+	'5/4': [[1,5,2,2,2,2],1],
+	'7/4': [[1,7,2,2,2,2],1],
+	'3/8': [[1,3,2,2,2,2],1],
+	'5/8': [[1,5,2,2,2,2],1],
+	'6/8': [[1,2,3,2,2,2],1],
+	'9/8': [[1,3,3,2,2,2],1],
+	'12/8':[[1,2,2,3,2,2],2],	
 }
 
 def add_time_signature(timeSignature, subdivisionSequence, beatLevel):
--- a/Syncopation models/rhythm_parser.py	Fri Apr 03 22:57:27 2015 +0100
+++ b/Syncopation models/rhythm_parser.py	Tue Apr 07 19:05:07 2015 +0100
@@ -6,7 +6,7 @@
 # Parse the rhythm file and return a list of Bar objects
 Piece = []
 
-from parameter_setter import timesigBase
+from parameter_setter import timeSignatureBase
 
 
 comment_sign = '#'
@@ -98,9 +98,9 @@
 #def checkTimesig(input):
 
 
-# TESTING
-line = 't{4/4}	q{1,0,0,1,0,0,1,0,0,0,1,0,0.8,0,0,0} t{2/4} # This is a comment'
-# print discardSpaces(discardComments(line))
-print line[line.find('{')+1:line.find('}')]
-#print readRhythm("rhythmbase/testrhythm.txt")
+# # TESTING
+# line = 't{4/4}	q{1,0,0,1,0,0,1,0,0,0,1,0,0.8,0,0,0} t{2/4} # This is a comment'
+# # print discardSpaces(discardComments(line))
+# print line[line.find('{')+1:line.find('}')]
+# #print readRhythm("rhythmbase/testrhythm.txt")