annotate synpy/LHL.py @ 76:90b68f259541 tip

updated parameter_setter to be able to find the TimeSignature.pkl file without putting it in the pwd
author christopherh <christopher.harte@eecs.qmul.ac.uk>
date Wed, 13 May 2015 09:27:36 +0100
parents ef891481231e
children
rev   line source
christopher@45 1 '''
christopher@45 2 Author: Chunyang Song
christopher@45 3 Institution: Centre for Digital Music, Queen Mary University of London
christopher@45 4 '''
christopher@45 5
christopher@45 6 from basic_functions import concatenate, repeat, subdivide, ceiling, get_rhythm_category
christopher@45 7 from parameter_setter import are_parameters_valid
christopher@45 8
christopher@45 9
christopher@45 10
christopher@45 11
christopher@50 12 # Each terminal node contains two properties: its node type (note or rest) and its metrical weight.
christopher@45 13 class Node:
christopher@45 14 def __init__(self,nodeType,metricalWeight):
christopher@45 15 self.nodeType = nodeType
christopher@45 16 self.metricalWeight = metricalWeight
christopher@45 17
christopher@45 18 # This function will recurse the tree for a binary sequence and return a sequence containing the terminal nodes in time order.
christopher@50 19 def recursive_tree(binarySequence, subdivisionSequence, weightSequence, metricalWeight, level, Lmax):
christopher@45 20 # If matching to a Note type, add to terminal nodes
christopher@50 21 output = list()
christopher@45 22 if binarySequence == concatenate([1],repeat([0],len(binarySequence)-1)):
christopher@50 23 output.append(Node('N',metricalWeight))
christopher@45 24
christopher@45 25 # If matching to a Rest type, add to terminal nodes
christopher@45 26 elif binarySequence == repeat([0],len(binarySequence)):
christopher@50 27 output.append(Node('R',metricalWeight))
christopher@50 28
christopher@50 29 elif level+1 == Lmax:
christopher@50 30 print "WARNING: LHL tree recursion descended to Lmax, returning a note node but result will not be fully accurate. Check the rhythm pattern under test and/or specify larger Lmax to rectify the problem."
christopher@50 31 output.append(Node('N',metricalWeight))
christopher@45 32
christopher@45 33 # Keep subdividing by the subdivisor of the next level
christopher@50 34 else:
christopher@45 35 subBinarySequences = subdivide(binarySequence, subdivisionSequence[level+1])
christopher@45 36 subWeightSequences = concatenate([metricalWeight],repeat([weightSequence[level+1]],subdivisionSequence[level+1]-1))
christopher@45 37 for a in range(len(subBinarySequences)):
christopher@50 38 output = output + recursive_tree(subBinarySequences[a], subdivisionSequence, weightSequence, subWeightSequences[a], level+1, Lmax)
christopher@50 39
christopher@50 40 return output
christopher@45 41
christopher@45 42 def get_syncopation(bar, parameters = None):
christopher@45 43 syncopation = None
christopher@50 44 naughtyglobal = 0
christopher@45 45
christopher@45 46 binarySequence = bar.get_binary_sequence()
christopher@45 47 subdivisionSequence = bar.get_subdivision_sequence()
christopher@45 48
christopher@45 49 # LHL can only measure monorhythms
christopher@45 50 if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly':
christopher@45 51 print 'Warning: LHL model detects polyrhythms so returning None.'
christopher@50 52 elif bar.is_empty():
christopher@50 53 print 'LHL model detects empty bar so returning -1.'
christopher@50 54 syncopation = -1
christopher@45 55 else:
christopher@45 56 # set defaults
christopher@50 57 Lmax = 10
christopher@45 58 weightSequence = range(0,-Lmax-1,-1)
christopher@45 59 # if parameters are specified by users, check their validities and update parameters if valid
christopher@45 60 if parameters!= None:
christopher@45 61 if 'Lmax' in parameters:
christopher@45 62 Lmax = parameters['Lmax']
christopher@45 63 if 'W' in parameters:
christopher@45 64 weightSequence = parameters['W']
christopher@45 65
christopher@45 66 if not are_parameters_valid(Lmax, weightSequence, subdivisionSequence):
christopher@45 67 print 'Error: the given parameters are not valid.'
christopher@45 68 else:
christopher@50 69
christopher@50 70 # For the rhythm in the current bar, process its tree structure and store the terminal nodes
christopher@50 71 terminalNodes = recursive_tree(ceiling(binarySequence), subdivisionSequence, weightSequence, weightSequence[0],0, Lmax)
christopher@50 72
christopher@50 73 # save the terminal nodes on the current bar so that
christopher@50 74 # the next bar can access them...
christopher@50 75 bar.LHLterminalNodes = terminalNodes
christopher@50 76
christopher@50 77 # If there is rhythm in the previous bar and we've already processed it
christopher@45 78 prevbar = bar.get_previous_bar()
christopher@50 79 if prevbar != None and prevbar.is_empty() != True:
christopher@50 80 # get its LHL tree if it has one
christopher@50 81 try:
christopher@50 82 prevbarNodes = prevbar.LHLterminalNodes
christopher@50 83 except AttributeError:
christopher@50 84 prevbarNodes = []
christopher@50 85
christopher@50 86 # find the final note node in the previous bar:
christopher@50 87 if len(prevbarNodes)>0:
christopher@50 88 i = len(prevbarNodes) - 1
christopher@45 89 # Only keep the last note-type node
christopher@50 90 while prevbarNodes[i].nodeType != 'N' and i>=0:
christopher@50 91 i = i-1
christopher@50 92 # prepend the note to the terminal node list for this bar
christopher@50 93 terminalNodes = [ prevbarNodes[i] ] + terminalNodes
christopher@50 94
christopher@45 95
christopher@50 96 # Search for the NR pairs that contribute to syncopation, then add the weight-difference to the NRpairSyncopation list
christopher@45 97 NRpairSyncopation = []
christopher@45 98 for i in range(len(terminalNodes)-1,0,-1):
christopher@45 99 if terminalNodes[i].nodeType == 'R':
christopher@45 100 for j in range(i-1, -1, -1):
christopher@45 101 if (terminalNodes[j].nodeType == 'N') & (terminalNodes[i].metricalWeight >= terminalNodes[j].metricalWeight):
christopher@45 102 NRpairSyncopation.append(terminalNodes[i].metricalWeight - terminalNodes[j].metricalWeight)
christopher@45 103 break
christopher@45 104
christopher@45 105 # If there are syncopation, sum all the local syncopation values stored in NRpairSyncopation list
christopher@45 106 if len(NRpairSyncopation) != 0:
christopher@45 107 syncopation = sum(NRpairSyncopation)
christopher@45 108 # If no syncopation, the value is -1;
christopher@45 109 elif len(terminalNodes) != 0:
christopher@45 110 syncopation = -1
christopher@45 111
christopher@45 112 return syncopation
christopher@45 113
christopher@45 114