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(bar, parameters = None): csong@1: syncopation = None csong@1: csong@23: noteSequence = bar.get_note_sequence() csong@23: barTicks = bar.get_bar_ticks() csong@20: subdivisionSequence = bar.get_subdivision_sequence() csong@20: strongBeatLevel = bar.get_beat_level() csong@23: csong@23: nextbarNoteSequence = None csong@23: if bar.get_next_bar() != None: csong@23: nextbarNoteSequence = bar.get_next_bar().get_note_sequence() csong@20: csong@23: # calculate each strong beat ticks csong@23: numberOfBeats = cumu_multiply(subdivisionSequence[:strongBeatLevel+1]) csong@23: beatIntervalTicks = barTicks/numberOfBeats csong@23: # beatsTicks represents the ticks for all the beats in the current bar and the first two beats in the next bar csong@23: beatsTicks = [i*beatIntervalTicks for i in range(numberOfBeats+2)] csong@23: #print beatsTicks csong@23: totalSyncopation = 0 csong@23: for note in noteSequence: csong@23: # print note.to_string() csong@23: # find such beatIndex such that note.startTime is located between (including) beatsTicks[beatIndex] and (not including) beatsTicks[beatIndex+1] csong@23: beatIndex = 0 csong@23: while note.startTime < beatsTicks[beatIndex] or note.startTime >= beatsTicks[beatIndex+1]: csong@23: beatIndex += 1 csong@20: csong@23: # print beatIndex csong@23: # calculate the distance of this note to its nearest beat csong@23: distanceToBeatOnLeft = abs(note.startTime - beatsTicks[beatIndex])/float(beatIntervalTicks) csong@23: distanceToBeatOnRight = abs(note.startTime - beatsTicks[beatIndex+1])/float(beatIntervalTicks) csong@23: distanceToNearestBeat = min(distanceToBeatOnLeft,distanceToBeatOnRight) csong@23: # print distanceToNearestBeat csong@23: csong@23: # calculate the WNBD measure for this note, and add to total syncopation value for this bar csong@23: if distanceToNearestBeat == 0: csong@23: totalSyncopation += 0 csong@23: # or if this note is held on past the following beat, but ends on or before the later beat csong@23: elif beatsTicks[beatIndex+1] < note.startTime+note.duration <= beatsTicks[beatIndex+2]: csong@23: totalSyncopation += float(2)/distanceToNearestBeat csong@23: else: csong@23: totalSyncopation += float(1)/distanceToNearestBeat csong@23: # print totalSyncopation csong@23: csong@23: return totalSyncopation csong@23: csong@23: #def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq): csong@23: # def get_syncopation(bar, parameters = None): csong@23: # syncopation = None csong@2: csong@23: # binarySequence = bar.get_binary_sequence() csong@23: # sequenceLength = len(binarySequence) csong@23: # subdivisionSequence = bar.get_subdivision_sequence() csong@23: # strongBeatLevel = bar.get_beat_level() csong@23: # nextbarBinarySequence = None csong@0: csong@23: # if bar.get_next_bar() != None: csong@23: # nextbarBinarySequence = bar.get_next_bar().get_binary_sequence() csong@0: csong@23: # numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1]) # numberOfBeats is the number of strong beats csong@23: csong@23: # if sequenceLength % numberOfBeats != 0: csong@23: # print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision sequence.' csong@23: # else: csong@23: # # Find the indices of all the strong-beats csong@23: # beatIndices = [] csong@23: # beatInterval = sequenceLength / numberOfBeats csong@23: # for i in range(numberOfBeats+1): csong@23: # beatIndices.append(i*beatInterval) csong@23: # if nextbarBinarySequence != None: # if there is a postbar_seq, add another two beats index for later calculation csong@23: # beatIndices += [sequenceLength+beatInterval, sequenceLength+ 2* beatInterval] csong@23: csong@23: # noteIndices = get_note_indices(binarySequence) # all the notes csong@23: csong@23: # # Calculate the WNBD measure for each note csong@23: # def measure_pernote(noteIndices, nextNoteIndex): csong@23: # # Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) csong@23: # j = 0 csong@23: # while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]: csong@23: # j = j + 1 csong@1: csong@23: # # The distance of note to nearest beat normalised by the beat interval csong@23: # distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval) csong@0: csong@23: # # if this note is on-beat csong@23: # if distanceToNearestBeat == 0: csong@23: # measure = 0 csong@23: # # or if this note is held on past the following beat, but ends on or before the later beat csong@23: # elif beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]: csong@23: # measure = float(2)/distanceToNearestBeat csong@23: # else: csong@23: # measure = float(1)/distanceToNearestBeat csong@23: # return measure csong@0: csong@23: # total = 0 csong@23: # for i in range(len(noteIndices)): csong@23: # # if this is the last note, end_time is the index of the following note in the next bar csong@23: # if i == len(noteIndices)-1: csong@23: # # if the next bar is not none or a bar of full rest, csong@23: # # the nextNoteIndex is the sum of sequence length in the current bar and the noteIndex in the next bar csong@23: # if nextbarBinarySequence != None and nextbarBinarySequence != repeat([0],len(nextbarBinarySequence)): csong@23: # nextNoteIndex = get_note_indices(nextbarBinarySequence)[0]+sequenceLength csong@23: # # else when the next bar is none or full rest, end_time is the end of this sequence. csong@23: # else: csong@23: # nextNoteIndex = sequenceLength csong@23: # # else this is not the last note, the nextNoteIndex is the following element in the noteIndices list csong@23: # else: csong@23: # nextNoteIndex = noteIndices[i+1] csong@23: # # sum up the syncopation value for individual note at noteIndices[i] csong@23: # total += measure_pernote(noteIndices[i],nextNoteIndex) csong@0: csong@23: # #syncopation = float(total) / len(note_indices) csong@0: csong@23: # # return the total value, leave the normalisation done in the end csong@23: # return total