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@22: # convert a velocity sequence to its minimum time-span representation csong@22: def velocity_sequence_to_min_timetpan(velocitySequence): csong@22: minTimeSpanVelocitySeq = [1] csong@22: for divisors in find_divisor(len(velocitySequence)): csong@22: segments = subdivide(velocitySequence,divisors) csong@2: if len(segments)!=0: csong@22: del minTimeSpanSequence[:] csong@2: for s in segments: csong@22: minTimeSpanVelocitySeq.append(s[0]) csong@22: if sum(minTimeSpanVelocitySeq) == sum(velocitySequence): csong@2: break csong@22: return minTimeSpanVelocitySeq csong@2: csong@22: # convert a note sequence to its minimum time-span representation csong@22: def note_sequence_to_min_timespan(noteSequence, barTicks): csong@22: barBinaryArray = [0]*barTicks csong@22: for note in noteSequence: csong@22: # mark note_on event (i.e. startTime) and note_off event (i.e. endTime = startTime + duration) as 1 in the barBinaryArray csong@22: barBinaryArray[note[0]] = 1 csong@22: barBinaryArray[note[0]+note[1]] = 1 csong@22: csong@22: # convert the barBinaryArray to its minimum time-span representation csong@22: minBarBinaryArray = velocitySequence_to_min_timeSpan(barBinaryArray) csong@22: delta_t = len(barBinaryArray)/len(minBarBinaryArray) csong@22: csong@22: # scale the startTime and duration of each note by delta_t csong@22: for note in noteSequence: csong@22: note[0] = note[0]/delta_t csong@22: note[1] = note[1]/delta_t csong@22: csong@22: return noteSequence csong@22: csong@22: csong@22: csong@22: # get_note_indices returns all the indices of all the notes in this velocity_sequence csong@22: def get_note_indices(velocitySequence): csong@20: noteIndices = [] csong@2: csong@22: for index in range(len(velocitySequence)): csong@22: if velocitySequence[index] != 0: csong@20: noteIndices.append(index) csong@2: csong@20: return noteIndices csong@2: csong@22: 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@22: csong@22: def calculate_bar_ticks(numerator, denominator, ticksPerQuarter): csong@21: return (numerator * ticksPerQuarter *4) / denominator csong@21: csong@22: csong@22: def get_rhythm_category(velocitySequence, subdivisionSequence): csong@22: ''' csong@22: The get_rhythm_category function is used to detect rhythm category: monorhythm or polyrhythm. csong@22: For monorhythms, all prime factors of the length of minimum time-span representation of this sequence are csong@22: elements of its subdivision_seq, otherwise it is polyrhythm; csong@22: e.g. prime_factors of polyrhythm 100100101010 in 4/4 is [2,3] but subdivision_seq = [1,2,2] for 4/4 csong@22: ''' csong@22: rhythmCategory = 'mono' csong@22: for f in find_prime_factors(len(get_min_timeSpan(velocitySequence))): csong@22: if not (f in subdivisionSequence): csong@22: rhythmCategory = 'poly' csong@22: break csong@22: return rhythmCategory csong@22: csong@22: csong@22: def string_to_sequence(inputString): csong@22: return map(int, inputString.split(',')) csong@22: 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@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