changeset 23:df1e7c378ee0

fixed KTH, and WNBD
author csong <csong@eecs.qmul.ac.uk>
date Sun, 12 Apr 2015 13:06:17 +0100
parents 2dbc09ca8013
children 08c298f47917 d9d22e6f396d
files Syncopation models/KTH.py Syncopation models/PRS.py Syncopation models/SG.py Syncopation models/TMC.py Syncopation models/TOB.py Syncopation models/WNBD.py Syncopation models/basic_functions.py Syncopation models/music_objects.py Syncopation models/syncopation.py
diffstat 9 files changed, 169 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/Syncopation models/KTH.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/KTH.py	Sun Apr 12 13:06:17 2015 +0100
@@ -4,7 +4,7 @@
 
 '''
 
-from basic_functions import get_note_indices, repeat, note_sequence_to_min_timespan
+from basic_functions import get_note_indices, repeat, note_sequence_to_min_timespan, velocity_sequence_to_min_timespan
 
 # To find the nearest power of 2 equal to or less than the given number
 def round_down_power_2(number):
@@ -49,26 +49,38 @@
 	else:
 		# retrieve note-sequence and next bar's note-sequence
 		noteSequence = bar.get_note_sequence()
+		#for note in noteSequence:
+		#	print note.to_string()
+		#print 'barlength',bar.get_bar_ticks()
+
 		nextbarNoteSequence = None
 		if bar.get_next_bar() != None:
 			nextbarNoteSequence = bar.get_next_bar().get_note_sequence()
 
 		# convert note sequence to its minimum time-span representation so that the later calculation can be faster
-		noteSequence = note_sequence_to_min_timespan(noteSequence)
+		# noteSequence = note_sequence_to_min_timespan(noteSequence)
+		# find delta_t 
+		Tmin = len(velocity_sequence_to_min_timespan(bar.get_velocity_sequence()))
+		#print 'Tmin',Tmin
+		T = round_up_power_2(Tmin)
+		#print 'T',T
+		deltaT = float(bar.get_bar_ticks())/T
+		#print 'delta',deltaT
+
 
 		# calculate syncopation note by note
 		syncopation = 0
 
 		for note in noteSequence:
-			startTime = note[0]
-			duration = note[1]
-			endTime = startTime + duration
-			c_n = round_down_power_2(duration)
+			c_n = round_down_power_2(note.duration/deltaT)
+			#print 'd', note.duration
+			#print 'c_n', c_n
+			endTime = note.startTime + note.duration
+			#print float(note.startTime)/deltaT, float(endTime)/deltaT
+			syncopation = syncopation + start_time_offbeat_measure(float(note.startTime)/deltaT,c_n) + end_time_offbeat_measure(float(endTime)/deltaT,c_n)
 
-			syncopation = syncopation + start_time_offbeat_measure(startTime,c_n) + end_time_offbeat_measure(endTime,c_n)
 
-
-
+	return syncopation
 
 # # To calculate syncopation value of the sequence in the given time-signature.
 # def get_syncopation(seq, timesig, postbar_seq):
--- a/Syncopation models/PRS.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/PRS.py	Sun Apr 12 13:06:17 2015 +0100
@@ -3,10 +3,10 @@
 Institution: Centre for Digital Music, Queen Mary University of London
 '''
 
-from basic_functions import repeat, subdivide, ceiling, get_min_timeSpan, get_rhythm_category
+from basic_functions import repeat, subdivide, ceiling, velocity_sequence_to_min_timespan, get_rhythm_category
 
 def get_cost(sequence,nextSequence):
-	sequence = get_min_timeSpan(sequence)					# converting to the minimum time-span format
+	sequence = velocity_sequence_to_min_timespan(sequence)					# converting to the minimum time-span format
 	
 	if sequence[1:] == repeat([0],len(sequence)-1):		# null prototype
 		cost = 0
--- a/Syncopation models/SG.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/SG.py	Sun Apr 12 13:06:17 2015 +0100
@@ -4,7 +4,7 @@
 
 '''
 
-from basic_functions import get_H, get_min_timeSpan, get_rhythm_category
+from basic_functions import get_H, velocity_sequence_to_min_timespan, get_rhythm_category
 from TMC import find_L
 
 #def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category):
@@ -16,7 +16,7 @@
 	if get_rhythm_category(velocitySequence, subdivisionSequence) == 'poly':
 		print 'Warning: SG model detects polyrhythms so returning None.'
 	else:
-		velocitySequence = get_min_timeSpan(velocitySequence)	# converting to the minimum time-span format
+		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:
--- a/Syncopation models/TMC.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/TMC.py	Sun Apr 12 13:06:17 2015 +0100
@@ -4,7 +4,7 @@
 
 '''
 
-from basic_functions import get_H, ceiling, get_min_timeSpan, get_rhythm_category
+from basic_functions import get_H, ceiling, velocity_sequence_to_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(binarySequence, H):
@@ -55,7 +55,7 @@
 	if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly':
 		print 'Warning: TMC model detects polyrhythms so returning None.'
 	else:
-		binarySequence = get_min_timeSpan(binarySequence)	# converting to the minimum time-span format
+		binarySequence = velocity_sequence_to_min_timespan(binarySequence)	# converting to the minimum time-span format
 
 		# If the parameters are not given, use the default settings
 		if parameters == None:
--- a/Syncopation models/TOB.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/TOB.py	Sun Apr 12 13:06:17 2015 +0100
@@ -4,7 +4,7 @@
 
 '''
 
-from basic_functions import ceiling, find_divisor, is_prime, get_min_timeSpan
+from basic_functions import ceiling, find_divisor, is_prime
 
 def get_syncopation(bar, parameter = None):
 	binarySequence = bar.get_binary_sequence()
--- a/Syncopation models/WNBD.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/WNBD.py	Sun Apr 12 13:06:17 2015 +0100
@@ -12,72 +12,117 @@
 		product = product*n
 	return product
 
-#def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq):
 def get_syncopation(bar, parameters = None):
 	syncopation = None
 	
-	binarySequence = bar.get_binary_sequence()
-	sequenceLength = len(binarySequence)
+	noteSequence = bar.get_note_sequence()
+	barTicks = bar.get_bar_ticks()
 	subdivisionSequence = bar.get_subdivision_sequence()
 	strongBeatLevel = bar.get_beat_level()
-	nextbarBinarySequence = None
+	
+	nextbarNoteSequence = None
+	if bar.get_next_bar() != None:
+		nextbarNoteSequence = bar.get_next_bar().get_note_sequence()
 
-	if bar.get_next_bar() != None:
-		nextbarBinarySequence = bar.get_next_bar().get_binary_sequence()
+	# calculate each strong beat ticks
+	numberOfBeats = cumu_multiply(subdivisionSequence[:strongBeatLevel+1])
+	beatIntervalTicks = barTicks/numberOfBeats
+	# beatsTicks represents the ticks for all the beats in the current bar and the first two beats in the next bar
+	beatsTicks = [i*beatIntervalTicks for i in range(numberOfBeats+2)] 
+	#print beatsTicks
+	totalSyncopation = 0
+	for note in noteSequence:
+	#	print note.to_string()
+		# find such beatIndex such that note.startTime is located between (including) beatsTicks[beatIndex] and (not including) beatsTicks[beatIndex+1]
+		beatIndex = 0
+		while note.startTime < beatsTicks[beatIndex] or note.startTime >= beatsTicks[beatIndex+1]:
+			beatIndex += 1
 
-	numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1])	# numberOfBeats is the number of strong beats
+	#	print beatIndex
+		# calculate the distance of this note to its nearest beat
+		distanceToBeatOnLeft = abs(note.startTime - beatsTicks[beatIndex])/float(beatIntervalTicks)
+		distanceToBeatOnRight = abs(note.startTime - beatsTicks[beatIndex+1])/float(beatIntervalTicks)
+		distanceToNearestBeat = min(distanceToBeatOnLeft,distanceToBeatOnRight)
+	#	print distanceToNearestBeat
+
+		# calculate the WNBD measure for this note, and add to total syncopation value for this bar
+		if distanceToNearestBeat == 0:	
+			totalSyncopation += 0
+		# or if this note is held on past the following beat, but ends on or before the later beat  
+		elif beatsTicks[beatIndex+1] < note.startTime+note.duration <= beatsTicks[beatIndex+2]:
+			totalSyncopation += float(2)/distanceToNearestBeat
+		else:
+			totalSyncopation += float(1)/distanceToNearestBeat
+	#	print totalSyncopation
+
+	return totalSyncopation
+
+#def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq):
+# def get_syncopation(bar, parameters = None):
+# 	syncopation = None
 	
-	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
-		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]
+# 	binarySequence = bar.get_binary_sequence()
+# 	sequenceLength = len(binarySequence)
+# 	subdivisionSequence = bar.get_subdivision_sequence()
+# 	strongBeatLevel = bar.get_beat_level()
+# 	nextbarBinarySequence = None
 
-		noteIndices = get_note_indices(binarySequence)	# all the notes
+# 	if bar.get_next_bar() != None:
+# 		nextbarBinarySequence = bar.get_next_bar().get_binary_sequence()
 
-		# Calculate the WNBD measure for each note
-		def measure_pernote(noteIndices, nextNoteIndex):
-			# Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) 
-			j = 0
-			while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]:
-				j = j + 1
+# 	numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1])	# numberOfBeats is the number of strong beats
+	
+# 	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
+# 		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]
+
+# 		noteIndices = get_note_indices(binarySequence)	# all the notes
+
+# 		# Calculate the WNBD measure for each note
+# 		def measure_pernote(noteIndices, nextNoteIndex):
+# 			# Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) 
+# 			j = 0
+# 			while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]:
+# 				j = j + 1
 			
-			# The distance of note to nearest beat normalised by the beat interval
-			distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval)
+# 			# The distance of note to nearest beat normalised by the beat interval
+# 			distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval)
 
-			# if this note is on-beat
-			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 beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]:
-				measure = float(2)/distanceToNearestBeat
-			else:
-				measure = float(1)/distanceToNearestBeat
-			return measure
+# 			# if this note is on-beat
+# 			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 beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]:
+# 				measure = float(2)/distanceToNearestBeat
+# 			else:
+# 				measure = float(1)/distanceToNearestBeat
+# 			return measure
 
-		total = 0
-		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:
-				nextNoteIndex = noteIndices[i+1]
-			# sum up the syncopation value for individual note at noteIndices[i]
-			total += measure_pernote(noteIndices[i],nextNoteIndex)
+# 		total = 0
+# 		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:
+# 				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)
+# 		#syncopation = float(total) / len(note_indices)
 
-	# return the total value, leave the normalisation done in the end
-	return total
+# 	# return the total value, leave the normalisation done in the end
+# 	return total
--- a/Syncopation models/basic_functions.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/basic_functions.py	Sun Apr 12 13:06:17 2015 +0100
@@ -80,12 +80,12 @@
 	return isPrime
 
 # convert a velocity sequence to its minimum time-span representation
-def velocity_sequence_to_min_timetpan(velocitySequence):
+def velocity_sequence_to_min_timespan(velocitySequence):
 	minTimeSpanVelocitySeq = [1]
 	for divisors in find_divisor(len(velocitySequence)):
 		segments = subdivide(velocitySequence,divisors)
 		if len(segments)!=0:
-			del minTimeSpanSequence[:]
+			del minTimeSpanVelocitySeq[:]
 			for s in segments:
 				minTimeSpanVelocitySeq.append(s[0])
 			if sum(minTimeSpanVelocitySeq) == sum(velocitySequence):
@@ -93,21 +93,27 @@
 	return minTimeSpanVelocitySeq
 
 # convert a note sequence to its minimum time-span representation
-def note_sequence_to_min_timespan(noteSequence, barTicks):
-	barBinaryArray = [0]*barTicks
+def note_sequence_to_min_timespan(noteSequence):
+	from music_objects import note_sequence_to_velocity_sequence
+	timeSpanTicks = len(note_sequence_to_velocity_sequence(noteSequence))
+#	print timeSpanTicks
+
+	barBinaryArray = [0]*(timeSpanTicks+1)
 	for note in noteSequence:
 		# mark note_on event (i.e. startTime) and note_off event (i.e. endTime = startTime + duration) as 1 in the barBinaryArray
-		barBinaryArray[note[0]] = 1
-		barBinaryArray[note[0]+note[1]] = 1
+		barBinaryArray[note.startTime] = 1
+		barBinaryArray[note.startTime + note.duration] = 1
 
 	# convert the barBinaryArray to its minimum time-span representation
-	minBarBinaryArray = velocitySequence_to_min_timeSpan(barBinaryArray)
+	minBarBinaryArray = velocity_sequence_to_min_timetpan(barBinaryArray[:-1])
+	print barBinaryArray
+	print minBarBinaryArray
 	delta_t = len(barBinaryArray)/len(minBarBinaryArray)
 
 	# scale the startTime and duration of each note by delta_t
 	for note in noteSequence:
-		note[0] = note[0]/delta_t
-		note[1] = note[1]/delta_t
+		note.startTime = note.startTime/delta_t
+		note.duration = note.duration/delta_t
 
 	return noteSequence
 
--- a/Syncopation models/music_objects.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/music_objects.py	Sun Apr 12 13:06:17 2015 +0100
@@ -61,7 +61,7 @@
 		return str(self)[1:-1].replace(" ","")
 
 
-def velocity_sequence_to_note_sequence(velocitySequence):
+def velocity_sequence_to_note_sequence(velocitySequence, nextbarVelocitySequence = None):
 	
 	noteSequence = NoteSequence()
 
@@ -80,8 +80,14 @@
 
 	# to set the duration for the last note
 	if( len(noteSequence) > 0):
-		previousNote = noteSequence[-1]
-		previousNote.duration = len(velocitySequence) - previousNote.startTime
+		lastNote = noteSequence[-1]
+		
+		if nextbarVelocitySequence == None:
+			lastNote.duration = len(velocitySequence) - lastNote.startTime
+		else:
+			nextNoteStartTime = next((index for index, v in enumerate(nextbarVelocitySequence) if v), None)
+			lastNote.duration = len(velocitySequence) + nextNoteStartTime-lastNote.startTime
+
 
 	return noteSequence
 
@@ -120,7 +126,6 @@
 
 
 class Bar:
-
 	def __init__(self, rhythmSequence, timeSignature, ticksPerQuarter=None, qpmTempo=None, nextBar=None, prevBar=None):
 		if isinstance(rhythmSequence, NoteSequence):
 			self.noteSequence = rhythmSequence
@@ -137,12 +142,15 @@
 
 	def get_note_sequence(self):
 		if self.noteSequence == None:
-			self.noteSequence = velocitySequence_to_noteSequence(self.velocitySequence)
+			nextbarVelocitySequence = None
+			if self.nextBar != None:
+				nextbarVelocitySequence = self.nextBar.get_velocity_sequence()
+			self.noteSequence = velocity_sequence_to_note_sequence(self.velocitySequence, nextbarVelocitySequence)
 		return self.noteSequence
 
 	def get_velocity_sequence(self):
 		if self.velocitySequence == None:
-			self.velocitySequence = noteSequence_to_velocitySequence(self.noteSequence)
+			self.velocitySequence = note_sequence_to_velocity_sequence(self.noteSequence)
 		return self.velocitySequence
 
 	def get_binary_sequence(self):
@@ -171,7 +179,7 @@
 
 	# return the length of a bar in time units (ticks)
 	def get_bar_ticks(self):
-		return calculate_bar_ticks(self.timeSignature.get_numerator(),self.timeSignature.get_denominator(), self.ticksPerQuarter)
+		return calculate_bar_ticks(self.timeSignature.get_numerator(),self.timeSignature.get_denominator(), self.tpq)
 
 
 class TimeSignature():
--- a/Syncopation models/syncopation.py	Thu Apr 09 23:49:16 2015 +0100
+++ b/Syncopation models/syncopation.py	Sun Apr 12 13:06:17 2015 +0100
@@ -7,7 +7,18 @@
 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, barlist, parameters):
+ 	total = 0
+ 	numberOfNotes = 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 model is WNBD:
+ 		total = (float) 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