changeset 38:cc38b3047ed9

updated syncopation.y to allow output of sync for a bar list also fixed some problems in models and other modules
author christopherh <christopher.harte@eecs.qmul.ac.uk>
date Mon, 13 Apr 2015 23:06:49 +0100
parents fefbe0d853c6
children 02f69d16603d 6371e8f21f7d
files Syncopation models/LHL.py Syncopation models/PRS.py Syncopation models/TOB.py Syncopation models/music_objects.py Syncopation models/rhythm_parser.py Syncopation models/syncopation.py
diffstat 6 files changed, 89 insertions(+), 96 deletions(-) [+]
line wrap: on
line diff
--- a/Syncopation models/LHL.py	Mon Apr 13 22:36:51 2015 +0100
+++ b/Syncopation models/LHL.py	Mon Apr 13 23:06:49 2015 +0100
@@ -8,6 +8,8 @@
 
 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):
@@ -57,14 +59,16 @@
 			print 'Error: the given parameters are not valid.'
 		else:
 			# 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()
+			prevbar =  bar.get_previous_bar()
+			if prevbar != None and not prevbar.is_empty():
+				prebarBinarySequence = prevbar.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 len(terminalNodes)>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)
--- a/Syncopation models/PRS.py	Mon Apr 13 22:36:51 2015 +0100
+++ b/Syncopation models/PRS.py	Mon Apr 13 23:06:49 2015 +0100
@@ -25,12 +25,12 @@
 
 # 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
+	#print 'subSequences', subSequences
 	total = 0
 	for l in range(len(subSequences)-1):
-		print 'cost', get_cost(subSequences[l], subSequences[l+1])
+		#print 'cost', get_cost(subSequences[l], subSequences[l+1])
 		total = total + get_cost(subSequences[l], subSequences[l+1])
-	print 'total this level', total
+	#print 'total this level', total
 	normalised = float(total)/(len(subSequences)-1)
 	
 	return normalised
--- a/Syncopation models/TOB.py	Mon Apr 13 22:36:51 2015 +0100
+++ b/Syncopation models/TOB.py	Mon Apr 13 23:06:49 2015 +0100
@@ -4,10 +4,10 @@
 
 '''
 
-from basic_functions import ceiling, find_divisor, is_prime
+from basic_functions import ceiling, find_divisor, is_prime, velocity_sequence_to_min_timespan
 
-def get_syncopation(bar, parameter = None):
-	binarySequence = bar.get_binary_sequence()
+def get_syncopation(bar, parameters = None):
+	binarySequence = velocity_sequence_to_min_timespan(bar.get_binary_sequence())
 	sequenceLength = len(binarySequence)
 
 	syncopation = 0
--- a/Syncopation models/music_objects.py	Mon Apr 13 22:36:51 2015 +0100
+++ b/Syncopation models/music_objects.py	Mon Apr 13 23:06:49 2015 +0100
@@ -115,7 +115,8 @@
 		velocitySequence += [0]*(noteSequence[-1].duration-1)
 
 	# normalising velocity sequence between 0-1
-	velocitySequence = VelocitySequence([float(v)/max(velocitySequence) for v in velocitySequence])
+	if max(velocitySequence)>0:
+		velocitySequence = VelocitySequence([float(v)/max(velocitySequence) for v in velocitySequence])
 
 	return velocitySequence
 
@@ -162,7 +163,7 @@
 
 	def get_velocity_sequence(self):
 		if self.velocitySequence == None:
-			self.velocitySequence = note_sequence_to_velocity_sequence(self.noteSequence)
+			self.velocitySequence = note_sequence_to_velocity_sequence(self.noteSequence, self.get_bar_ticks())
 		return self.velocitySequence
 
 	def get_binary_sequence(self):
@@ -193,11 +194,23 @@
 	def get_bar_ticks(self):
 		return calculate_bar_ticks(self.timeSignature.get_numerator(),self.timeSignature.get_denominator(), self.tpq)
 
+	def is_empty(self):
+		if max(self.get_velocity_sequence())>0:
+			return False
+		else:
+			return True
+
 	def to_string(self, sequenceType=None):
+		output = "t{"+self.timeSignature.to_string()+"}"
+		prev = self.get_previous_bar()
+		if prev!=None:
+			if prev.get_time_signature()==self.get_time_signature():
+				output=""
+
 		if sequenceType==None or sequenceType=="v":
-			output = "v{"+self.get_velocity_sequence().to_string()+"}"
+			output += "v{"+self.get_velocity_sequence().to_string()+"}"
 		else:
-			output = "y{"+self.get_note_sequence().to_string()+"}"
+			output += "y{"+self.get_note_sequence().to_string()+"}"
 		return output
 
 
--- a/Syncopation models/rhythm_parser.py	Mon Apr 13 22:36:51 2015 +0100
+++ b/Syncopation models/rhythm_parser.py	Mon Apr 13 23:06:49 2015 +0100
@@ -10,6 +10,7 @@
 from music_objects import *
 
 comment_sign = '#'
+
 def discard_comments(line):
 	if comment_sign in line:
 		line = line[0:line.find(comment_sign)]
--- a/Syncopation models/syncopation.py	Mon Apr 13 22:36:51 2015 +0100
+++ b/Syncopation models/syncopation.py	Mon Apr 13 23:06:49 2015 +0100
@@ -3,102 +3,77 @@
 Institution: Centre for Digital Music, Queen Mary University of London
 
 '''
+from rhythm_parser import *
+from music_objects import *
+
 
 def sync_perbar_permodel (model, bar, parameters=None):
 	return model.get_syncopation(bar, parameters)
 
- def syncopation_barlist_permodel(model, source, parameters=None):
- 	total = 0
+def syncopation_barlist_permodel(model, source, parameters=None):
+ 	total = 0.0
+ 	barResults = []
  	numberOfNotes = 0
 
+	barlist = None
+
+ 	if isinstance(source, BarList):
+ 		barlist = source
+ 		sourceType = "bar list"
+	elif isinstance(source, basestring):
+		#treat source as a filename
+		sourceType = source
+		if source[-4:]==".mid":
+			import readmidi
+			midiFile = readmidi.read_midi_file(source)
+			barlist = readmidi.get_bars_from_midi(midiFile)
+
+		elif source[-4:]==".rhy":
+			#import rhythm_parser 
+			barlist = read_rhythm(source)
+		else:
+			print "Error in syncopation_barlist_permodel(): Unrecognised file type."
+	else:
+		print "Error in syncopation_barlist_permodel(): unrecognised source type."
 	
+	barsDiscarded=0
 
- 	for bar in barlist:
- 		if sync_perbar_permodel(model, bar, parameters) != None:
- 			total += sync_perbar_permodel(model, bar, parameters)
- 			numberOfNotes += sum(bar.get_binary_sequence())
- 		else:
- 			print 'Bar %d cannot be measured, returning None.' % barlist.index(bar)
+	if barlist!=None:
+		for bar in barlist:
+			if not bar.is_empty():
+				barSyncopation = sync_perbar_permodel(model, bar, parameters)
+			else:
+				barSyncopation = None
+				print 'Bar %d cannot be measured because it is empty, returning None.' % barlist.index(bar)
+			
+			barResults.append(barSyncopation)
+			if barSyncopation != None:
+				total += barSyncopation
+				numberOfNotes += sum(bar.get_binary_sequence())
+			else:
+				barsDiscarded += 1
+				print 'Model could not measure bar %d, returning None.' % barlist.index(bar)
 
- 	if model is WNBD:
- 		total = (float) total/ numberOfNotes
+		import WNBD
+		if model is WNBD:
+			total =  total / numberOfNotes
 
-# def sync_perbar_permodel(seq, model, timesig = None, subdivision_seq = None, weight_seq = None, L_max = 5, prebar_seq = None, postbar_seq = None, strong_beat_level = None):
-# 	syncopation = None
+		average = total / (len(barResults)-barsDiscarded)
 
-# 	if seq == None or model == None:
-# 		print 'Error: please indicate rhythm sequence and syncopation model.'
+ 	return {"summed_syncopation":total, "average_syncopation_per_bar":average, "source":sourceType, "number_of_bars":len(barResults), "number_of_bars_not_measured":barsDiscarded, "syncopation_by_bar":barResults}
 
-# 	elif timesig == None and subdivision_seq == None:
-# 		print 'Error: please indicate either time signature or subdivision sequence.'
-	
-# 	else:
-# 		while subdivision_seq == None:
-# 			from basic_functions import get_subdivision_seq
-# 			subdivision_seq = get_subdivision_seq(timesig, L_max)
 
-# 		# The get_rhythm_category function is used to detect rhythm category: monorhythm or polyrhythm.
-# 		# For monorhythms, all prime factors of the length of minimum time-span representation of this sequence are
-# 		# elements of its subdivision_seq, otherwise it is polyrhythm; 
-# 		# e.g. prime_factors of polyrhythm 100100101010 in 4/4 is [2,3] but subdivision_seq = [1,2,2] for 4/4 
-# 		def get_rhythm_category():
-# 			rhythm_category = 'mono'
-# 			from basic_functions import get_min_timeSpan, find_prime_factors
-# 			for f in find_prime_factors(len(get_min_timeSpan(seq))):
-# 				if not (f in subdivision_seq): 
-# 					rhythm_category = 'poly'
-# 					break
-# 			return rhythm_category
-		
-# 		rhythm_category = get_rhythm_category()
 
-# 		if model == 'LHL':	
-# 			import LHL
-# 			if weight_seq == None:
-# 				weight_seq = range(0,-L_max,-1)
-# 			syncopation = LHL.get_syncopation(seq, subdivision_seq, weight_seq, prebar_seq, rhythm_category)
-# 		elif model == 'PRS':	
-# 			import PRS
-# 			syncopation = PRS.get_syncopation(seq, subdivision_seq, postbar_seq, rhythm_category)
-# 		elif model == 'TMC':	
-# 			import TMC
-# 			if weight_seq == None:
-# 				weight_seq = range(L_max+1,0,-1)
-# 			syncopation = TMC.get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category)
-# 		elif model == 'SG':		
-# 			import SG
-# 			if weight_seq == None:
-# 				weight_seq = range(L_max+1)
-# 			syncopation = SG.get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category)
-# 		elif model == 'KTH':
-# 			import KTH
-# 			syncopation = KTH.get_syncopation(seq, timesig, postbar_seq)
-# 		elif model == 'TOB':	
-# 			import TOB
-# 			syncopation = TOB.get_syncopation(seq)
-# 		elif model == 'WNBD':
-# 			import WNBD
-# 			if strong_beat_level == None:
-# 				if timesig == '4/4':
-# 					strong_beat_level = 2
-# 				else:
-# 					strong_beat_level = 1 
-# 			syncopation = WNBD.get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq)
+def results_to_xml(results, outputFilename):
+	from xml.etree.ElementTree import Element, ElementTree
 
-# 		else:
-# 			print 'Error: undefined syncopation model.'
+	elem = Element("syncopation_results")
 
-# 	return syncopation
+	for key, val in results.items():
+		child = Element(key)
+		child.text = str(val)
+		elem.append(child)
 
-# def syncopation_all(rhythm, model, timesig, subdivision_seq = None, weight_seq = None, L_max = 5, strong_beat_level = None):
-# 	syncopation = 0
-# 	# Chope rhythm into seq
-# 	# ...
+	ElementTree(elem).write(outputFilename)
 
-# 	for (seq_perbar in seq):
-# 		sync_perbar = syncopation_perbar(seq_perbar,model, timesig, subdivision_seq, weight_seq, L_max, strong_beat_level)
-# 		if sync_perbar != None:
-# 			syncopation = syncopation + sync_perbar
 
-# 	return syncopation
-