christopher@45
|
1
|
christopher@45
|
2 from basic_functions import ceiling, string_to_sequence, calculate_bar_ticks, velocity_sequence_to_min_timespan
|
christopher@45
|
3 import parameter_setter
|
christopher@45
|
4 import rhythm_parser
|
christopher@45
|
5
|
christopher@45
|
6 class Note():
|
christopher@45
|
7 def __init__(self, firstarg = None, duration = None, velocity = None):
|
christopher@45
|
8 self.startTime = 0
|
christopher@45
|
9 self.duration = 0
|
christopher@45
|
10 self.velocity = 0
|
christopher@45
|
11
|
christopher@45
|
12 if firstarg != None:
|
christopher@45
|
13 if isinstance(firstarg,basestring):
|
christopher@45
|
14 intlist = string_to_sequence(firstarg,int)
|
christopher@45
|
15 self.startTime = intlist[0]
|
christopher@45
|
16 self.duration = intlist[1]
|
christopher@45
|
17 self.velocity = intlist[2]
|
christopher@45
|
18 elif isinstance(firstarg,int):
|
christopher@45
|
19 self.startTime = firstarg
|
christopher@45
|
20
|
christopher@45
|
21 if duration != None:
|
christopher@45
|
22 self.duration = duration
|
christopher@45
|
23 if velocity != None:
|
christopher@45
|
24 self.velocity = velocity
|
christopher@45
|
25
|
christopher@45
|
26
|
christopher@45
|
27 def to_string(self):
|
christopher@45
|
28 return "(%d,%d,%f)" %(self.startTime, self.duration, self.velocity)
|
christopher@45
|
29
|
christopher@45
|
30
|
christopher@45
|
31 # NoteSequence is a list of Note
|
christopher@45
|
32 class NoteSequence(list):
|
christopher@45
|
33 def __init__(self, noteSequenceString = None):
|
christopher@45
|
34 if noteSequenceString != None:
|
christopher@45
|
35 self.string_to_note_sequence(noteSequenceString)
|
christopher@45
|
36
|
christopher@45
|
37 def string_to_note_sequence(self, noteSequenceString):
|
christopher@45
|
38 noteSequenceString = rhythm_parser.discard_spaces(noteSequenceString)
|
christopher@45
|
39 # try:
|
christopher@45
|
40 # Turning "(1,2,3),(4,5,6),(7,8,9)" into ["1,2,3","4,5,6,","7,8,9"]
|
christopher@45
|
41 listStrings = noteSequenceString[1:-1].split("),(")
|
christopher@45
|
42 for localString in listStrings:
|
christopher@45
|
43 self.append(Note(localString))
|
christopher@45
|
44
|
christopher@45
|
45 def to_string(self):
|
christopher@45
|
46 noteSequenceString = ""
|
christopher@45
|
47 for note in self:
|
christopher@45
|
48 noteSequenceString += note.to_string() + ","
|
christopher@45
|
49 return noteSequenceString[:-1]
|
christopher@45
|
50
|
christopher@45
|
51
|
christopher@45
|
52 class NormalisedVelocityValueOutOfRange(Exception):
|
christopher@45
|
53 def __init__(self, value):
|
christopher@45
|
54 self.value = value
|
christopher@45
|
55 def __str__(self):
|
christopher@45
|
56 return repr(self.value)
|
christopher@45
|
57
|
christopher@45
|
58 # VelocitySequence is a list of float numbers
|
christopher@45
|
59 class VelocitySequence(list):
|
christopher@45
|
60 def __init__(self, velocitySequence = None):
|
christopher@45
|
61 if velocitySequence != None:
|
christopher@45
|
62 if isinstance(velocitySequence,basestring):
|
christopher@45
|
63 self.string_to_velocity_sequence(velocitySequence)
|
christopher@45
|
64 elif isinstance(velocitySequence, list):
|
christopher@45
|
65 self+=velocitySequence
|
christopher@45
|
66
|
christopher@45
|
67 def string_to_velocity_sequence(self,inputString):
|
christopher@45
|
68
|
christopher@45
|
69 def convert_velocity_value(argstring):
|
christopher@45
|
70 value = float(argstring)
|
christopher@45
|
71 if value>=0 and value<=1:
|
christopher@45
|
72 return value
|
christopher@45
|
73 else:
|
christopher@45
|
74 raise NormalisedVelocityValueOutOfRange("Value: "+argstring+" in " + inputString)
|
christopher@45
|
75
|
christopher@45
|
76 self.extend(string_to_sequence(inputString,convert_velocity_value))
|
christopher@45
|
77
|
christopher@45
|
78
|
christopher@45
|
79 def to_string(self):
|
christopher@45
|
80 return str(velocity_sequence_to_min_timespan(self))[1:-1].replace(" ","")
|
christopher@45
|
81
|
christopher@45
|
82
|
christopher@45
|
83 def velocity_sequence_to_note_sequence(velocitySequence, nextbarVelocitySequence = None):
|
christopher@45
|
84
|
christopher@45
|
85 noteSequence = NoteSequence()
|
christopher@45
|
86
|
christopher@45
|
87 for index in range(len(velocitySequence)):
|
christopher@45
|
88 if (velocitySequence[index]!= 0): # onset detected
|
christopher@45
|
89 startTime = index
|
christopher@45
|
90 velocity = velocitySequence[index]
|
christopher@45
|
91
|
christopher@45
|
92 # if there are previous notes added
|
christopher@45
|
93 if( len(noteSequence) > 0):
|
christopher@45
|
94 previousNote = noteSequence[-1]
|
christopher@45
|
95 previousNote.duration = startTime - previousNote.startTime
|
christopher@45
|
96
|
christopher@45
|
97 # add the current note into note sequence
|
christopher@45
|
98 noteSequence.append( Note(startTime, 0, velocity) )
|
christopher@45
|
99
|
christopher@45
|
100 # to set the duration for the last note
|
christopher@45
|
101 if( len(noteSequence) > 0):
|
christopher@45
|
102 lastNote = noteSequence[-1]
|
christopher@45
|
103
|
christopher@45
|
104 if nextbarVelocitySequence == None:
|
christopher@45
|
105 lastNote.duration = len(velocitySequence) - lastNote.startTime
|
christopher@45
|
106 else:
|
christopher@45
|
107 nextNoteStartTime = next((index for index, v in enumerate(nextbarVelocitySequence) if v), None)
|
christopher@45
|
108 lastNote.duration = len(velocitySequence) + nextNoteStartTime-lastNote.startTime
|
christopher@45
|
109
|
christopher@45
|
110
|
christopher@45
|
111 return noteSequence
|
christopher@45
|
112
|
christopher@45
|
113
|
christopher@45
|
114 def note_sequence_to_velocity_sequence(noteSequence, timespanTicks = None):
|
christopher@45
|
115
|
christopher@45
|
116 velocitySequence = VelocitySequence()
|
christopher@45
|
117
|
christopher@45
|
118 previousNoteStartTime = -1
|
christopher@45
|
119
|
christopher@45
|
120 for note in noteSequence:
|
christopher@45
|
121
|
christopher@45
|
122 interOnsetInterval = note.startTime - previousNoteStartTime
|
christopher@50
|
123 #ignore note if it is part of a chord...
|
christopher@50
|
124 if interOnsetInterval!=0:
|
christopher@50
|
125 velocitySequence += [0]*(interOnsetInterval-1)
|
christopher@50
|
126 velocitySequence += [note.velocity]
|
christopher@45
|
127
|
christopher@45
|
128 previousNoteStartTime = note.startTime
|
christopher@45
|
129
|
christopher@45
|
130 if timespanTicks!=None:
|
christopher@45
|
131 velocitySequence += [0]*(timespanTicks - len(velocitySequence))
|
christopher@45
|
132 else:
|
christopher@45
|
133 velocitySequence += [0]*(noteSequence[-1].duration-1)
|
christopher@45
|
134
|
christopher@45
|
135 # normalising velocity sequence between 0-1
|
christopher@45
|
136 if max(velocitySequence)>0:
|
christopher@45
|
137 velocitySequence = VelocitySequence([float(v)/max(velocitySequence) for v in velocitySequence])
|
christopher@45
|
138
|
christopher@45
|
139 return velocitySequence
|
christopher@45
|
140
|
christopher@45
|
141
|
christopher@45
|
142 class BarList(list):
|
christopher@45
|
143 def append(self,bar):
|
christopher@45
|
144 if(len(self)>0):
|
christopher@45
|
145 bar.set_previous_bar(self[-1])
|
christopher@45
|
146 self[-1].set_next_bar(bar)
|
christopher@45
|
147 super(BarList, self).append(bar)
|
christopher@45
|
148
|
christopher@45
|
149 def concat(self, barList):
|
christopher@45
|
150 while(len(barList)!=0):
|
christopher@45
|
151 localbar = barList[0]
|
christopher@45
|
152 self.append(localbar)
|
christopher@45
|
153 barList.remove(localbar)
|
christopher@45
|
154
|
christopher@50
|
155 def to_string(self, sequenceType="y"):
|
christopher@50
|
156
|
christopher@50
|
157 output = ""
|
christopher@50
|
158
|
christopher@50
|
159 for bar in self:
|
christopher@50
|
160 prev = bar.get_previous_bar()
|
christopher@50
|
161
|
christopher@50
|
162 params = "t"+sequenceType
|
christopher@50
|
163
|
christopher@50
|
164 if prev!=None and prev.get_time_signature()==bar.get_time_signature():
|
christopher@50
|
165 params = "-"+params
|
christopher@50
|
166
|
christopher@50
|
167 output += " " + bar.to_string(params)
|
christopher@50
|
168
|
christopher@50
|
169 return output
|
christopher@45
|
170
|
christopher@45
|
171 class Bar:
|
christopher@45
|
172 def __init__(self, rhythmSequence, timeSignature, ticksPerQuarter=None, qpmTempo=None, nextBar=None, prevBar=None):
|
christopher@45
|
173 if isinstance(rhythmSequence, NoteSequence):
|
christopher@45
|
174 self.noteSequence = rhythmSequence
|
christopher@45
|
175 self.velocitySequence = None
|
christopher@45
|
176 elif isinstance(rhythmSequence, VelocitySequence):
|
christopher@45
|
177 self.velocitySequence = rhythmSequence
|
christopher@45
|
178 self.noteSequence = None
|
christopher@45
|
179
|
christopher@45
|
180 if isinstance(timeSignature, basestring):
|
christopher@45
|
181 self.timeSignature = TimeSignature(timeSignature)
|
christopher@45
|
182 else:
|
christopher@45
|
183 self.timeSignature = timeSignature
|
christopher@71
|
184
|
christopher@71
|
185 if ticksPerQuarter==None:
|
christopher@71
|
186 self.tpq = len(self.get_velocity_sequence())*self.timeSignature.get_denominator()/(4*self.timeSignature.get_numerator())
|
christopher@71
|
187 else:
|
christopher@71
|
188 self.tpq = ticksPerQuarter
|
christopher@71
|
189
|
christopher@71
|
190 self.qpm = qpmTempo
|
christopher@71
|
191
|
christopher@71
|
192
|
christopher@71
|
193
|
christopher@45
|
194 self.nextBar = nextBar
|
christopher@45
|
195 self.prevBar = prevBar
|
christopher@45
|
196
|
christopher@45
|
197 def get_note_sequence(self):
|
christopher@45
|
198 if self.noteSequence == None:
|
christopher@45
|
199 nextbarVelocitySequence = None
|
christopher@45
|
200 if self.nextBar != None:
|
christopher@45
|
201 nextbarVelocitySequence = self.nextBar.get_velocity_sequence()
|
christopher@45
|
202 self.noteSequence = velocity_sequence_to_note_sequence(self.velocitySequence, nextbarVelocitySequence)
|
christopher@45
|
203 return self.noteSequence
|
christopher@45
|
204
|
christopher@45
|
205 def get_velocity_sequence(self):
|
christopher@45
|
206 if self.velocitySequence == None:
|
christopher@45
|
207 self.velocitySequence = note_sequence_to_velocity_sequence(self.noteSequence, self.get_bar_ticks())
|
christopher@45
|
208 return self.velocitySequence
|
christopher@45
|
209
|
christopher@45
|
210 def get_binary_sequence(self):
|
christopher@45
|
211 return ceiling(self.get_velocity_sequence())
|
christopher@45
|
212
|
christopher@45
|
213 def get_next_bar(self):
|
christopher@45
|
214 return self.nextBar
|
christopher@45
|
215
|
christopher@45
|
216 def get_previous_bar(self):
|
christopher@45
|
217 return self.prevBar
|
christopher@45
|
218
|
christopher@45
|
219 def set_next_bar(self, bar):
|
christopher@45
|
220 self.nextBar = bar
|
christopher@45
|
221
|
christopher@45
|
222 def set_previous_bar(self, bar):
|
christopher@45
|
223 self.prevBar = bar
|
christopher@45
|
224
|
christopher@45
|
225 def get_subdivision_sequence(self):
|
christopher@45
|
226 return self.timeSignature.get_subdivision_sequence()
|
christopher@45
|
227
|
christopher@45
|
228 def get_beat_level(self):
|
christopher@45
|
229 return self.timeSignature.get_beat_level()
|
christopher@45
|
230
|
christopher@45
|
231 def get_time_signature(self):
|
christopher@45
|
232 return self.timeSignature
|
christopher@45
|
233
|
christopher@45
|
234 # return the length of a bar in time units (ticks)
|
christopher@45
|
235 def get_bar_ticks(self):
|
christopher@45
|
236 return calculate_bar_ticks(self.timeSignature.get_numerator(),self.timeSignature.get_denominator(), self.tpq)
|
christopher@45
|
237
|
christopher@45
|
238 def is_empty(self):
|
christopher@45
|
239 if max(self.get_velocity_sequence())>0:
|
christopher@45
|
240 return False
|
christopher@45
|
241 else:
|
christopher@45
|
242 return True
|
christopher@45
|
243
|
christopher@50
|
244 def to_string(self, sequenceType="ty"):
|
christopher@50
|
245
|
christopher@50
|
246 # prev = self.get_previous_bar()
|
christopher@50
|
247 # if prev!=None:
|
christopher@50
|
248 # if prev.get_time_signature()==self.get_time_signature():
|
christopher@50
|
249 # output=""
|
christopher@50
|
250 output = ""
|
christopher@45
|
251
|
christopher@50
|
252 if "-t" not in sequenceType:
|
christopher@50
|
253 output = "t{"+self.timeSignature.to_string()+"}"
|
christopher@50
|
254
|
christopher@50
|
255 if "v" in sequenceType:
|
christopher@45
|
256 output += "v{"+self.get_velocity_sequence().to_string()+"}"
|
christopher@45
|
257 else:
|
christopher@45
|
258 output += "y{"+self.get_note_sequence().to_string()+"}"
|
christopher@50
|
259
|
christopher@45
|
260 return output
|
christopher@45
|
261
|
christopher@45
|
262
|
christopher@45
|
263 class TimeSignature():
|
christopher@45
|
264 def __init__(self, inputString):
|
christopher@45
|
265 if inputString in parameter_setter.read_time_signature():
|
christopher@45
|
266 self.tsString = inputString
|
christopher@45
|
267 else:
|
christopher@45
|
268 print "Error: undefined time-signature: ", inputString
|
christopher@45
|
269 raise NullTimeSignatureError
|
christopher@45
|
270
|
christopher@45
|
271 def get_subdivision_sequence(self):
|
christopher@45
|
272 return parameter_setter.timeSignatureBase[self.tsString][0]
|
christopher@45
|
273
|
christopher@45
|
274 def get_beat_level(self):
|
christopher@45
|
275 return parameter_setter.timeSignatureBase[self.tsString][1]
|
christopher@45
|
276
|
christopher@45
|
277 def get_numerator(self):
|
christopher@45
|
278 return int(self.tsString.split('/')[0])
|
christopher@45
|
279
|
christopher@45
|
280 def get_denominator(self):
|
christopher@45
|
281 return int(self.tsString.split('/')[1])
|
christopher@45
|
282
|
christopher@45
|
283 def to_string(self):
|
christopher@45
|
284 return self.tsString
|
christopher@45
|
285
|
christopher@45
|
286
|