changeset 40:6371e8f21f7d

updating the syncopation functions to fix a few problems
author christopherh <christopher.harte@eecs.qmul.ac.uk>
date Thu, 23 Apr 2015 15:46:58 +0100
parents cc38b3047ed9
children 903aec3d5b9f
files SMC2015latex/syncopation_toolkit.tex Syncopation models/basic_functions.py Syncopation models/music_objects.py Syncopation models/rhythm_parser.py Syncopation models/syncopation.py
diffstat 5 files changed, 94 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/SMC2015latex/syncopation_toolkit.tex	Mon Apr 13 23:06:49 2015 +0100
+++ b/SMC2015latex/syncopation_toolkit.tex	Thu Apr 23 15:46:58 2015 +0100
@@ -191,6 +191,47 @@
 \label{ta:BNF} 
 \end{table*}
 
+
+\begin{table}
+\footnotesize{
+\begin{minted}[frame=single,framesep=10pt]{python}
+T{4/4} # time signature
+TPQ{4} # ticks per quarternote
+# Bar 1
+Y{(0,3,2),(3,1,1),(6,2,2),(10,2,1),(12,4,1)}
+# Bar 2
+V{1,0,0,0.5,0,0,1,0,0,0,0.5,0,0.5,0,0,0}
+\end{minted}
+}
+\caption{Example rhythm annotation file containing two bars of the Son Clave rhythm. The first is expressed as a note sequence with resolution of four ticks per quarternote; the second is the same rhythm expressed as a velocity sequence.}
+\label{ta:clave} 
+\end{table}
+
+
+\begin{table}
+\footnotesize{
+\begin{minted}[frame=single,framesep=10pt]{python}
+>>>from syncopation import *
+>>>import PRS as model
+>>>calculate_syncopation(model, "clave.rhy")
+{'bars_with_valid_output': [0, 1],
+ 'mean_syncopation_per_bar': 8.625,
+ 'model_name': 'PRS',
+ 'number_of_bars': 2,
+ 'number_of_bars_not_measured': 0,
+ 'source': 'clave.rhy',
+ 'summed_syncopation': 17.25,
+ 'syncopation_by_bar': [8.625, 8.625]}
+\end{minted}
+}
+\caption{Syntax of rhythm text format Backus-Naur Form}
+\label{ta:clave} 
+\end{table}
+
+
+
+
+
 % \section{Page size and format}\label{sec:page_size}
 
 % \begin{equation}
--- a/Syncopation models/basic_functions.py	Mon Apr 13 23:06:49 2015 +0100
+++ b/Syncopation models/basic_functions.py	Thu Apr 23 15:46:58 2015 +0100
@@ -182,8 +182,8 @@
 	return rhythmCategory
 
 
-def string_to_sequence(inputString):
-	return map(int, inputString.split(','))
+def string_to_sequence(inputString,typeFunction=float):
+	return map(typeFunction, inputString.split(','))
 
 # # The get_subdivision_seq function returns the subdivision sequence of several common time-signatures defined by GTTM, 
 # # or ask for the top three level of subdivision_seq manually set by the user.
--- a/Syncopation models/music_objects.py	Mon Apr 13 23:06:49 2015 +0100
+++ b/Syncopation models/music_objects.py	Thu Apr 23 15:46:58 2015 +0100
@@ -11,7 +11,7 @@
 
 		if firstarg != None:
 			if isinstance(firstarg,basestring):
-				intlist = string_to_sequence(firstarg)
+				intlist = string_to_sequence(firstarg,int)
 				self.startTime = intlist[0]
 				self.duration = intlist[1]
 				self.velocity = intlist[2]
@@ -48,6 +48,13 @@
 			noteSequenceString += note.to_string() + ","
 		return noteSequenceString[:-1]
 
+
+class NormalisedVelocityValueOutOfRange(Exception):
+	def __init__(self, value):
+		self.value = value
+	def __str__(self):
+		return repr(self.value)
+
 # VelocitySequence is a list of float numbers
 class VelocitySequence(list):
 	def __init__(self, velocitySequence = None):
@@ -58,7 +65,16 @@
 				self+=velocitySequence
 
 	def string_to_velocity_sequence(self,inputString):
-		self.extend(string_to_sequence(inputString))
+		
+		def convert_velocity_value(argstring):
+			value = float(argstring)
+			if value>=0 and value<=1:
+				return value
+			else:
+				raise NormalisedVelocityValueOutOfRange("Value: "+argstring+" in " + inputString)
+
+		self.extend(string_to_sequence(inputString,convert_velocity_value))
+
 
 	def to_string(self):
 		return str(velocity_sequence_to_min_timespan(self))[1:-1].replace(" ","")
@@ -96,7 +112,7 @@
 
 
 def note_sequence_to_velocity_sequence(noteSequence, timespanTicks = None):
-	
+
 	velocitySequence = VelocitySequence()
 	
 	previousNoteStartTime = -1
--- a/Syncopation models/rhythm_parser.py	Mon Apr 13 23:06:49 2015 +0100
+++ b/Syncopation models/rhythm_parser.py	Thu Apr 23 15:46:58 2015 +0100
@@ -20,6 +20,11 @@
 	line = line.replace(" ", '').replace("\t", '')
 	return line
 
+def discard_linereturns(line):
+	line = line.replace("\n","").replace("\r","")
+	return line
+	
+
 # def extractInfo(line):
 # 	try:
 # 		if '{' not in line and '}' not in line:
@@ -54,7 +59,7 @@
 			# if there is a valid field, it should be a time signature
 			if field!=None:
 				[fieldname,value] = field
-				if fieldname=="t":
+				if fieldname.lower()=="t":
 					timeSignature = TimeSignature(value)
 				else:
 					print 'Error, first field in the file should set the time signature.'
@@ -76,8 +81,8 @@
 
 def parse_line(line,  timeSignature=None, tempo=None, ticksPerQuarter=None):
 	
-	#strip the line of spaces and comments
-	line = discard_spaces(discard_comments(line)).replace("\n","")
+	#strip the line of line returns, spaces and comments
+	line = discard_linereturns(discard_spaces(discard_comments(line)))
 	
 	bars = BarList()
 
@@ -116,6 +121,11 @@
 	
 	return bars, tempo, timeSignature, ticksPerQuarter
 
+class RhythmSyntaxError(Exception):
+	def __init__(self, value):
+		self.value = value
+	def __str__(self):
+		return repr(self.value)
 
 def get_next_field(line):
 	index = line.find("}")
@@ -126,7 +136,7 @@
 		field = fieldtext.split("{")
 	else:
 		print 'Error, incorrect syntax: "'+line+'"'
-		#raise RhythmSyntaxError(line)
+		raise RhythmSyntaxError(line)
 
 	return field,line
 
--- a/Syncopation models/syncopation.py	Mon Apr 13 23:06:49 2015 +0100
+++ b/Syncopation models/syncopation.py	Thu Apr 23 15:46:58 2015 +0100
@@ -10,7 +10,7 @@
 def sync_perbar_permodel (model, bar, parameters=None):
 	return model.get_syncopation(bar, parameters)
 
-def syncopation_barlist_permodel(model, source, parameters=None):
+def calculate_syncopation(model, source, parameters=None):
  	total = 0.0
  	barResults = []
  	numberOfNotes = 0
@@ -20,6 +20,9 @@
  	if isinstance(source, BarList):
  		barlist = source
  		sourceType = "bar list"
+ 	elif isinstance(source, Bar):
+ 		barlist = BarList().append(source)
+ 		sourceType = "single bar"
 	elif isinstance(source, basestring):
 		#treat source as a filename
 		sourceType = source
@@ -37,6 +40,8 @@
 		print "Error in syncopation_barlist_permodel(): unrecognised source type."
 	
 	barsDiscarded=0
+	discardedlist = []
+	includedlist = []
 
 	if barlist!=None:
 		for bar in barlist:
@@ -50,8 +55,10 @@
 			if barSyncopation != None:
 				total += barSyncopation
 				numberOfNotes += sum(bar.get_binary_sequence())
+				includedlist.append(barlist.index(bar))
 			else:
 				barsDiscarded += 1
+				discardedlist.append(barlist.index(bar))
 				print 'Model could not measure bar %d, returning None.' % barlist.index(bar)
 
 		import WNBD
@@ -60,7 +67,16 @@
 
 		average = total / (len(barResults)-barsDiscarded)
 
- 	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}
+ 	return {
+ 			"model_name":model.__name__ , 
+ 			"summed_syncopation":total, 
+ 			"mean_syncopation_per_bar":average, 
+ 			"source":sourceType, 
+ 			"number_of_bars":len(barResults), 
+ 			"number_of_bars_not_measured":barsDiscarded, 
+ 			"bars_with_valid_output":includedlist, 
+ 			"syncopation_by_bar":barResults
+ 			}