Mercurial > hg > syncopation-dataset
view Syncopation models/LHL.py @ 0:76ce27beba95
Have uploaded the syncopation dataset and audio wavefies.
author | Chunyang Song <csong@eecs.qmul.ac.uk> |
---|---|
date | Fri, 21 Mar 2014 15:49:46 +0000 |
parents | |
children | b2da092dc2e0 |
line wrap: on
line source
''' Author: Chunyang Song Institution: Centre for Digital Music, Queen Mary University of London ** Longuet-Higgins and Lee's Model (LHL) ** Algorithm: Only applicable to simple rhtyhms. Generate tree structure: 1) Time_signature decides sequence of subdivisions: 4/4 - [2,2,2,...2], 3/4 - [3,2,2,...2], 6/8 - [2,3,2,2...2]; 2) If after subdivision, a node has at least one branch that is full rest or one note, then it cannot be subdivided; 3) Weight of node, initialized as 0, decrease by 1 with increasing depth of the tree. Assign each node in the tree three attributes - weight, pos, event; Search for Note-Rest pairs that Note's weight is no more than Rest's weight; Syncopation for such a pair is the difference in weights; Total Syncopation value for non-syncopation rhythms is -1; for syncopated rhythms is the sum of syncopation of all notes. In order to evaluate LHL models with the others, re-scale LHL's measures to non-negative. Apart from the immeasuable rhythms, add 1 to each syncopation value. ''' from MeterStructure import MeterStructure class Node: def __init__(self, pos, event): self.pos = pos self.event = event def assignWeight(self, time_sig): ms = MeterStructure(time_sig) # Retrieve the LHL meter hierarchy in one bar; #(e.g. weight = [0, -4, -3, -4, -2, -4, -3, -4, -1, -4, -3, -4, -2, -4, -3, -4] in 4/4 meter) weights = ms.getLHLWeights(1) # find the metrical weight of the note that locates at certain position(self.pos) # index is the corresponding position of "self.pos" in the "weights" list index = int(((abs(self.pos)%48)/48.0)*len(weights)) self.weight = weights[index] class Tree: def __init__(self): self.nodes = [] def add(self, Node): self.nodes.append (Node) def getWeight(self, time_sig): for n in self.nodes: n.assignWeight(time_sig) def display(self): print 'Pos, Event, Weight' for n in self.nodes: print '%02d %s %02d' % (n.pos, n.event, n.weight) def subdivide(sequence, segments_num): subSeq = [] if len(sequence) % segments_num != 0: print 'Error: rhythm segment cannot be equally subdivided.' else: n = len(sequence) / segments_num start , end = 0, n for i in range(segments_num): subSeq.append(sequence[start : end]) start = end end = end + n return subSeq def eventType (sequence): if not(1 in sequence): event = 'R' # Full rest elif sequence[0] == 1 and not(1 in sequence[1:]): event = 'N' # Only one on-beat note else: event = 'D' # Divisable return event def splitInto2 (sequence, tree, pos, isRecursive): subs = subdivide(sequence, 2) for s in subs: if eventType(s) == 'R' or eventType(s) == 'N': #print 'test', s, eventType(s), pos tree.add( Node(pos, eventType(s)) ) else: if isRecursive: #print 'test', s, eventType(s), pos splitInto2 (s, tree, pos, True) else: splitInto3 (s, tree, pos) pos = pos + len(sequence) / 2 return tree def splitInto3 (sequence, tree, pos): subs = subdivide(sequence, 3) for s in subs: if eventType(s) == 'R' or eventType(s) == 'N': #print 'test', s, eventType(s), pos tree.add( Node(pos, eventType(s)) ) else: splitInto2 (s, tree, pos, True) pos = pos + len(sequence) / 3 return tree def createTree(rhythm, time_sig, bar): t = Tree() # The root is the rhtyhm in a entire bar, has weight 0, at position 0 weight, pos, event = 0 , 0, eventType(rhythm) root = Node (pos, event) if '2/4' in time_sig: t.add ( Node(-24, 'N') ) # Treat the last metronome beat in the previous bar as a sounded note if event == 'D': t = splitInto2(rhythm, t, pos, True) else: t.add (root) elif '4/4' in time_sig: t.add ( Node(-12, 'N') ) # Treat the last metronome beat in the previous bar as a sounded note if event == 'D': t = splitInto2(rhythm, t, pos, True) else: t.add (root) elif '3/4' in time_sig: t.add ( Node(-8, 'N') ) # Treat the last metronome beat in the previous bar as a sounded note segments = subdivide (rhythm, bar) for s in segments: if eventType(s) == 'D': t = splitInto3(s, t, pos) pos = pos + len(rhythm)/bar elif '6/8' in time_sig: t.add ( Node(-8, 'N') ) # Treat the last metronome beat in the previous bar as a sounded note segments = subdivide (rhythm, bar) for s in segments: if eventType(s) == 'D': t = splitInto2(s, t, pos, False) pos = pos + len(rhythm)/bar else: print 'This time signature is not defined. Choose between 4/4, 3/4 or 6/8' return t def lhl(rhythm, time_sig, category, bar): measures = [] if 'poly' in category: return -1 else: tree = createTree(rhythm, time_sig, bar) tree.getWeight(time_sig) #tree.display() # find NR-pair that N's weight <= R's weight, add difference in weight to measures[] N_weight = tree.nodes[0].weight size = len(tree.nodes) for i in range(1, size): if tree.nodes[i].event == 'N': N_weight = tree.nodes[i].weight if tree.nodes[i].event == 'R' and tree.nodes[i].weight >= N_weight: measures.append(tree.nodes[i].weight - N_weight ) # Calculate syncopation if len(measures) == 0: # syncopation of non-syncopated rhythm is -1 syncopation = -1 else: syncopation = sum(measures) # syncopation of syncopated rhythm is the sum of measures[] return syncopation + 1 # For evaluation purpose, scale up by 1 to make scores above 0 # Retrieve the stimuli #f = file('stimuli.txt') f = file('stimuli_34only.txt') #Calculate syncopation for each rhythm pattern while True: line = f.readline().split(';') if len(line) == 1: break else: sti_name = line[0] rhythmString = line[1].split() time_sig = line[2] category = line[3] bar = int(line[4]) rhythm = map(int,rhythmString[0].split(',')) print sti_name, lhl(rhythm, time_sig, category, bar)