csong@2: # This python file is a collection of basic functions that are used in the syncopation models. csong@2: csong@2: import math csong@2: csong@2: # The concatenation function is used to concatenate two sequences. csong@2: def concatenate(seq1,seq2): csong@2: return seq1+seq2 csong@2: csong@2: # The repetition function is to concatenate a sequence to itself for 'times' number of times. csong@2: def repeat(seq,times): csong@2: new_seq = list(seq) csong@2: if times >= 1: csong@2: for i in range(times-1): csong@2: new_seq = concatenate(new_seq,seq) csong@2: else: csong@2: #print 'Error: repetition times needs to be no less than 1.' csong@2: new_seq = [] csong@2: return new_seq csong@2: csong@2: # The subdivision function is to equally subdivide a sequence into 'divisor' number of segments. csong@2: def subdivide(seq,divisor): csong@2: subSeq = [] csong@2: if len(seq) % divisor != 0: csong@2: print 'Error: rhythmic sequence cannot be equally subdivided.' csong@2: else: csong@2: n = len(seq) / divisor csong@2: start , end = 0, n csong@2: for i in range(divisor): csong@2: subSeq.append(seq[start : end]) csong@2: start = end csong@2: end = end + n csong@2: return subSeq csong@2: csong@2: csong@2: # The ceiling function is to round each number inside a sequence up to its nearest integer. csong@2: def ceiling(seq): csong@2: seq_ceil = [] csong@2: for s in seq: csong@2: seq_ceil.append(int(math.ceil(s))) csong@2: return seq_ceil csong@2: csong@2: # The find_divisor function returns a list of all possible divisors for a length of sequence. csong@2: def find_divisor(number): csong@2: divisors = [1] csong@2: for i in range(2,number+1): csong@2: if number%i ==0: csong@2: divisors.append(i) csong@2: return divisors csong@2: csong@2: # The find_divisor function returns a list of all possible divisors for a length of sequence. csong@2: def find_prime_factors(number): csong@20: primeFactors = find_divisor(number) csong@2: csong@20: # remove 1 because 1 is not prime number csong@20: del primeFactors[0] csong@2: csong@20: # reversely traverse all the divisors list and once find a non-prime then delete csong@20: for i in range(len(primeFactors)-1,0,-1): csong@20: # print primeFactors[i], is_prime(primeFactors[i]) csong@20: if not is_prime(primeFactors[i]): csong@20: del primeFactors[i] csong@2: csong@20: return primeFactors csong@20: csong@20: def is_prime(number): csong@20: isPrime = True csong@20: # 0 or 1 is not prime numbers csong@20: if number < 2: csong@20: isPrime = False csong@20: # 2 is the only even prime number csong@20: elif number == 2: csong@20: pass csong@20: # all the other even numbers are non-prime csong@20: elif number % 2 == 0: csong@20: isPrime = False csong@20: else: csong@20: for odd in range(3, int(math.sqrt(number) + 1), 2): csong@20: if number % odd == 0: csong@20: isPrime = False csong@20: return isPrime csong@2: csong@2: # The min_timeSpan function searches for the shortest possible time-span representation for a sequence. csong@2: def get_min_timeSpan(seq): csong@20: minTimeSpan = [1] csong@2: for d in find_divisor(len(seq)): csong@2: segments = subdivide(seq,d) csong@2: if len(segments)!=0: csong@20: del minTimeSpan[:] csong@2: for s in segments: csong@20: minTimeSpan.append(s[0]) csong@20: if sum(minTimeSpan) == sum(seq): csong@2: break csong@20: return minTimeSpan csong@2: csong@2: # get_note_indices returns all the indices of all the notes in this sequence csong@20: def get_note_indices(sequence): csong@20: noteIndices = [] csong@2: csong@20: for index in range(len(sequence)): csong@20: if sequence[index] != 0: csong@20: noteIndices.append(index) csong@2: csong@20: return noteIndices csong@2: csong@2: # The get_H returns a sequence of metrical weight for a certain metrical level (horizontal), csong@2: # given the sequence of metrical weights in a hierarchy (vertical) and a sequence of subdivisions. csong@19: def get_H(weightSequence,subdivisionSequence, level): csong@2: H = [] csong@2: #print len(weight_seq), len(subdivision_seq), level csong@19: if (level <= len(subdivisionSequence)-1) and (level <= len(weightSequence)-1): csong@2: if level == 0: csong@19: H = repeat([weightSequence[0]],subdivisionSequence[0]) csong@2: else: csong@19: H_pre = get_H(weightSequence,subdivisionSequence,level-1) csong@2: for h in H_pre: csong@19: H = concatenate(H, concatenate([h], repeat([weightSequence[level]],subdivisionSequence[level]-1))) csong@2: else: csong@2: print 'Error: a subdivision factor or metrical weight is not defined for the request metrical level.' csong@2: return H csong@2: csong@19: # # The get_subdivision_seq function returns the subdivision sequence of several common time-signatures defined by GTTM, csong@19: # # or ask for the top three level of subdivision_seq manually set by the user. csong@19: # def get_subdivision_seq(timesig, L_max): csong@19: # subdivision_seq = [] csong@2: csong@19: # if timesig == '2/4' or timesig == '4/4': csong@19: # subdivision_seq = [1,2,2] csong@19: # elif timesig == '3/4' or timesig == '3/8': csong@19: # subdivision_seq = [1,3,2] csong@19: # elif timesig == '6/8': csong@19: # subdivision_seq = [1,2,3] csong@19: # elif timesig == '9/8': csong@19: # subdivision_seq = [1,3,3] csong@19: # elif timesig == '12/8': csong@19: # subdivision_seq = [1,4,3] csong@19: # elif timesig == '5/4' or timesig == '5/8': csong@19: # subdivision_seq = [1,5,2] csong@19: # elif timesig == '7/4' or timesig == '7/8': csong@19: # subdivision_seq = [1,7,2] csong@19: # elif timesig == '11/4' or timesig == '11/8': csong@19: # subdivision_seq = [1,11,2] csong@19: # else: csong@19: # print 'Time-signature',timesig,'is undefined. Please indicate subdivision sequence for this requested time-signature, e.g. [1,2,2] for 4/4 meter.' csong@19: # for i in range(3): csong@19: # s = int(input('Enter the subdivision factor at metrical level '+str(i)+':')) csong@19: # subdivision_seq.append(s) csong@2: csong@19: # if L_max > 2: csong@19: # subdivision_seq = subdivision_seq + [2]*(L_max-2) csong@19: # else: csong@19: # subdivision_seq = subdivision_seq[0:L_max+1] csong@2: csong@19: # return subdivision_seq csong@2: csong@9: csong@13: def get_rhythm_category(velocitySequence, subdivisionSequence): csong@13: ''' csong@13: The get_rhythm_category function is used to detect rhythm category: monorhythm or polyrhythm. csong@13: For monorhythms, all prime factors of the length of minimum time-span representation of this sequence are csong@13: elements of its subdivision_seq, otherwise it is polyrhythm; csong@13: e.g. prime_factors of polyrhythm 100100101010 in 4/4 is [2,3] but subdivision_seq = [1,2,2] for 4/4 csong@13: ''' csong@13: rhythmCategory = 'mono' csong@13: for f in find_prime_factors(len(get_min_timeSpan(velocitySequence))): csong@13: if not (f in subdivisionSequence): csong@13: rhythmCategory = 'poly' csong@9: break csong@13: return rhythmCategory csong@13: csong@13: def string_to_sequence(inputString): csong@13: return map(int, inputString.split(',')) csong@9: csong@9: csong@2: # The split_by_bar function seperates the score representation of rhythm by bar lines, csong@2: # resulting in a list representingbar-by-bar rhythm sequence, csong@2: # e.g. rhythm = ['|',[ts1,td1,v1], [ts2,td2,v2], '|',[ts3,td3,v3],'|'...] csong@2: # rhythm_bybar = [ [ [ts1,td1,v1], [ts2,td2,v2] ], [ [ts3,td3,v3] ], [...]] csong@2: # def split_by_bar(rhythm): csong@2: # rhythm_bybar = [] csong@2: # bar_index = [] csong@2: # for index in range(len(rhythm)): csong@2: # if rhythm[index] == '|': csong@2: csong@2: # return rhythm_bybar csong@2: csong@2: # def yseq_to_vseq(yseq): csong@2: # vseq = [] csong@2: csong@2: # return vseq csong@2: csong@2: csong@2: # # testing csong@20: # print find_prime_factors(10) csong@20: # print find_prime_factors(2) csong@20: # print find_prime_factors(12) csong@20: csong@20: csong@20: # print is_prime(1) # False csong@20: # print is_prime(2) # True csong@20: # print is_prime(3) # True csong@20: # print is_prime(29) # True csong@20: # print is_prime(345) # False csong@20: # print is_prime(999979) # True csong@20: # print is_prime(999981) # False