csong@0: ''' csong@0: Author: Chunyang Song csong@0: Institution: Centre for Digital Music, Queen Mary University of London csong@0: csong@1: ''' csong@20: from basic_functions import repeat, get_note_indices csong@0: csong@2: # To find the product of multiple numbers csong@1: def cumu_multiply(numbers): csong@1: product = 1 csong@1: for n in numbers: csong@1: product = product*n csong@1: return product csong@0: csong@20: #def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq): csong@20: def get_syncopation(bar, parameters = None): csong@1: syncopation = None csong@1: csong@20: binarySequence = bar.get_binary_sequence() csong@20: sequenceLength = len(binarySequence) csong@20: subdivisionSequence = bar.get_subdivision_sequence() csong@20: strongBeatLevel = bar.get_beat_level() csong@20: nextbarBinarySequence = None csong@20: csong@20: if bar.get_next_bar() != None: csong@20: nextbarBinarySequence = bar.get_next_bar().get_binary_sequence() csong@20: csong@20: numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1]) # numberOfBeats is the number of strong beats csong@2: csong@20: if sequenceLength % numberOfBeats != 0: csong@20: print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision sequence.' csong@1: else: csong@1: # Find the indices of all the strong-beats csong@20: beatIndices = [] csong@20: beatInterval = sequenceLength / numberOfBeats csong@20: for i in range(numberOfBeats+1): csong@20: beatIndices.append(i*beatInterval) csong@20: if nextbarBinarySequence != None: # if there is a postbar_seq, add another two beats index for later calculation csong@20: beatIndices += [sequenceLength+beatInterval, sequenceLength+ 2* beatInterval] csong@0: csong@20: noteIndices = get_note_indices(binarySequence) # all the notes csong@0: csong@1: # Calculate the WNBD measure for each note csong@20: def measure_pernote(noteIndices, nextNoteIndex): csong@1: # Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) csong@1: j = 0 csong@20: while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]: csong@1: j = j + 1 csong@1: csong@1: # The distance of note to nearest beat normalised by the beat interval csong@20: distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval) csong@0: csong@1: # if this note is on-beat csong@20: if distanceToNearestBeat == 0: csong@1: measure = 0 csong@1: # or if this note is held on past the following beat, but ends on or before the later beat csong@20: elif beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]: csong@20: measure = float(2)/distanceToNearestBeat csong@1: else: csong@20: measure = float(1)/distanceToNearestBeat csong@1: return measure csong@0: csong@1: total = 0 csong@20: for i in range(len(noteIndices)): csong@20: # if this is the last note, end_time is the index of the following note in the next bar csong@20: if i == len(noteIndices)-1: csong@20: # if the next bar is not none or a bar of full rest, csong@20: # the nextNoteIndex is the sum of sequence length in the current bar and the noteIndex in the next bar csong@20: if nextbarBinarySequence != None and nextbarBinarySequence != repeat([0],len(nextbarBinarySequence)): csong@20: nextNoteIndex = get_note_indices(nextbarBinarySequence)[0]+sequenceLength csong@20: # else when the next bar is none or full rest, end_time is the end of this sequence. csong@20: else: csong@20: nextNoteIndex = sequenceLength csong@20: # else this is not the last note, the nextNoteIndex is the following element in the noteIndices list csong@0: else: csong@20: nextNoteIndex = noteIndices[i+1] csong@20: # sum up the syncopation value for individual note at noteIndices[i] csong@20: total += measure_pernote(noteIndices[i],nextNoteIndex) csong@0: csong@2: #syncopation = float(total) / len(note_indices) csong@0: csong@20: # return the total value, leave the normalisation done in the end csong@2: return total