Skip to content

Commit de33570

Browse files
committed
basic envelope and initial pulse working
1 parent 287dd0b commit de33570

File tree

6 files changed

+251
-181
lines changed

6 files changed

+251
-181
lines changed

birdfish/envelope.py

+108-27
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ def __init__(self, tween=tween.LINEAR, start=0, change=1.0, duration=1.0,
6161
self.duration = float(duration)
6262
self.label = label
6363

64-
assert self.duration > 0 # only a duration > 0 makes sense
64+
# assert self.duration > 0 # only a duration > 0 makes sense
6565

6666
def get_value(self, delta):
6767
if delta > self.duration:
6868
delta = self.duration
6969
# TODO save a self.value here for more consistency with envelopes?
70-
print "updating %s %s at %s" % (self.label, self, delta)
70+
# print "updating %s %s at %s" % (self.label, self, delta)
7171
return self.tween(delta, self.start, self.change, self.duration)
7272

7373
def get_jump_time(self, value):
@@ -90,16 +90,22 @@ def __init__(self, tween=tween.LINEAR, start=0, change=1.0, duration=1.0,
9090
self.elapsed = 0
9191
self.value = 0
9292

93-
assert self.duration > 0 # only a duration > 0 makes sense
93+
# assert self.duration > 0 # only a duration > 0 makes sense
9494

9595
def reset(self):
9696
self.elapsed = 0
9797
self.value = 0
9898

99+
def get_profile(self):
100+
return self.profile
101+
99102
def update(self, delta):
100-
print "updating %s %s at delta %s" % (self.label, self, delta)
103+
# print "updating %s %s at delta %s" % (self.label, self, delta)
101104
self.elapsed += delta
102-
self.value = self.profile.get_value(self.elapsed)
105+
if not self.duration:
106+
self.value = self.profile.start + self.profile.change
107+
else:
108+
self.value = self.profile.get_value(self.elapsed)
103109
return self.value
104110

105111
class StaticEnvelopeSegment(EnvelopeSegment):
@@ -109,6 +115,9 @@ class StaticEnvelopeSegment(EnvelopeSegment):
109115
def __init__(self, *args, **kwargs):
110116
super(StaticEnvelopeSegment, self).__init__(*args, **kwargs)
111117
self.duration = 0
118+
# TODO - this is a hack so sustain doesn't get advanced over
119+
# as part of an on envelope that has no attack
120+
self.duration = 99999999999999999999999999999999
112121

113122
def update(self, delta):
114123
return self.profile.start
@@ -118,16 +127,27 @@ class Envelope(EnvelopeSegment):
118127
def __init__(self, loop=0, label="envelope"):
119128
self.segments = []
120129
self.label = label
130+
self.index = 0
131+
self.advancing = True
121132

122133
# Not sure I need this state
123134
self.running = False
124135
self.paused = 0
125136
self.pause_duration = 0
126137

127138
self.loop = loop # negative loop is infinite looping
128-
self._duration = None
139+
self._duration = 0
129140
self.reset()
130141

142+
def get_profile(self):
143+
print "Envelop profile property, index: ", self.index
144+
print self, self.label
145+
if self.segments:
146+
return self.segments[self.index].profile
147+
elif hasattr(self, 'profile'):
148+
return self.profile
149+
raise RuntimeError("profile not available")
150+
131151
def start(self):
132152
pass
133153

@@ -146,50 +166,69 @@ def reset(self):
146166
self.current_segment_time_delta = 0
147167
for segment in self.segments:
148168
segment.reset()
169+
self.advancing = True
149170

150171
def advance(self):
151172
# return True if advanced
152-
print "advancing"
153-
print self.index, len(self.segments)
173+
# print "advancing ", self.label
174+
# print self.index, len(self.segments)
154175
if self.index + 1 == len(self.segments): # last segment
155176
if self.loop and self.loop_counter:
156-
print 'looping'
177+
# print 'looping'
157178
self.segments[self.index].reset()
158179
self.index = 0
159180
if self.loop > 0: # this is a finite loop
160181
self.loop_counter -= 1
161-
print 'loop counter now: ', self.loop_counter
182+
# print 'loop counter now: ', self.loop_counter
162183
return True
163-
print 'infinite loop counter now: ', self.loop_counter
184+
# print 'infinite loop counter now: ', self.loop_counter
164185
else:
165-
print "not looping on advance - staying at last segment"
186+
# print "not looping on advance - staying at last segment"
166187
# non-looping, or done with final loop
167188
pass
168189
else:
169190
# proceed through sequence of segments
170-
self.segments[self.index].reset()
191+
if self.segments[self.index]:
192+
# TODO - this is a reason to have always have a segment of some sort
193+
# even if it is a null segment, rather than use none
194+
self.segments[self.index].reset()
171195
self.index += 1
172196
self.current_segment_time_delta = 0
197+
print "advanced to %s" % self.segments[self.index].label
173198
return True
199+
self.advancing = False
174200
return False
175201

176202

177203
def update(self, delta):
178-
print '---------------------'
179-
print "updating %s %s at delta %s" % (self.label, self, delta)
204+
# print '---------------------'
205+
# print "updating %s %s at delta %s" % (self.label, self, delta)
180206

181207
# delta is time passed since last update
182208
if self.index + 1 > len(self.segments):
183209
# non looping or end of finite loop
184210
# just reurn last value until something resets index
185211
return self.value
186212
segment = self.segments[self.index]
213+
if not segment.duration:
214+
# for example, no attack value
215+
# self.advance()
216+
pass
187217
self.current_segment_time_delta += delta
188-
print "self current elapsed %s" % self.current_segment_time_delta
189-
if (segment.duration and
190-
(self.current_segment_time_delta > segment.duration)):
218+
print "%s-%s: self current elapsed %s, after delta %s" % (
219+
id(self),
220+
self.label,
221+
self.current_segment_time_delta,
222+
delta,
223+
)
224+
print "current segment ", segment, segment.label
225+
# TODO this is advancing past end of on segemnt,
226+
# when that on segment only contains a 0 duration attack, and no decay
227+
# not going into any sustain
228+
if (self.current_segment_time_delta > segment.duration and
229+
not isinstance(segment, StaticEnvelopeSegment)):
191230
overage = self.current_segment_time_delta - segment.duration
192-
# TODO don't handle case where overage > new segment
231+
# TODO currently don't handle case where overage > new segment
193232
# duration - could need recursion
194233

195234
if self.advance():
@@ -198,6 +237,7 @@ def update(self, delta):
198237
segment = self.segments[self.index]
199238

200239
self.value = segment.update(delta)
240+
print self.value
201241
return self.value
202242

203243
@property
@@ -207,7 +247,14 @@ def duration(self):
207247
return self._duration
208248

209249
def set_duration(self):
210-
self._duration = sum([segment.duration for segment in self.segments if segment.duration > 0])
250+
# a duration of 0 means it is infinite
251+
self._duration = 0
252+
for segment in self.segments:
253+
if segment and segment.duration:
254+
self._duration += segment.duration
255+
else:
256+
self._duration = 0
257+
# self._duration = sum([segment.duration for segment in self.segments if segment.duration > 0])
211258

212259

213260
class TriggeredEnvelope(Envelope):
@@ -233,14 +280,46 @@ def trigger(self, state=1, value=1.0):
233280
self.state = state
234281
if state:
235282
# on trigger
283+
print "trigger on - resetting"
236284
self.reset()
285+
print "%s-%s: self post reset current elapsed %s" % (
286+
id(self),
287+
self.label,
288+
self.current_segment_time_delta,
289+
)
237290
else:
238291
# off trigger
239-
print 'advance'
292+
# print 'advance'
240293
self.advance()
241-
if self.value != self.segments[1].profile.start:
294+
# print self.segments
295+
print "current value: %s" % self.value
296+
print "current change for release: %s" % self.segments[1].get_profile().change
297+
if self.value < self.segments[1].get_profile().start:
298+
# TODO this shortcut works on release, but for attack?
299+
# also need to sort out when in decay (say .9), and release
300+
# start is .8 - now will be greater - want a way to change
301+
# release start value for this time only
302+
# perhaps start value should always just be current value - for when greater
303+
# if dimmer, want shorter release
304+
# if brigher (.9) then want standard release time but greater change
305+
242306
print "shortcutting time"
243-
self.current_segment_time_delta += self.segments[1].profile.get_jump_time(self.value)
307+
jump_value = self.segments[1].get_profile().get_jump_time(self.value)
308+
print jump_value
309+
self.update(jump_value)
310+
# self.current_segment_time_delta += self.segments[1].profile.get_jump_time(self.value)
311+
# self.segments[0].segments[0].current_segment_time_delta = self.current_segment_time_delta
312+
# else:
313+
# print "set change of release"
314+
# TODO - this won't work for multisegment release
315+
# if not hasattr(self.segments[1], 'segments'):
316+
# self.segments[1].segments[0].profile.change = -1 * self.value
317+
# self.segments[1].segments[0].profile.start = self.value
318+
# else:
319+
# print "has segments"
320+
# print self.segments[1].segments
321+
322+
print "new current change for release: %s" % self.segments[1].get_profile().change
244323
self.state = state
245324

246325
def update(self, delta):
@@ -265,10 +344,10 @@ def __init__(self, peak_value=1.0, sustain_value=0.8,
265344
attack_shape=tween.LINEAR, attack_duration=0.5,
266345
decay_shape=tween.LINEAR, decay_duration=.2,
267346
release_shape=tween.LINEAR, release_duration=.5,
268-
bell_mode=False, label='ADSR-envelope'):
347+
bell_mode=False, label='ADSR-envelope', **kwargs):
269348

270349
super(ADSREnvelope, self).__init__(loop=0, label=label)
271-
350+
# print attack_duration
272351
self.attack_envelope = EnvelopeSegment(
273352
tween=attack_shape,
274353
change=peak_value,
@@ -304,10 +383,12 @@ def __init__(self, peak_value=1.0, sustain_value=0.8,
304383
label="release",
305384
)
306385
self.off_envelope = Envelope(label="off-envelope")
307-
if self.release_envelope.profile.change and self.release_envelope.duration:
308-
self.off_envelope.segments.append(self.release_envelope)
386+
# if self.release_envelope.profile.change and self.release_envelope.duration:
387+
self.off_envelope.segments.append(self.release_envelope)
309388
self.segments = [self.on_envelope, self.off_envelope]
310389

390+
# def trigger(self, state=1, value=1.0):
391+
# if trigger off during attack, skip decay
311392

312393
class EnvelopeGenerator(object):
313394
pass

birdfish/input/midi.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
import protomidi.portmidi as pypm
23
import Queue
34
import threading
@@ -184,14 +185,18 @@ def run(self):
184185
f.write(str(m) + '\n')
185186
# print d
186187
# put it into a pipe or queue
188+
count += 1
189+
if not count % 10:
190+
sys.stdout.write('.')
191+
sys.stdout.flush()
187192
self._stopevent.wait(.1)
188193
print "%s ends" % (self.getName(),)
189194

190195

191196

192197
def join (self,timeout=None):
193198
self._stopevent.set()
194-
pypm.quit()
199+
# pypm.quit()
195200
if self.file:
196201
try:
197202
self.file_obj.close()

0 commit comments

Comments
 (0)