csong@0: ''' csong@0: Author: Chunyang Song csong@0: Institution: Centre for Digital Music, Queen Mary University of London csong@0: ''' csong@0: csong@23: from basic_functions import repeat, subdivide, ceiling, velocity_sequence_to_min_timespan, get_rhythm_category csong@1: csong@12: def get_cost(sequence,nextSequence): csong@23: sequence = velocity_sequence_to_min_timespan(sequence) # converting to the minimum time-span format csong@1: csong@12: if sequence[1:] == repeat([0],len(sequence)-1): # null prototype csong@1: cost = 0 csong@12: elif sequence == repeat([1],len(sequence)): # filled prototype csong@1: cost = 1 csong@12: elif sequence[0] == 1 and sequence[-1] == 0: # run1 prototype csong@1: cost = 2 csong@20: elif sequence[0] == 1 and (nextSequence == None or nextSequence[0] == 0): # run2 prototype csong@1: cost = 2 csong@20: elif sequence[-1] == 1 and nextSequence != None and nextSequence[0] == 1: # upbeat prototype csong@1: cost = 3 csong@12: elif sequence[0] == 0: # syncopated prototype csong@1: cost = 5 csong@1: csong@1: return cost csong@1: csong@1: # This function calculates the syncopation value (cost) for the sequence with the postbar_seq for a certain level. csong@12: def syncopation_perlevel(subSequences): christopher@38: #print 'subSequences', subSequences csong@1: total = 0 csong@12: for l in range(len(subSequences)-1): christopher@38: #print 'cost', get_cost(subSequences[l], subSequences[l+1]) csong@12: total = total + get_cost(subSequences[l], subSequences[l+1]) christopher@38: #print 'total this level', total csong@12: normalised = float(total)/(len(subSequences)-1) csong@1: csong@1: return normalised csong@1: csong@19: def get_syncopation(bar, parameters = None): csong@1: syncopation = None csong@12: csong@12: binarySequence = bar.get_binary_sequence() csong@12: subdivisionSequence = bar.get_subdivision_sequence() csong@12: csong@20: # PRS does not handle polyrhythms csong@12: if get_rhythm_category(binarySequence, subdivisionSequence) == 'poly': csong@12: print 'Warning: PRS model detects polyrhythms so returning None.' csong@0: else: csong@1: syncopation = 0 csong@0: csong@20: # retrieve the binary sequence in the next bar csong@12: if bar.get_next_bar() != None: csong@12: nextbarBinarySequence = bar.get_next_bar().get_binary_sequence() csong@12: else: csong@12: nextbarBinarySequence = None csong@12: csong@20: # numberOfSubSeqs is the number of sub-sequences at a certain metrical level, initialised to be 1 (at the bar level) csong@12: numberOfSubSeqs = 1 csong@12: for subdivisor in subdivisionSequence: csong@20: # numberOfSubSeqs is product of all the subdivisors up to the current level csong@12: numberOfSubSeqs = numberOfSubSeqs * subdivisor csong@20: csong@1: # recursion stops when the length of sub-sequence is less than 2 csong@12: if len(binarySequence)/numberOfSubSeqs >= 2: csong@1: # generate sub-sequences and append the next bar sequence csong@12: subSequences = subdivide(ceiling(binarySequence), numberOfSubSeqs) csong@12: subSequences.append(nextbarBinarySequence) csong@1: # adding syncopation at each metrical level to the total syncopation csong@20: #print 'per level', syncopation_perlevel(subSequences) csong@12: syncopation += syncopation_perlevel(subSequences) csong@1: else: csong@1: break csong@0: csong@1: return syncopation