csong@1: ''' csong@1: Author: Chunyang Song csong@1: Institution: Centre for Digital Music, Queen Mary University of London csong@1: csong@1: ''' csong@1: csong@32: from basic_functions import get_note_indices, repeat, velocity_sequence_to_min_timespan csong@1: csong@1: # To find the nearest power of 2 equal to or less than the given number csong@20: def round_down_power_2(number): csong@1: i = 0 csong@1: if number > 0: csong@1: while pow(2,i) > number or number >= pow(2,i+1): csong@1: i = i+1 csong@1: power2 = pow(2,i) csong@1: else: csong@1: print 'Error: numbers that are less than 1 cannot be rounded down to its nearest power of two.' csong@1: power2 = None csong@1: return power2 csong@1: csong@1: # To find the nearest power of 2 equal to or more than the given number csong@20: def round_up_power_2(number): csong@1: i = 0 csong@1: while pow(2,i) < number: csong@1: i = i + 1 csong@1: return pow(2,i) csong@1: csong@1: # To examine whether start_time is 'off-beat' csong@22: def start_time_offbeat_measure(startTime, c_n): csong@22: measure = 0 csong@20: if startTime % c_n != 0: csong@22: measure = 2 csong@22: return measure csong@1: csong@1: # To examine whether end_time is 'off-beat' csong@22: def end_time_offbeat_measure(endTime, c_n): csong@22: measure = 0 csong@20: if endTime % c_n != 0: csong@22: measure = 1 csong@22: return measure csong@1: csong@22: def get_syncopation(bar, parameters = None): csong@21: syncopation = None csong@1: csong@21: # KTH only deals with simple-duple meter where the number of beats per bar is a power of two. csong@21: numerator = bar.get_time_signature().get_numerator() csong@21: if numerator != round_down_power_2(numerator): csong@21: print 'Warning: KTH model detects non simple-duple meter so returning None.' csong@21: else: csong@21: # retrieve note-sequence and next bar's note-sequence csong@21: noteSequence = bar.get_note_sequence() csong@23: #for note in noteSequence: csong@23: # print note.to_string() csong@23: #print 'barlength',bar.get_bar_ticks() csong@23: csong@21: nextbarNoteSequence = None csong@21: if bar.get_next_bar() != None: csong@21: nextbarNoteSequence = bar.get_next_bar().get_note_sequence() csong@1: csong@22: # convert note sequence to its minimum time-span representation so that the later calculation can be faster csong@23: # noteSequence = note_sequence_to_min_timespan(noteSequence) csong@23: # find delta_t csong@23: Tmin = len(velocity_sequence_to_min_timespan(bar.get_velocity_sequence())) csong@23: #print 'Tmin',Tmin csong@23: T = round_up_power_2(Tmin) csong@23: #print 'T',T csong@23: deltaT = float(bar.get_bar_ticks())/T csong@23: #print 'delta',deltaT csong@23: csong@22: csong@22: # calculate syncopation note by note csong@22: syncopation = 0 csong@22: csong@22: for note in noteSequence: csong@23: c_n = round_down_power_2(note.duration/deltaT) csong@23: #print 'd', note.duration csong@23: #print 'c_n', c_n csong@23: endTime = note.startTime + note.duration csong@23: #print float(note.startTime)/deltaT, float(endTime)/deltaT csong@23: syncopation = syncopation + start_time_offbeat_measure(float(note.startTime)/deltaT,c_n) + end_time_offbeat_measure(float(endTime)/deltaT,c_n) csong@22: csong@21: csong@23: return syncopation csong@21: csong@21: # # To calculate syncopation value of the sequence in the given time-signature. csong@21: # def get_syncopation(seq, timesig, postbar_seq): csong@21: # syncopation = 0 csong@21: csong@21: # numerator = int(timesig.split("/")[0]) csong@21: # if numerator == round_down_power_2(numerator): # if is a binary time-signature csong@21: # # converting to minimum time-span format csong@21: # seq = get_min_timeSpan(seq) csong@21: # if postbar_seq != None: csong@21: # postbar_seq = get_min_timeSpan(postbar_seq) csong@21: csong@21: # # sf is a stretching factor matching rhythm sequence and meter, as Keith defines the note duration as a multiple of 1/(2^d) beats where d is number of metrical level csong@21: # sf = round_up_power_2(len(seq)) csong@1: csong@21: # # retrieve all the indices of all the notes in this sequence csong@21: # note_indices = get_note_indices(seq) csong@1: csong@21: # for i in range(len(note_indices)): csong@21: # # Assuming start_time is the index of this note, end_time is the index of the following note csong@21: # start_time = note_indices[i]*sf/float(len(seq)) csong@1: csong@21: # if i == len(note_indices)-1: # if this is the last note, end_time is the index of the following note in the next bar csong@21: # if postbar_seq != None and postbar_seq != repeat([0],len(postbar_seq)): csong@21: # next_index = get_note_indices(postbar_seq)[0]+len(seq) csong@21: # end_time = next_index*sf/float(len(seq)) csong@21: # else: # or if the next bar is none or full rest, end_time is the end of this sequence. csong@21: # end_time = sf csong@21: # else: csong@21: # end_time = note_indices[i+1]*sf/float(len(seq)) csong@1: csong@21: # duration = end_time - start_time csong@21: # c_n = round_down_power_2(duration) csong@21: # syncopation = syncopation + start(start_time,c_n) + end(end_time,c_n) csong@21: # else: csong@21: # print 'Error: KTH model can only deal with binary time-signature, e.g. 2/4 and 4/4. ' csong@21: # syncopation = None csong@1: csong@21: # return syncopation