# HG changeset patch # User csong # Date 1428840377 -3600 # Node ID df1e7c378ee00ebd72c3a79f0ae59882996ea8f8 # Parent 2dbc09ca80136d6ddaf066e54a26dbfd400ab6b9 fixed KTH, and WNBD diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/KTH.py --- a/Syncopation models/KTH.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/KTH.py Sun Apr 12 13:06:17 2015 +0100 @@ -4,7 +4,7 @@ ''' -from basic_functions import get_note_indices, repeat, note_sequence_to_min_timespan +from basic_functions import get_note_indices, repeat, note_sequence_to_min_timespan, velocity_sequence_to_min_timespan # To find the nearest power of 2 equal to or less than the given number def round_down_power_2(number): @@ -49,26 +49,38 @@ else: # retrieve note-sequence and next bar's note-sequence noteSequence = bar.get_note_sequence() + #for note in noteSequence: + # print note.to_string() + #print 'barlength',bar.get_bar_ticks() + nextbarNoteSequence = None if bar.get_next_bar() != None: nextbarNoteSequence = bar.get_next_bar().get_note_sequence() # convert note sequence to its minimum time-span representation so that the later calculation can be faster - noteSequence = note_sequence_to_min_timespan(noteSequence) + # noteSequence = note_sequence_to_min_timespan(noteSequence) + # find delta_t + Tmin = len(velocity_sequence_to_min_timespan(bar.get_velocity_sequence())) + #print 'Tmin',Tmin + T = round_up_power_2(Tmin) + #print 'T',T + deltaT = float(bar.get_bar_ticks())/T + #print 'delta',deltaT + # calculate syncopation note by note syncopation = 0 for note in noteSequence: - startTime = note[0] - duration = note[1] - endTime = startTime + duration - c_n = round_down_power_2(duration) + c_n = round_down_power_2(note.duration/deltaT) + #print 'd', note.duration + #print 'c_n', c_n + endTime = note.startTime + note.duration + #print float(note.startTime)/deltaT, float(endTime)/deltaT + syncopation = syncopation + start_time_offbeat_measure(float(note.startTime)/deltaT,c_n) + end_time_offbeat_measure(float(endTime)/deltaT,c_n) - syncopation = syncopation + start_time_offbeat_measure(startTime,c_n) + end_time_offbeat_measure(endTime,c_n) - - + return syncopation # # To calculate syncopation value of the sequence in the given time-signature. # def get_syncopation(seq, timesig, postbar_seq): diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/PRS.py --- a/Syncopation models/PRS.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/PRS.py Sun Apr 12 13:06:17 2015 +0100 @@ -3,10 +3,10 @@ Institution: Centre for Digital Music, Queen Mary University of London ''' -from basic_functions import repeat, subdivide, ceiling, get_min_timeSpan, get_rhythm_category +from basic_functions import repeat, subdivide, ceiling, velocity_sequence_to_min_timespan, get_rhythm_category def get_cost(sequence,nextSequence): - sequence = get_min_timeSpan(sequence) # converting to the minimum time-span format + sequence = velocity_sequence_to_min_timespan(sequence) # converting to the minimum time-span format if sequence[1:] == repeat([0],len(sequence)-1): # null prototype cost = 0 diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/SG.py --- a/Syncopation models/SG.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/SG.py Sun Apr 12 13:06:17 2015 +0100 @@ -4,7 +4,7 @@ ''' -from basic_functions import get_H, get_min_timeSpan, get_rhythm_category +from basic_functions import get_H, velocity_sequence_to_min_timespan, get_rhythm_category from TMC import find_L #def get_syncopation(seq, subdivision_seq, weight_seq, L_max, rhythm_category): @@ -16,7 +16,7 @@ if get_rhythm_category(velocitySequence, subdivisionSequence) == 'poly': print 'Warning: SG model detects polyrhythms so returning None.' else: - velocitySequence = get_min_timeSpan(velocitySequence) # converting to the minimum time-span format + velocitySequence = velocity_sequence_to_min_timespan(velocitySequence) # converting to the minimum time-span format # If the parameters are not given, use the default settings if parameters == None: diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/TMC.py --- a/Syncopation models/TMC.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/TMC.py Sun Apr 12 13:06:17 2015 +0100 @@ -4,7 +4,7 @@ ''' -from basic_functions import get_H, ceiling, get_min_timeSpan, get_rhythm_category +from basic_functions import get_H, ceiling, velocity_sequence_to_min_timespan, get_rhythm_category # The get_metricity function calculates the metricity for a binary sequence with given sequence of metrical weights in a certain metrical level. def get_metricity(binarySequence, H): @@ -55,7 +55,7 @@ if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly': print 'Warning: TMC model detects polyrhythms so returning None.' else: - binarySequence = get_min_timeSpan(binarySequence) # converting to the minimum time-span format + binarySequence = velocity_sequence_to_min_timespan(binarySequence) # converting to the minimum time-span format # If the parameters are not given, use the default settings if parameters == None: diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/TOB.py --- a/Syncopation models/TOB.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/TOB.py Sun Apr 12 13:06:17 2015 +0100 @@ -4,7 +4,7 @@ ''' -from basic_functions import ceiling, find_divisor, is_prime, get_min_timeSpan +from basic_functions import ceiling, find_divisor, is_prime def get_syncopation(bar, parameter = None): binarySequence = bar.get_binary_sequence() diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/WNBD.py --- a/Syncopation models/WNBD.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/WNBD.py Sun Apr 12 13:06:17 2015 +0100 @@ -12,72 +12,117 @@ product = product*n return product -#def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq): def get_syncopation(bar, parameters = None): syncopation = None - binarySequence = bar.get_binary_sequence() - sequenceLength = len(binarySequence) + noteSequence = bar.get_note_sequence() + barTicks = bar.get_bar_ticks() subdivisionSequence = bar.get_subdivision_sequence() strongBeatLevel = bar.get_beat_level() - nextbarBinarySequence = None + + nextbarNoteSequence = None + if bar.get_next_bar() != None: + nextbarNoteSequence = bar.get_next_bar().get_note_sequence() - if bar.get_next_bar() != None: - nextbarBinarySequence = bar.get_next_bar().get_binary_sequence() + # calculate each strong beat ticks + numberOfBeats = cumu_multiply(subdivisionSequence[:strongBeatLevel+1]) + beatIntervalTicks = barTicks/numberOfBeats + # beatsTicks represents the ticks for all the beats in the current bar and the first two beats in the next bar + beatsTicks = [i*beatIntervalTicks for i in range(numberOfBeats+2)] + #print beatsTicks + totalSyncopation = 0 + for note in noteSequence: + # print note.to_string() + # find such beatIndex such that note.startTime is located between (including) beatsTicks[beatIndex] and (not including) beatsTicks[beatIndex+1] + beatIndex = 0 + while note.startTime < beatsTicks[beatIndex] or note.startTime >= beatsTicks[beatIndex+1]: + beatIndex += 1 - numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1]) # numberOfBeats is the number of strong beats + # print beatIndex + # calculate the distance of this note to its nearest beat + distanceToBeatOnLeft = abs(note.startTime - beatsTicks[beatIndex])/float(beatIntervalTicks) + distanceToBeatOnRight = abs(note.startTime - beatsTicks[beatIndex+1])/float(beatIntervalTicks) + distanceToNearestBeat = min(distanceToBeatOnLeft,distanceToBeatOnRight) + # print distanceToNearestBeat + + # calculate the WNBD measure for this note, and add to total syncopation value for this bar + if distanceToNearestBeat == 0: + totalSyncopation += 0 + # or if this note is held on past the following beat, but ends on or before the later beat + elif beatsTicks[beatIndex+1] < note.startTime+note.duration <= beatsTicks[beatIndex+2]: + totalSyncopation += float(2)/distanceToNearestBeat + else: + totalSyncopation += float(1)/distanceToNearestBeat + # print totalSyncopation + + return totalSyncopation + +#def get_syncopation(seq, subdivision_seq, strong_beat_level, postbar_seq): +# def get_syncopation(bar, parameters = None): +# syncopation = None - if sequenceLength % numberOfBeats != 0: - print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision sequence.' - else: - # Find the indices of all the strong-beats - beatIndices = [] - beatInterval = sequenceLength / numberOfBeats - for i in range(numberOfBeats+1): - beatIndices.append(i*beatInterval) - if nextbarBinarySequence != None: # if there is a postbar_seq, add another two beats index for later calculation - beatIndices += [sequenceLength+beatInterval, sequenceLength+ 2* beatInterval] +# binarySequence = bar.get_binary_sequence() +# sequenceLength = len(binarySequence) +# subdivisionSequence = bar.get_subdivision_sequence() +# strongBeatLevel = bar.get_beat_level() +# nextbarBinarySequence = None - noteIndices = get_note_indices(binarySequence) # all the notes +# if bar.get_next_bar() != None: +# nextbarBinarySequence = bar.get_next_bar().get_binary_sequence() - # Calculate the WNBD measure for each note - def measure_pernote(noteIndices, nextNoteIndex): - # Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) - j = 0 - while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]: - j = j + 1 +# numberOfBeats = cumu_multiply(subdivisionSequence[0:strongBeatLevel+1]) # numberOfBeats is the number of strong beats + +# if sequenceLength % numberOfBeats != 0: +# print 'Error: the length of sequence is not subdivable by the subdivision factor in subdivision sequence.' +# else: +# # Find the indices of all the strong-beats +# beatIndices = [] +# beatInterval = sequenceLength / numberOfBeats +# for i in range(numberOfBeats+1): +# beatIndices.append(i*beatInterval) +# if nextbarBinarySequence != None: # if there is a postbar_seq, add another two beats index for later calculation +# beatIndices += [sequenceLength+beatInterval, sequenceLength+ 2* beatInterval] + +# noteIndices = get_note_indices(binarySequence) # all the notes + +# # Calculate the WNBD measure for each note +# def measure_pernote(noteIndices, nextNoteIndex): +# # Find the nearest beats where this note locates - in [beat_indices[j], beat_indices[j+1]) +# j = 0 +# while noteIndices < beatIndices[j] or noteIndices >= beatIndices[j+1]: +# j = j + 1 - # The distance of note to nearest beat normalised by the beat interval - distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval) +# # The distance of note to nearest beat normalised by the beat interval +# distanceToNearestBeat = min(abs(noteIndices - beatIndices[j]), abs(noteIndices - beatIndices[j+1]))/float(beatInterval) - # if this note is on-beat - if distanceToNearestBeat == 0: - measure = 0 - # or if this note is held on past the following beat, but ends on or before the later beat - elif beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]: - measure = float(2)/distanceToNearestBeat - else: - measure = float(1)/distanceToNearestBeat - return measure +# # if this note is on-beat +# if distanceToNearestBeat == 0: +# measure = 0 +# # or if this note is held on past the following beat, but ends on or before the later beat +# elif beatIndices[j+1] < nextNoteIndex <= beatIndices[j+2]: +# measure = float(2)/distanceToNearestBeat +# else: +# measure = float(1)/distanceToNearestBeat +# return measure - total = 0 - for i in range(len(noteIndices)): - # if this is the last note, end_time is the index of the following note in the next bar - if i == len(noteIndices)-1: - # if the next bar is not none or a bar of full rest, - # the nextNoteIndex is the sum of sequence length in the current bar and the noteIndex in the next bar - if nextbarBinarySequence != None and nextbarBinarySequence != repeat([0],len(nextbarBinarySequence)): - nextNoteIndex = get_note_indices(nextbarBinarySequence)[0]+sequenceLength - # else when the next bar is none or full rest, end_time is the end of this sequence. - else: - nextNoteIndex = sequenceLength - # else this is not the last note, the nextNoteIndex is the following element in the noteIndices list - else: - nextNoteIndex = noteIndices[i+1] - # sum up the syncopation value for individual note at noteIndices[i] - total += measure_pernote(noteIndices[i],nextNoteIndex) +# total = 0 +# for i in range(len(noteIndices)): +# # if this is the last note, end_time is the index of the following note in the next bar +# if i == len(noteIndices)-1: +# # if the next bar is not none or a bar of full rest, +# # the nextNoteIndex is the sum of sequence length in the current bar and the noteIndex in the next bar +# if nextbarBinarySequence != None and nextbarBinarySequence != repeat([0],len(nextbarBinarySequence)): +# nextNoteIndex = get_note_indices(nextbarBinarySequence)[0]+sequenceLength +# # else when the next bar is none or full rest, end_time is the end of this sequence. +# else: +# nextNoteIndex = sequenceLength +# # else this is not the last note, the nextNoteIndex is the following element in the noteIndices list +# else: +# nextNoteIndex = noteIndices[i+1] +# # sum up the syncopation value for individual note at noteIndices[i] +# total += measure_pernote(noteIndices[i],nextNoteIndex) - #syncopation = float(total) / len(note_indices) +# #syncopation = float(total) / len(note_indices) - # return the total value, leave the normalisation done in the end - return total +# # return the total value, leave the normalisation done in the end +# return total diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/basic_functions.py --- a/Syncopation models/basic_functions.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/basic_functions.py Sun Apr 12 13:06:17 2015 +0100 @@ -80,12 +80,12 @@ return isPrime # convert a velocity sequence to its minimum time-span representation -def velocity_sequence_to_min_timetpan(velocitySequence): +def velocity_sequence_to_min_timespan(velocitySequence): minTimeSpanVelocitySeq = [1] for divisors in find_divisor(len(velocitySequence)): segments = subdivide(velocitySequence,divisors) if len(segments)!=0: - del minTimeSpanSequence[:] + del minTimeSpanVelocitySeq[:] for s in segments: minTimeSpanVelocitySeq.append(s[0]) if sum(minTimeSpanVelocitySeq) == sum(velocitySequence): @@ -93,21 +93,27 @@ return minTimeSpanVelocitySeq # convert a note sequence to its minimum time-span representation -def note_sequence_to_min_timespan(noteSequence, barTicks): - barBinaryArray = [0]*barTicks +def note_sequence_to_min_timespan(noteSequence): + from music_objects import note_sequence_to_velocity_sequence + timeSpanTicks = len(note_sequence_to_velocity_sequence(noteSequence)) +# print timeSpanTicks + + barBinaryArray = [0]*(timeSpanTicks+1) for note in noteSequence: # mark note_on event (i.e. startTime) and note_off event (i.e. endTime = startTime + duration) as 1 in the barBinaryArray - barBinaryArray[note[0]] = 1 - barBinaryArray[note[0]+note[1]] = 1 + barBinaryArray[note.startTime] = 1 + barBinaryArray[note.startTime + note.duration] = 1 # convert the barBinaryArray to its minimum time-span representation - minBarBinaryArray = velocitySequence_to_min_timeSpan(barBinaryArray) + minBarBinaryArray = velocity_sequence_to_min_timetpan(barBinaryArray[:-1]) + print barBinaryArray + print minBarBinaryArray delta_t = len(barBinaryArray)/len(minBarBinaryArray) # scale the startTime and duration of each note by delta_t for note in noteSequence: - note[0] = note[0]/delta_t - note[1] = note[1]/delta_t + note.startTime = note.startTime/delta_t + note.duration = note.duration/delta_t return noteSequence diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/music_objects.py --- a/Syncopation models/music_objects.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/music_objects.py Sun Apr 12 13:06:17 2015 +0100 @@ -61,7 +61,7 @@ return str(self)[1:-1].replace(" ","") -def velocity_sequence_to_note_sequence(velocitySequence): +def velocity_sequence_to_note_sequence(velocitySequence, nextbarVelocitySequence = None): noteSequence = NoteSequence() @@ -80,8 +80,14 @@ # to set the duration for the last note if( len(noteSequence) > 0): - previousNote = noteSequence[-1] - previousNote.duration = len(velocitySequence) - previousNote.startTime + lastNote = noteSequence[-1] + + if nextbarVelocitySequence == None: + lastNote.duration = len(velocitySequence) - lastNote.startTime + else: + nextNoteStartTime = next((index for index, v in enumerate(nextbarVelocitySequence) if v), None) + lastNote.duration = len(velocitySequence) + nextNoteStartTime-lastNote.startTime + return noteSequence @@ -120,7 +126,6 @@ class Bar: - def __init__(self, rhythmSequence, timeSignature, ticksPerQuarter=None, qpmTempo=None, nextBar=None, prevBar=None): if isinstance(rhythmSequence, NoteSequence): self.noteSequence = rhythmSequence @@ -137,12 +142,15 @@ def get_note_sequence(self): if self.noteSequence == None: - self.noteSequence = velocitySequence_to_noteSequence(self.velocitySequence) + nextbarVelocitySequence = None + if self.nextBar != None: + nextbarVelocitySequence = self.nextBar.get_velocity_sequence() + self.noteSequence = velocity_sequence_to_note_sequence(self.velocitySequence, nextbarVelocitySequence) return self.noteSequence def get_velocity_sequence(self): if self.velocitySequence == None: - self.velocitySequence = noteSequence_to_velocitySequence(self.noteSequence) + self.velocitySequence = note_sequence_to_velocity_sequence(self.noteSequence) return self.velocitySequence def get_binary_sequence(self): @@ -171,7 +179,7 @@ # return the length of a bar in time units (ticks) def get_bar_ticks(self): - return calculate_bar_ticks(self.timeSignature.get_numerator(),self.timeSignature.get_denominator(), self.ticksPerQuarter) + return calculate_bar_ticks(self.timeSignature.get_numerator(),self.timeSignature.get_denominator(), self.tpq) class TimeSignature(): diff -r 2dbc09ca8013 -r df1e7c378ee0 Syncopation models/syncopation.py --- a/Syncopation models/syncopation.py Thu Apr 09 23:49:16 2015 +0100 +++ b/Syncopation models/syncopation.py Sun Apr 12 13:06:17 2015 +0100 @@ -7,7 +7,18 @@ def sync_perbar_permodel (model, bar, parameters): return model.get_syncopation(bar, parameters) -# def syncopation_barlist_permodel(model, barlist, parameters): + def syncopation_barlist_permodel(model, barlist, parameters): + total = 0 + numberOfNotes = 0 + for bar in barlist: + if sync_perbar_permodel(model, bar, parameters) != None: + total += sync_perbar_permodel(model, bar, parameters) + numberOfNotes += sum(bar.get_binary_sequence()) + else: + print 'Bar %d cannot be measured, returning None.' % barlist.index(bar) + + if model is WNBD: + total = (float) total/ numberOfNotes # def sync_perbar_permodel(seq, model, timesig = None, subdivision_seq = None, weight_seq = None, L_max = 5, prebar_seq = None, postbar_seq = None, strong_beat_level = None): # syncopation = None