36
36
a pause segment could be represented as a segment with negative duration
37
37
38
38
"""
39
+ # TODO
40
+ # Need to introduce an EnvelopeProfile for the reusable math bits
41
+ # all cumulative update tracking should happen in the end segment
42
+ # and a containing envelope needs to "reset" the segment as needed
39
43
40
- class EnvelopeSegment (object ):
44
+
45
+ class EnvelopeProfile (object ):
41
46
"""
42
47
contains one phase or segment of a multi-segment envelope
43
48
attributes:
@@ -48,22 +53,55 @@ class EnvelopeSegment(object):
48
53
end value represented as delta/change
49
54
"""
50
55
51
- def __init__ (self , tween = tween .LINEAR , start = 0 , change = 1.0 , duration = 1.0 ):
56
+ def __init__ (self , tween = tween .LINEAR , start = 0 , change = 1.0 , duration = 1.0 ,
57
+ label = "profile" ):
52
58
self .tween = tween
53
59
self .start = float (start )
54
60
self .change = float (change )
55
61
self .duration = float (duration )
62
+ self .label = label
63
+
56
64
assert self .duration > 0 # only a duration > 0 makes sense
57
65
58
- def update (self , delta ):
66
+ def get_value (self , delta ):
59
67
if delta > self .duration :
60
68
delta = self .duration
69
+ # TODO save a self.value here for more consistency with envelopes?
70
+ print "updating %s %s at %s" % (self .label , self , delta )
61
71
return self .tween (delta , self .start , self .change , self .duration )
62
72
63
73
def get_jump_time (self , value ):
64
74
return tween .jump_time (self .tween , value , self .start , self .change , self .duration )
65
75
66
76
77
+ class EnvelopeSegment (object ):
78
+ """
79
+ An ultimate end point in a nested envelope
80
+ contains reference to an envelope profile"""
81
+ def __init__ (self , tween = tween .LINEAR , start = 0 , change = 1.0 , duration = 1.0 ,
82
+ profile = None , label = "segment" ):
83
+ if profile :
84
+ self .profile = profile
85
+ else :
86
+ self .profile = EnvelopeProfile (tween = tween , start = start ,
87
+ change = change , duration = duration , label = "%s-profile" % label )
88
+ self .duration = float (duration )
89
+ self .label = label
90
+ self .elapsed = 0
91
+ self .value = 0
92
+
93
+ assert self .duration > 0 # only a duration > 0 makes sense
94
+
95
+ def reset (self ):
96
+ self .elapsed = 0
97
+ self .value = 0
98
+
99
+ def update (self , delta ):
100
+ print "updating %s %s at delta %s" % (self .label , self , delta )
101
+ self .elapsed += delta
102
+ self .value = self .profile .get_value (self .elapsed )
103
+ return self .value
104
+
67
105
class StaticEnvelopeSegment (EnvelopeSegment ):
68
106
# just returns the start value unchanged - always
69
107
# will never advance on its own without a trigger interving
@@ -73,12 +111,13 @@ def __init__(self, *args, **kwargs):
73
111
self .duration = 0
74
112
75
113
def update (self , delta ):
76
- return self .start
114
+ return self .profile . start
77
115
78
116
class Envelope (EnvelopeSegment ):
79
117
80
- def __init__ (self , loop = 0 ):
118
+ def __init__ (self , loop = 0 , label = "envelope" ):
81
119
self .segments = []
120
+ self .label = label
82
121
83
122
# Not sure I need this state
84
123
self .running = False
@@ -105,39 +144,60 @@ def reset(self):
105
144
self .index = 0
106
145
self .value = 0
107
146
self .current_segment_time_delta = 0
147
+ for segment in self .segments :
148
+ segment .reset ()
108
149
109
150
def advance (self ):
110
- self .index += 1
111
- self .current_segment_time_delta = 0
112
- if self .index + 1 > len (self .segments ) and self .loop and self .loop_counter :
113
- self .index = 0
114
- if self .loop > 0 : # this is a finite loop
115
- self .loop_counter -= 1
151
+ # return True if advanced
152
+ print "advancing"
153
+ print self .index , len (self .segments )
154
+ if self .index + 1 == len (self .segments ): # last segment
155
+ if self .loop and self .loop_counter :
156
+ print 'looping'
157
+ self .segments [self .index ].reset ()
158
+ self .index = 0
159
+ if self .loop > 0 : # this is a finite loop
160
+ self .loop_counter -= 1
161
+ print 'loop counter now: ' , self .loop_counter
162
+ return True
163
+ print 'infinite loop counter now: ' , self .loop_counter
164
+ else :
165
+ print "not looping on advance - staying at last segment"
166
+ # non-looping, or done with final loop
167
+ pass
168
+ else :
169
+ # proceed through sequence of segments
170
+ self .segments [self .index ].reset ()
171
+ self .index += 1
172
+ self .current_segment_time_delta = 0
173
+ return True
174
+ return False
116
175
117
176
118
177
def update (self , delta ):
178
+ print '---------------------'
179
+ print "updating %s %s at delta %s" % (self .label , self , delta )
180
+
119
181
# delta is time passed since last update
120
182
if self .index + 1 > len (self .segments ):
121
183
# non looping or end of finite loop
122
184
# just reurn last value until something resets index
123
185
return self .value
124
186
segment = self .segments [self .index ]
125
187
self .current_segment_time_delta += delta
126
- # self.timedelta += delta
188
+ print " self current elapsed %s" % self . current_segment_time_delta
127
189
if (segment .duration and
128
190
(self .current_segment_time_delta > segment .duration )):
129
191
overage = self .current_segment_time_delta - segment .duration
130
- self .advance ()
131
192
# TODO don't handle case where overage > new segment
132
193
# duration - could need recursion
133
- self . current_segment_time_delta = overage
134
- if self .index + 1 > len ( self . segments ):
135
- self . value = segment . update ( segment . duration )
136
- else :
194
+
195
+ if self .advance ( ):
196
+ print 'advanced, new delta: ' , overage
197
+ delta = self . current_segment_time_delta = overage
137
198
segment = self .segments [self .index ]
138
- self .value = segment .update (self .current_segment_time_delta )
139
- else :
140
- self .value = segment .update (self .current_segment_time_delta )
199
+
200
+ self .value = segment .update (delta )
141
201
return self .value
142
202
143
203
@property
@@ -155,6 +215,7 @@ class TriggeredEnvelope(Envelope):
155
215
def __init__ (self , * args , ** kwargs ):
156
216
super (TriggeredEnvelope , self ).__init__ (* args , ** kwargs )
157
217
self .state = 0 # on:1 off:0
218
+ self .label = kwargs .get ('label' , 'triggered-envelope' )
158
219
# two states - each has a segment - which may be a envelope with sub-segments
159
220
160
221
def trigger (self , state = 1 , value = 1.0 ):
@@ -177,8 +238,9 @@ def trigger(self, state=1, value=1.0):
177
238
# off trigger
178
239
print 'advance'
179
240
self .advance ()
180
- if self .value != self .segments [1 ].start :
181
- self .current_segment_time_delta += self .segments [1 ].get_jump_time (self .value )
241
+ if self .value != self .segments [1 ].profile .start :
242
+ print "shortcutting time"
243
+ self .current_segment_time_delta += self .segments [1 ].profile .get_jump_time (self .value )
182
244
self .state = state
183
245
184
246
def update (self , delta ):
@@ -203,28 +265,46 @@ def __init__(self, peak_value=1.0, sustain_value=0.8,
203
265
attack_shape = tween .LINEAR , attack_duration = 0.5 ,
204
266
decay_shape = tween .LINEAR , decay_duration = .2 ,
205
267
release_shape = tween .LINEAR , release_duration = .5 ,
206
- bell_mode = False ):
268
+ bell_mode = False , label = 'ADSR-envelope' ):
269
+
270
+ super (ADSREnvelope , self ).__init__ (loop = 0 , label = label )
207
271
208
- self .attack_envelope = EnvelopeSegment (tween = attack_shape ,
209
- change = peak_value , duration = attack_duration )
272
+ self .attack_envelope = EnvelopeSegment (
273
+ tween = attack_shape ,
274
+ change = peak_value ,
275
+ duration = attack_duration ,
276
+ label = "attack" ,
277
+ )
210
278
211
- self .on_envelope = Envelope ()
279
+ self .on_envelope = Envelope (label = "on-envelope" )
212
280
self .on_envelope .segments = [self .attack_envelope ]
213
281
214
282
decay_change = - (peak_value - sustain_value )
283
+
215
284
if decay_change :
216
- self .decay_envelope = EnvelopeSegment (tween = decay_shape ,
217
- start = peak_value , change = decay_change , duration = decay_duration )
218
- self .on_evenlope .segments .append (self .decay_envelope )
285
+ self .decay_envelope = EnvelopeSegment (
286
+ tween = decay_shape ,
287
+ start = peak_value ,
288
+ change = decay_change ,
289
+ duration = decay_duration ,
290
+ label = "decay" ,
291
+ )
292
+ self .on_envelope .segments .append (self .decay_envelope )
219
293
else :
220
294
self .decay_envelope = None
221
295
222
- self .sustain_envelope = StaticEnvelopeSegment (start = sustain_value )
296
+ self .sustain_envelope = StaticEnvelopeSegment (start = sustain_value ,
297
+ label = "sustain" )
223
298
self .on_envelope .segments .append (self .sustain_envelope )
224
- self .release_envelope = EnvelopeSegment (tween = release_shape ,
225
- start = sustain_value , change = 0 - sustain_value , duration = release_duration )
226
- self .off_envelope = Envelope ()
227
- if self .release_envelope .change and self .release_envelope .duration :
299
+ self .release_envelope = EnvelopeSegment (
300
+ tween = release_shape ,
301
+ start = sustain_value ,
302
+ change = 0 - sustain_value ,
303
+ duration = release_duration ,
304
+ label = "release" ,
305
+ )
306
+ self .off_envelope = Envelope (label = "off-envelope" )
307
+ if self .release_envelope .profile .change and self .release_envelope .duration :
228
308
self .off_envelope .segments .append (self .release_envelope )
229
309
self .segments = [self .on_envelope , self .off_envelope ]
230
310
0 commit comments