annotate synpy/basic_functions.py @ 76:90b68f259541 tip

updated parameter_setter to be able to find the TimeSignature.pkl file without putting it in the pwd
author christopherh <christopher.harte@eecs.qmul.ac.uk>
date Wed, 13 May 2015 09:27:36 +0100
parents ef891481231e
children
rev   line source
christopher@45 1 # This python file is a collection of basic functions that are used in the syncopation models.
christopher@45 2
christopher@45 3 import math
christopher@45 4
christopher@45 5 # The concatenation function is used to concatenate two sequences.
christopher@45 6 def concatenate(seq1,seq2):
christopher@45 7 return seq1+seq2
christopher@45 8
christopher@45 9 # The repetition function is to concatenate a sequence to itself for 'times' number of times.
christopher@45 10 def repeat(seq,times):
christopher@45 11 new_seq = list(seq)
christopher@45 12 if times >= 1:
christopher@45 13 for i in range(times-1):
christopher@45 14 new_seq = concatenate(new_seq,seq)
christopher@45 15 else:
christopher@45 16 #print 'Error: repetition times needs to be no less than 1.'
christopher@45 17 new_seq = []
christopher@45 18 return new_seq
christopher@45 19
christopher@45 20 # The subdivision function is to equally subdivide a sequence into 'divisor' number of segments.
christopher@45 21 def subdivide(seq,divisor):
christopher@45 22 subSeq = []
christopher@45 23 if len(seq) % divisor != 0:
christopher@45 24 print 'Error: rhythmic sequence cannot be equally subdivided.'
christopher@45 25 else:
christopher@45 26 n = len(seq) / divisor
christopher@45 27 start , end = 0, n
christopher@45 28 for i in range(divisor):
christopher@45 29 subSeq.append(seq[start : end])
christopher@45 30 start = end
christopher@45 31 end = end + n
christopher@45 32 return subSeq
christopher@45 33
christopher@45 34
christopher@45 35 # The ceiling function is to round each number inside a sequence up to its nearest integer.
christopher@45 36 def ceiling(seq):
christopher@45 37 seq_ceil = []
christopher@45 38 for s in seq:
christopher@45 39 seq_ceil.append(int(math.ceil(s)))
christopher@45 40 return seq_ceil
christopher@45 41
christopher@45 42 # The find_divisor function returns a list of all possible divisors for a length of sequence.
christopher@45 43 def find_divisor(number):
christopher@45 44 divisors = [1]
christopher@45 45 for i in range(2,number+1):
christopher@45 46 if number%i ==0:
christopher@45 47 divisors.append(i)
christopher@45 48 return divisors
christopher@45 49
christopher@45 50 # The find_divisor function returns a list of all possible divisors for a length of sequence.
christopher@45 51 def find_prime_factors(number):
christopher@45 52 primeFactors = find_divisor(number)
christopher@45 53
christopher@45 54 # remove 1 because 1 is not prime number
christopher@45 55 del primeFactors[0]
christopher@45 56
christopher@45 57 # reversely traverse all the divisors list and once find a non-prime then delete
christopher@45 58 for i in range(len(primeFactors)-1,0,-1):
christopher@45 59 # print primeFactors[i], is_prime(primeFactors[i])
christopher@45 60 if not is_prime(primeFactors[i]):
christopher@45 61 del primeFactors[i]
christopher@45 62
christopher@45 63 return primeFactors
christopher@45 64
christopher@45 65 def is_prime(number):
christopher@45 66 isPrime = True
christopher@45 67 # 0 or 1 is not prime numbers
christopher@45 68 if number < 2:
christopher@45 69 isPrime = False
christopher@45 70 # 2 is the only even prime number
christopher@45 71 elif number == 2:
christopher@45 72 pass
christopher@45 73 # all the other even numbers are non-prime
christopher@45 74 elif number % 2 == 0:
christopher@45 75 isPrime = False
christopher@45 76 else:
christopher@45 77 for odd in range(3, int(math.sqrt(number) + 1), 2):
christopher@45 78 if number % odd == 0:
christopher@45 79 isPrime = False
christopher@45 80 return isPrime
christopher@45 81
christopher@45 82 # upsample a velocity sequence to certain length, e.g. [1,1] to [1,0,0,0,1,0,0,0]
christopher@45 83 def upsample_velocity_sequence(velocitySequence, length):
christopher@45 84 upsampledVelocitySequence = None
christopher@45 85 if length < len(velocitySequence):
christopher@45 86 print 'Error: the requested upsampling length needs to be longer than velocity sequence.'
christopher@45 87 elif length % len(velocitySequence) != 0:
christopher@45 88 print 'Error: velocity sequence can only be upsampled to a interger times of its own length.'
christopher@45 89 else:
christopher@45 90 upsampledVelocitySequence = [0]*length
christopher@45 91 scalingFactor = length/len(velocitySequence)
christopher@45 92 for index in range(len(velocitySequence)):
christopher@45 93 upsampledVelocitySequence[index*scalingFactor] = velocitySequence[index]
christopher@45 94 return upsampledVelocitySequence
christopher@45 95
christopher@45 96
christopher@45 97 # convert a velocity sequence to its minimum time-span representation
christopher@45 98 def velocity_sequence_to_min_timespan(velocitySequence):
christopher@45 99 from music_objects import VelocitySequence
christopher@45 100 minTimeSpanVelocitySeq = [1]
christopher@45 101 for divisors in find_divisor(len(velocitySequence)):
christopher@45 102 segments = subdivide(velocitySequence,divisors)
christopher@45 103 if len(segments)!=0:
christopher@45 104 del minTimeSpanVelocitySeq[:]
christopher@45 105 for s in segments:
christopher@45 106 minTimeSpanVelocitySeq.append(s[0])
christopher@45 107 if sum(minTimeSpanVelocitySeq) == sum(velocitySequence):
christopher@45 108 break
christopher@45 109 return VelocitySequence(minTimeSpanVelocitySeq)
christopher@45 110
christopher@45 111 """
christopher@45 112 # convert a note sequence to its minimum time-span representation
christopher@45 113 def note_sequence_to_min_timespan(noteSequence):
christopher@45 114 from music_objects import note_sequence_to_velocity_sequence
christopher@45 115 timeSpanTicks = len(note_sequence_to_velocity_sequence(noteSequence))
christopher@45 116 # print timeSpanTicks
christopher@45 117
christopher@45 118 barBinaryArray = [0]*(timeSpanTicks+1)
christopher@45 119 for note in noteSequence:
christopher@45 120 # mark note_on event (i.e. startTime) and note_off event (i.e. endTime = startTime + duration) as 1 in the barBinaryArray
christopher@45 121 barBinaryArray[note.startTime] = 1
christopher@45 122 barBinaryArray[note.startTime + note.duration] = 1
christopher@45 123
christopher@45 124 # convert the barBinaryArray to its minimum time-span representation
christopher@45 125 minBarBinaryArray = velocity_sequence_to_min_timetpan(barBinaryArray[:-1])
christopher@45 126 print barBinaryArray
christopher@45 127 print minBarBinaryArray
christopher@45 128 delta_t = len(barBinaryArray)/len(minBarBinaryArray)
christopher@45 129
christopher@45 130 # scale the startTime and duration of each note by delta_t
christopher@45 131 for note in noteSequence:
christopher@45 132 note.startTime = note.startTime/delta_t
christopher@45 133 note.duration = note.duration/delta_t
christopher@45 134
christopher@45 135 return noteSequence
christopher@45 136 """
christopher@45 137
christopher@45 138 # get_note_indices returns all the indices of all the notes in this velocity_sequence
christopher@45 139 def get_note_indices(velocitySequence):
christopher@45 140 noteIndices = []
christopher@45 141
christopher@45 142 for index in range(len(velocitySequence)):
christopher@45 143 if velocitySequence[index] != 0:
christopher@45 144 noteIndices.append(index)
christopher@45 145
christopher@45 146 return noteIndices
christopher@45 147
christopher@45 148
christopher@45 149 # The get_H returns a sequence of metrical weight for a certain metrical level (horizontal),
christopher@45 150 # given the sequence of metrical weights in a hierarchy (vertical) and a sequence of subdivisions.
christopher@45 151 def get_H(weightSequence,subdivisionSequence, level):
christopher@45 152 H = []
christopher@45 153 #print len(weight_seq), len(subdivision_seq), level
christopher@45 154 if (level <= len(subdivisionSequence)-1) and (level <= len(weightSequence)-1):
christopher@45 155 if level == 0:
christopher@45 156 H = repeat([weightSequence[0]],subdivisionSequence[0])
christopher@45 157 else:
christopher@45 158 H_pre = get_H(weightSequence,subdivisionSequence,level-1)
christopher@45 159 for h in H_pre:
christopher@45 160 H = concatenate(H, concatenate([h], repeat([weightSequence[level]],subdivisionSequence[level]-1)))
christopher@45 161 else:
christopher@45 162 print 'Error: a subdivision factor or metrical weight is not defined for the request metrical level.'
christopher@45 163 return H
christopher@45 164
christopher@45 165
christopher@45 166 def calculate_bar_ticks(numerator, denominator, ticksPerQuarter):
christopher@45 167 return (numerator * ticksPerQuarter *4) / denominator
christopher@45 168
christopher@45 169
christopher@45 170 def get_rhythm_category(velocitySequence, subdivisionSequence):
christopher@45 171 '''
christopher@45 172 The get_rhythm_category function is used to detect rhythm category: monorhythm or polyrhythm.
christopher@45 173 For monorhythms, all prime factors of the length of minimum time-span representation of this sequence are
christopher@45 174 elements of its subdivision_seq, otherwise it is polyrhythm;
christopher@45 175 e.g. prime_factors of polyrhythm 100100101010 in 4/4 is [2,3] but subdivision_seq = [1,2,2] for 4/4
christopher@45 176 '''
christopher@45 177 rhythmCategory = 'mono'
christopher@45 178 for f in find_prime_factors(len(velocity_sequence_to_min_timespan(velocitySequence))):
christopher@45 179 if not (f in subdivisionSequence):
christopher@45 180 rhythmCategory = 'poly'
christopher@45 181 break
christopher@45 182 return rhythmCategory
christopher@45 183
christopher@45 184
christopher@45 185 def string_to_sequence(inputString,typeFunction=float):
christopher@45 186 return map(typeFunction, inputString.split(','))
christopher@45 187
christopher@71 188 # find the metrical level L that contains the same number of metrical positions as the length of the binary sequence
christopher@71 189 # if the given Lmax is not big enough to analyse the given sequence, request a bigger Lmax
christopher@71 190 def find_rhythm_Lmax(rhythmSequence, Lmax, weightSequence, subdivisionSequence):
christopher@71 191 L = Lmax
christopher@71 192
christopher@71 193 # initially assuming the Lmax is not big enough
christopher@71 194 needBiggerLmax = True
christopher@71 195
christopher@71 196 # from the lowest metrical level (Lmax) to the highest, find the matching metrical level that
christopher@71 197 # has the same length as the length of binary sequence
christopher@71 198 while L >= 0:
christopher@71 199 if len(get_H(weightSequence,subdivisionSequence, L)) == len(rhythmSequence):
christopher@71 200 needBiggerLmax = False
christopher@71 201 break
christopher@71 202 else:
christopher@71 203 L = L - 1
christopher@71 204
christopher@71 205 # if need a bigger Lmax, print error message and return None; otherwise return the matching metrical level L
christopher@71 206 if needBiggerLmax:
christopher@71 207 print 'Error: needs a bigger L_max (i.e. the lowest metrical level) to match the given rhythm sequence.'
christopher@71 208 L = None
christopher@71 209
christopher@71 210 return L
christopher@71 211
christopher@71 212
christopher@45 213 # # The get_subdivision_seq function returns the subdivision sequence of several common time-signatures defined by GTTM,
christopher@45 214 # # or ask for the top three level of subdivision_seq manually set by the user.
christopher@45 215 # def get_subdivision_seq(timesig, L_max):
christopher@45 216 # subdivision_seq = []
christopher@45 217
christopher@45 218 # if timesig == '2/4' or timesig == '4/4':
christopher@45 219 # subdivision_seq = [1,2,2]
christopher@45 220 # elif timesig == '3/4' or timesig == '3/8':
christopher@45 221 # subdivision_seq = [1,3,2]
christopher@45 222 # elif timesig == '6/8':
christopher@45 223 # subdivision_seq = [1,2,3]
christopher@45 224 # elif timesig == '9/8':
christopher@45 225 # subdivision_seq = [1,3,3]
christopher@45 226 # elif timesig == '12/8':
christopher@45 227 # subdivision_seq = [1,4,3]
christopher@45 228 # elif timesig == '5/4' or timesig == '5/8':
christopher@45 229 # subdivision_seq = [1,5,2]
christopher@45 230 # elif timesig == '7/4' or timesig == '7/8':
christopher@45 231 # subdivision_seq = [1,7,2]
christopher@45 232 # elif timesig == '11/4' or timesig == '11/8':
christopher@45 233 # subdivision_seq = [1,11,2]
christopher@45 234 # else:
christopher@45 235 # print 'Time-signature',timesig,'is undefined. Please indicate subdivision sequence for this requested time-signature, e.g. [1,2,2] for 4/4 meter.'
christopher@45 236 # for i in range(3):
christopher@45 237 # s = int(input('Enter the subdivision factor at metrical level '+str(i)+':'))
christopher@45 238 # subdivision_seq.append(s)
christopher@45 239
christopher@45 240 # if L_max > 2:
christopher@45 241 # subdivision_seq = subdivision_seq + [2]*(L_max-2)
christopher@45 242 # else:
christopher@45 243 # subdivision_seq = subdivision_seq[0:L_max+1]
christopher@45 244
christopher@45 245 # return subdivision_seq
christopher@45 246