view Syncopation models/music_objects.py @ 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
line wrap: on
line source

from basic_functions import ceiling, string_to_sequence, calculate_bar_ticks
import parameter_setter 
import rhythm_parser 

class Note():
	def __init__(self, firstarg = None, duration = None, velocity = None):
		self.startTime = 0
		self.duration = 0
		self.velocity = 0

		if firstarg != None:
			if isinstance(firstarg,basestring):
				intlist = string_to_sequence(firstarg)
				self.startTime = intlist[0]
				self.duration = intlist[1]
				self.velocity = intlist[2]
			elif isinstance(firstarg,int):
				self.startTime = firstarg

		if duration != None:
			self.duration = duration
		if velocity != None:
			self.velocity = velocity


	def to_string(self):
		return "(%d,%d,%f)" %(self.startTime, self.duration, self.velocity)


# NoteSequence is a list of Note
class NoteSequence(list):
	def __init__(self, noteSequenceString = None):
		if noteSequenceString != None:
			self.string_to_note_sequence(noteSequenceString)

	def string_to_note_sequence(self, noteSequenceString):
		noteSequenceString = rhythm_parser.discardSpaces(noteSequenceString)
		# try:
			# Turning "(1,2,3),(4,5,6),(7,8,9)" into ["1,2,3","4,5,6,","7,8,9"]
		listStrings = noteSequenceString[1:-1].split("),(")
		for localString in listStrings:
			self.append(Note(localString))

	def to_string(self):
		noteSequenceString = ""
		for note in self:
			noteSequenceString += note.to_string() + ","
		return noteSequenceString[:-1]

# VelocitySequence is a list of float numbers
class VelocitySequence(list):
	def __init__(self, velocitySequenceString = None):
		if velocitySequenceString != None:
			self.string_to_velocity_sequence(velocitySequenceString)

	def string_to_velocity_sequence(self,inputString):
		self.extend(string_to_sequence(inputString))

	def to_string(self):
		return str(self)[1:-1].replace(" ","")


def velocity_sequence_to_note_sequence(velocitySequence, nextbarVelocitySequence = None):
	
	noteSequence = NoteSequence()

	for index in range(len(velocitySequence)):
		if (velocitySequence[index]!= 0): # onset detected
			startTime = index			
			velocity = velocitySequence[index]

			# if there are previous notes added
			if( len(noteSequence) > 0):
				previousNote = noteSequence[-1]
				previousNote.duration = startTime - previousNote.startTime

			# add the current note into note sequence
			noteSequence.append( Note(startTime, 0, velocity) )

	# to set the duration for the last note
	if( len(noteSequence) > 0):
		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


def note_sequence_to_velocity_sequence(noteSequence, timespanTicks = None):
	
	velocitySequence = VelocitySequence()
	
	previousNoteStartTime = -1

	for note in noteSequence:
		
		interOnsetInterval = note.startTime - previousNoteStartTime	
		velocitySequence += [0]*(interOnsetInterval-1)	
		velocitySequence += [note.velocity]

		previousNoteStartTime = note.startTime

	if timespanTicks!=None:
		velocitySequence += [0]*(timespanTicks - len(velocitySequence))
	else:
		velocitySequence += [0]*(noteSequence[-1].duration-1)

	# normalising velocity sequence between 0-1
	velocitySequence = [float(v)/max(velocitySequence) for v in velocitySequence]

	return velocitySequence


class BarList(list):
	def append(self,bar):
		if(len(self)>0):
			bar.set_previous_bar(self[-1])
			self[-1].set_next_bar(bar)
		super(BarList, self).append(bar)


class Bar:
	def __init__(self, rhythmSequence, timeSignature, ticksPerQuarter=None, qpmTempo=None, nextBar=None, prevBar=None):
		if isinstance(rhythmSequence, NoteSequence):
			self.noteSequence = rhythmSequence
			self.velocitySequence = None 
		elif isinstance(rhythmSequence, VelocitySequence):
			self.velocitySequence = rhythmSequence
			self.noteSequence = None 

		self.tpq = ticksPerQuarter
		self.qpm = qpmTempo
		self.timeSignature = TimeSignature(timeSignature)
		self.nextBar = nextBar
		self.prevBar = prevBar

	def get_note_sequence(self):
		if self.noteSequence == None:
			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 = note_sequence_to_velocity_sequence(self.noteSequence)
		return self.velocitySequence

	def get_binary_sequence(self):
		return ceiling(self.get_velocity_sequence())

	def get_next_bar(self):
		return self.nextBar

	def get_previous_bar(self):
		return self.prevBar

	def set_next_bar(self, bar):
		self.nextBar = bar

	def set_previous_bar(self, bar):
		self.prevBar = bar		

	def get_subdivision_sequence(self):
		return self.timeSignature.get_subdivision_sequence()

	def get_beat_level(self):
		return self.timeSignature.get_beat_level()

	def get_time_signature(self):
		return self.timeSignature

	# 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.tpq)


class TimeSignature():
	def __init__(self, inputString):
		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.timeSignatureBase[self.tsString][0]
	
	def get_beat_level(self):
		return parameter_setter.timeSignatureBase[self.tsString][1]

	def get_numerator(self):
		return int(self.tsString.split('/')[0])
			
	def get_denominator(self):
		return int(self.tsString.split('/')[1])