def advance(self): # return True if advanced if self.index + 1 == len(self.segments): # last segment if self.loop: self.segments[self.index].reset() self.index = 0 self.current_segment_time_delta = 0 if self.loop_counter and self.loop > 0: # this is a finite loop self.loop_counter -= 1 return True else: # non-looping, or done with final loop pass else: # proceed through sequence of segments if self.segments[self.index]: # TODO - this is a reason to always have a segment of some sort # even if it is a null segment, rather than use none self.segments[self.index].reset() self.index += 1 self.current_segment_time_delta = 0 logger.debug("advanced to %s" % self.segments[self.index].label) return True self.advancing = False return False
def _off_trigger(self): self.trigger_state = 0 # TODO - setting trigger intensity to 0 here - works for sweeps # but non-sweep has to use trigger 1 as it doesn't have access to the # original trigger_intensity self.trigger_intensity = 0 # if self.bell_mode: # TODO does bell apply to chase classes? # ignore release in bell mode # return logger.debug("%s: pulse trigger off" % self.name) self.reset_positions() if self.off_mode == "all": # TODO some cleanup needed - moving set to false in # reset_positions, need to more clearly define between # these functions what does what self.moving = False for e in self.elements: # blackout e.trigger(0) elif self.off_mode in ["follow", "reverse"]: # reset the chase to follow itself as trigger off # TODO - placeholder, not sure anything needs to be done self.moving = True
def trigger(self, intensity, **kwargs): # @@ need toggle mode implementation here if self.simple: # print "BaseLightElement triggered" self.set_intensity(intensity) return if intensity > 0 and self.trigger_state == 0: if self.bell_mode: self.bell_reset() self.trigger_state = 1 [x.trigger(intensity) for x in self.effects] self.trigger_intensity = intensity logger.debug("%s: trigger on @ %s" % (self.name, intensity)) self.set_intensity(0.0) # reset light on trigger self.adsr_envelope.trigger(state=1) self._on_trigger(intensity, **kwargs) elif intensity == 0 and (self.trigger_state and not self.trigger_toggle and not self.bell_mode): self._off_trigger() elif intensity and self.trigger_state and self.trigger_toggle: self._off_trigger() elif intensity > self.intensity and self.trigger_state == 1: # a greater trigger intensity has occured - override self.trigger_intensity = intensity logger.debug("%s: override trigger on @ %s" % (self.name, intensity)) self.intensity = 0.0 # reset light on trigger # reset the envelope with a forced on trigger self.adsr_envelope.trigger(state=1, force=True)
def trigger(self, state=1, value=1.0, force=False): """ state is on or off value is 0-1 for max value of tween - currently not handled from here - must be set at segment level can be used to scale max value on callers end - ie midi velocity value of 0 is also implicit state=0 """ assert len(self.segments) == 2, "too many segments for a \ trigger envelope" # TODO I think the whole value arg and associated code needs to go # this is only about state if value == 0: state = 0 if force: self.state = not state if self.state != state: self.state = state if state: # on trigger logger.debug("envelope trigger on - resetting") self.reset() logger.debug("%s-%s: self post reset current elapsed %s" % ( id(self), self.label, self.current_segment_time_delta, )) else: # off trigger self.advance() logger.debug("current value: %s" % self.value) logger.debug("current change for release: %s" % self.segments[1].get_profile().change) if self.value < self.segments[1].get_profile().start: # TODO this shortcut works on release, but for attack? # also need to sort out when in decay (say .9), and release # start is .8 - now will be greater - want a way to change # release start value for this time only # perhaps start value should always just be current value # - for when greater if dimmer, want shorter release if # brigher (.9) then want standard release time but greater # change jump_value = self.segments[1].get_profile().get_jump_time( self.value) self.update(jump_value) # TODO - this won't work for multisegment release # if not hasattr(self.segments[1], 'segments'): # self.segments[1].segments[0].profile.change = -1 * self.value # self.segments[1].segments[0].profile.start = self.value # else: # print "has segments" # print self.segments[1].segments logger.debug("new current change for release: %s" % self.segments[1].get_profile().change) self.state = state
def _move_completed(self): # called at the end of a move, for looping, pong, etc # TODO while pulse paused at one end - this is firing multiple # times if self.continuation_mode == 'pong': if round(self.center_position) == self.end_pos: logger.debug("%s pong-end @ %s" % (self.name, self.end_pos)) self.moveto = self.start_pos if round(self.center_position) == self.start_pos: self.moveto = self.end_pos elif self.continuation_mode == 'loop': # TODO the last_center reset is an easy one to miss, and should # be built into something else self.last_center = self.center_position = self.start_pos self.setup_move() else: self.moving = False self.move_complete = True if self.bell_mode and self.trigger_state: self._off_trigger()
def set_current_nodes(self): """ the node array becomes a list of values - generally for intensity that describes the left and right shape of the pulse around the center_position. The node_range specifies the location start and end of the pulse overall """ node_offset = self.center_position % 1 left_of_center = math.floor(self.center_position) far_left = int(left_of_center - self.left_width) self.nodes = [] for n in range(self.left_width + 1): self.nodes.append(self.left_shape( n + node_offset, 1, -1, self.left_width + 1.0)) if far_left >= 1: self.nodes.append(0) far_left -= 1 self.nodes.reverse() for n in range(1, self.right_width + 1): self.nodes.append(self.right_shape( max(0, n - node_offset), 1, -1, self.right_width + 1.0)) self.nodes.append(0) self.node_range = range(far_left, far_left + len(self.nodes)) logger.debug("NodeData:") logger.debug(self.node_range) logger.debug(self.nodes)
def update(self, delta): # delta is time passed since last update if self.index + 1 > len(self.segments): # non looping or end of finite loop # just reurn last value until something resets index return self.value segment = self.current_segment if not segment.duration: # for example, no attack value # self.advance() pass self.current_segment_time_delta += delta logger.debug("%s-%s: self current elapsed %s, after delta %s" % ( id(self), self.label, self.current_segment_time_delta, delta, )) logger.debug("current segment %s" % segment.label) # TODO this is advancing past end of on segemnt, # when that on segment only contains a 0 duration attack, and no decay # not going into any sustain if (self.current_segment_time_delta > segment.duration and not isinstance(segment, StaticEnvelopeSegment)): overage = self.current_segment_time_delta - segment.duration logger.debug("overage: %s" % overage) # TODO currently don't handle case where overage > new segment # duration - could need recursion if self.advance(): logger.debug('advanced, new delta: %s' % overage) delta = self.current_segment_time_delta = overage segment = self.segments[self.index] else: logger.debug("did not advance after overage") self.value = segment.update(delta) return self.value
def trigger(self, intensity, **kwargs): if intensity > 0 and self.trigger_state == 0: # or note off message if self.moving: # we are already in either in an active on or off chase # TODO - do we reset everything - draw on top...? # print "Already moving" return # self.reset_positions() # TODO so reset positions only for off trigger? self.trigger_state = 1 self.trigger_intensity = intensity self.center_position = self.last_center = self.start_pos self.moveto = self.end_pos logger.debug("%s: chase trigger on @ %s" % (self.name, intensity)) self.moving = True self.setup_move() self._on_trigger(intensity, **kwargs) elif intensity == 0 and (self.trigger_state and not self.trigger_toggle and not self.bell_mode): self._off_trigger() elif intensity and self.trigger_state and self.trigger_toggle: logger.info("%s: chase trigger toggle off @ %s" % (self.name, intensity)) self._off_trigger()
def update(self, show): """ The update method is called once per iteration of the main show loop. """ if (self.simple or not (self.update_active)): # light is inactive or in sustain mode # print 'inactive intensity: ', self.name, self.intensity return self.intensity if self.bell_mode and self.adsr_envelope.segments[0].index == 1: # bell mode ignores trigger off - simulate trigger off once # sustain levels are reached self.bell_reset() return if self.adsr_envelope.advancing: intensity_scale = self.adsr_envelope.update(show.time_delta) self.set_intensity(self.trigger_intensity * intensity_scale) elif self.trigger_intensity: logger.debug(self.name) logger.debug('not advancing, intensity: {}'.format(self.intensity)) self.trigger_intensity = 0.0 self.intensity = max(0, self.intensity) logger.debug('not advancing, intensity: {}'.format(self.intensity)) logger.debug('not advancing, trigger intensity: {}'.format( self.trigger_intensity)) # only turn off effects here so they can continue to effect # releases [x.trigger(0) for x in self.effects] # moved dmx update to show update, to accomodate effects # self.dmx_update(show.universes[self.universe].dmx) # if self.last_used_intensity != self.intensity: # print int(self.intensity) for effect in self.effects: effect.update(show, [self]) self.device.set_intensity(self.intensity) #print 'intensity: ', self.name, self.intensity return self.intensity
def get_profile(self): logger.debug("Envelop %s profile property, index: %s" % (self.label, self.index)) end_segment = self.get_current_segment() return end_segment.profile
def update(self, show): super(PulseChase, self).update(show) logger.debug("%s Centered @ %s -> %s" % (self.name, self.center_position, self.end_pos))
def _off_trigger(self): self.trigger_state = 0 logger.debug("%s: trigger off" % self.name) self.adsr_envelope.trigger(state=0)