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