Changeset - 5670f66845ce
[Not reviewed]
default
0 4 0
dmcc - 22 years ago 2003-06-14 15:00:47

- results of work from 6.13 rehearsal
4 files changed with 67 insertions and 115 deletions:
0 comments (0 inline, 0 general)
flax/Submaster.py
Show inline comments
 
@@ -8,17 +8,14 @@ import Patch
 

	
 
class Submaster:
 
    "Contain a dictionary of levels, but you didn't need to know that"
 
    def __init__(self, name, leveldict=None, temporary=0):
 
    def __init__(self, name, leveldict=None):
 
        self.name = name
 
        self.temporary = temporary
 
        if leveldict:
 
            self.levels = leveldict
 
        else:
 
            self.levels = {}
 
            self.reload()
 
    def reload(self):
 
        if self.temporary:
 
            return
 
        try:
 
            self.levels.clear()
 
            subfile = file("subs/%s" % self.name)
 
@@ -36,9 +33,6 @@ class Submaster:
 
        except IOError:
 
            print "Can't read file for sub: %s" % self.name
 
    def save(self):
 
        if self.temporary:
 
            return
 

	
 
        subfile = file("subs/%s" % self.name, 'w')
 
        names = self.levels.keys()
 
        names.sort()
 
@@ -58,62 +52,24 @@ class Submaster:
 
        return self.levels
 
    def __mul__(self, scalar):
 
        return Submaster("%s*%s" % (self.name, scalar), 
 
            dict_scale(self.levels, scalar), temporary=1)
 
            dict_scale(self.levels, scalar))
 
    __rmul__ = __mul__
 
    def max(self, *othersubs):
 
        return sub_maxes(self, *othersubs)
 
    def __repr__(self):
 
        levels = ' '.join(["%s:%.2f" % item for item in self.levels.items()])
 
        return "<'%s': [%s]>" % (self.name, levels)
 
        return "<%s: %r>" % (self.name, self.levels)
 
    def get_dmx_list(self):
 
        leveldict = self.get_levels() # gets levels of sub contents
 

	
 
        levels = [0] * 68
 
        for k, v in leveldict.items():
 
            dmxchan = Patch.get_dmx_channel(k) - 1
 
            levels[dmxchan] = max(v, levels[dmxchan])
 
            levels[Patch.get_dmx_channel(k) - 1] = v
 

	
 
        return levels
 
    def normalize_patch_names(self):
 
        """Use only the primary patch names."""
 
        # possibly busted -- don't use unless you know what you're doing
 
        self.set_all_levels(self.levels.copy())
 
    def get_normalized_copy(self):
 
        """Get a copy of this sumbaster that only uses the primary patch 
 
        names.  The levels will be the same."""
 
        newsub = Submaster("%s (normalized)" % self.name, temporary=1)
 
        newsub.set_all_levels(self.levels)
 
        return newsub
 
    def crossfade(self, othersub, amount):
 
        """Returns a new sub that is a crossfade between this sub and
 
        another submaster.  
 
        
 
        NOTE: You should only crossfade between normalized submasters."""
 
        otherlevels = othersub.get_levels()
 
        keys_set = {}
 
        for k in self.levels.keys() + otherlevels.keys():
 
            keys_set[k] = 1
 
        all_keys = keys_set.keys()
 

	
 
        xfaded_sub = Submaster("xfade", temporary=1)
 
        for k in all_keys:
 
            xfaded_sub.set_level(k, 
 
                                 linear_fade(self.levels.get(k, 0),
 
                                             otherlevels.get(k, 0),
 
                                             amount))
 

	
 
        return xfaded_sub
 
                                            
 
def linear_fade(start, end, amount):
 
    """Fades between two floats by an amount.  amount is a float between
 
    0 and 1.  If amount is 0, it will return the start value.  If it is 1,
 
    the end value will be returned."""
 
    level = start + (amount * (end - start))
 
    return level
 

	
 
def sub_maxes(*subs):
 
    return Submaster("max(%r)" % (subs,),
 
        dict_max(*[sub.levels for sub in subs]), temporary=1)
 
        dict_max(*[sub.levels for sub in subs]))
 

	
 
class Submasters:
 
    "Collection o' Submaster objects"
 
@@ -124,25 +80,12 @@ class Submasters:
 
        files = os.listdir('subs')
 

	
 
        for filename in files:
 
            # we don't want these files
 
            if filename.startswith('.') or filename.endswith('~') or \
 
               filename.startswith('CVS'):
 
            if filename.startswith('.') or filename.endswith('~'):
 
                continue
 
            self.submasters[filename] = Submaster(filename)
 
    def get_all_subs(self):
 
        "All Submaster objects"
 
        l = self.submasters.items()
 
        l.sort()
 
        l = [x[1] for x in l]
 
        songs = []
 
        notsongs = []
 
        for s in l:
 
            if s.name.startswith('song'):
 
                songs.append(s)
 
            else:
 
                notsongs.append(s)
 
        combined = notsongs + songs
 
        return combined
 
        return self.submasters.values()
 
    def get_sub_by_name(self, name):
 
        "Makes a new sub if there isn't one."
 
        return self.submasters.get(name, Submaster(name))
 
@@ -151,10 +94,6 @@ class Submasters:
 
if __name__ == "__main__":
 
    Patch.reload_data()
 
    s = Submasters()
 
    newsub = s['newsub']
 
    newsub.set_all_levels({'5' : 1, '7': 0.2})
 
    print s.get_all_subs()
 
    if 0: # turn this on to normalize all subs
 
        for sub in s.get_all_subs():
 
            print "before", sub
 
            sub.normalize_patch_names()
 
            sub.save()
 
            print "after", sub
flax/TheShow.py
Show inline comments
 

	
 

	
 
from Timeline import *
 
from Submaster import Submasters, sub_maxes
 

	
 
@@ -34,9 +36,6 @@ class Show:
 
        "set time of current timeline"
 
        self.current_time = time
 
        self.current_timeline.set_time(time)
 
    def get_timelines(self):
 
        "Get names of all timelines"
 
        return self.timelines.keys()
 

	
 
# this is the default blender
 
linear = LinearBlender()
flax/Timeline.py
Show inline comments
 
@@ -22,16 +22,20 @@ class MissingBlender(Exception):
 
FORWARD = 1
 
BACKWARD = -1
 

	
 
MISSING = 'missing'
 

	
 
class TimedEvent:
 
    """Container for a Frame which includes a time that it occurs at,
 
    and which blender occurs after it."""
 
    def __init__(self, time, frame, blender=None, level=1.0):
 
    def __init__(self, time, frame=MISSING, blender=None, level=1.0):
 
        make_attributes_from_args('time', 'frame')
 
        self.next_blender = blender
 
        self.level = level
 
    def __float__(self):
 
        return self.time
 
    def __cmp__(self, other):
 
        if other is None:
 
            raise "I can't compare with a None.  I am '%s'" % str(self)
 
        if type(other) in (float, int):
 
            return cmp(self.time, other)
 
        else:
 
@@ -48,11 +52,12 @@ class Blender:
 
    """Blenders are functions that merge the effects of two LevelFrames."""
 
    def __init__(self):
 
        pass
 
    def __call__(self, startframe, endframe, blendtime):
 
    def __call__(self, startframe, endframe, blendtime,  time_since_startframe):
 
        """Return a LevelFrame combining two LevelFrames (startframe and 
 
        endframe).  blendtime is how much of the blend should be performed
 
        and will be expressed as a percentage divided by 100, i.e. a float
 
        between 0.0 and 1.0.  
 
        between 0.0 and 1.0.  time_since_startframe is the time since the
 
        startframe was on screen in seconds (float).
 
        
 
        Very important note: Blenders will *not* be asked for values
 
        at end points (i.e. blendtime=0.0 and blendtime=1.0).
 
@@ -74,24 +79,20 @@ class Blender:
 
        0.25 * endframe.  This function is included since many blenders are
 
        just functions on the percentage and still combine start and end frames
 
        in this way."""
 
        # print "linear_blend", startframe, endframe, blendtime
 
        if startframe.frame == endframe.frame:
 
            # print "same frames"
 
            startlevel = startframe.level * (1.0 - blendtime)
 
            endlevel = endframe.level * blendtime
 
            levels = {startframe.frame : max(startlevel, endlevel)}
 
            level = startframe.level + (blendtime * \
 
                (endframe.level - startframe.level))
 
            levels = {startframe.frame : level}
 
        else:
 
            # print "diff frames"
 
            levels = {startframe.frame : (1.0 - blendtime) * startframe.level,
 
                endframe.frame : blendtime * endframe.level}
 
        # print "return", levels
 
        return levels
 

	
 
class InstantEnd(Blender):
 
    """Instant change from startframe to endframe at the end.  In other words,
 
    the value returned will be the startframe all the way until the very end
 
    of the blend."""
 
    def __call__(self, startframe, endframe, blendtime):
 
    def __call__(self, startframe, endframe, blendtime, time_since_startframe):
 
        # "What!?" you say, "Why don't you care about blendtime?"
 
        # This is because Blenders never be asked for blenders at the endpoints
 
        # (after all, they wouldn't be blenders if they were). Please see
 
@@ -102,7 +103,7 @@ class InstantStart(Blender):
 
    """Instant change from startframe to endframe at the beginning.  In other
 
    words, the value returned will be the startframe at the very beginning
 
    and then be endframe at all times afterwards."""
 
    def __call__(self, startframe, endframe, blendtime):
 
    def __call__(self, startframe, endframe, blendtime, time_since_startframe):
 
        # "What!?" you say, "Why don't you care about blendtime?"
 
        # This is because Blenders never be asked for blenders at the endpoints
 
        # (after all, they wouldn't be blenders if they were). Please see
 
@@ -111,7 +112,7 @@ class InstantStart(Blender):
 

	
 
class LinearBlender(Blender):
 
    """Linear fade from one frame to another"""
 
    def __call__(self, startframe, endframe, blendtime):
 
    def __call__(self, startframe, endframe, blendtime, time_since_startframe):
 
        return self.linear_blend(startframe, endframe, blendtime)
 

	
 
class ExponentialBlender(Blender):
 
@@ -120,7 +121,7 @@ class ExponentialBlender(Blender):
 
    as LinearBlender."""
 
    def __init__(self, exponent):
 
        self.exponent = exponent
 
    def __call__(self, startframe, endframe, blendtime):
 
    def __call__(self, startframe, endframe, blendtime, time_since_startframe):
 
        blendtime = blendtime ** self.exponent
 
        return self.linear_blend(startframe, endframe, blendtime)
 

	
 
@@ -129,19 +130,42 @@ class ExponentialBlender(Blender):
 
class SmoothBlender(Blender):
 
    """Drew's "Smoove" Blender function.  Hopefully he'll document and
 
    parametrize it."""
 
    def __call__(self, startframe, endframe, blendtime):
 
    def __call__(self, startframe, endframe, blendtime, time_since_startframe):
 
        blendtime = (-1 * blendtime) * blendtime * (blendtime - 1.5) * 2
 
        return self.linear_blend(startframe, endframe, blendtime)
 

	
 
class Strobe(Blender):
 
    "Strobes the frame on the right side between offlevel and onlevel."
 
    def __init__(self, ontime, offtime, onlevel=1, offlevel=0):
 
        "times are in seconds"
 
        make_attributes_from_args('ontime', 'offtime', 'onlevel', 'offlevel')
 
        self.cycletime = ontime + offtime
 
    def __call__(self, startframe, endframe, blendtime, time_since_startframe):
 
        # time into the current period
 
        period_time = time_since_startframe % self.cycletime
 
        if period_time <= self.ontime:
 
            return {endframe.frame : self.onlevel}
 
        else:
 
            return {endframe.frame : self.offlevel}
 

	
 
class TimelineTrack:
 
    """TimelineTrack is a single track in a Timeline.  It consists of a
 
    list of TimedEvents and a name.  Length is automatically the location
 
    of the last TimedEvent.  To extend the Timeline past that, add an
 
    EmptyTimedEvent (which doesn't exist :-/)."""
 
    def __init__(self, name, *timedevents):
 
    def __init__(self, name, *timedevents, **kw):
 
        if kw.get('default_frame'):
 
            self.default_frame = kw['default_frame']
 
        else:
 
            self.default_frame = None
 
        self.name = name
 
        self.events = list(timedevents)
 
        self.events.sort()
 
        self.fill_in_missing_subs()
 
    def fill_in_missing_subs(self):
 
        for event in self.events:
 
            if event.frame == MISSING:
 
                event.frame = self.default_frame
 
    def __str__(self):
 
        return "<TimelineTrack with events: %r>" % self.events
 
    def has_events(self):
 
@@ -206,10 +230,10 @@ class TimelineTrack:
 
            percent = elapsed / diff
 
            if not before.next_blender:
 
                raise MissingBlender, before
 
            return before.next_blender(before, after, percent)
 
            return before.next_blender(before, after, percent, elapsed)
 

	
 
class Timeline:
 
    def __init__(self, tracks, rate=1, direction=FORWARD):
 
    def __init__(self, name, tracks, rate=1, direction=FORWARD):
 
        """
 
        Most/all of this is old:
 

	
 
@@ -223,7 +247,7 @@ class Timeline:
 
        is bounded by their last frame.  You can put an EmptyFrame at
 
        some time if you want to extend a Timeline."""
 
        
 
        make_attributes_from_args('tracks', 'rate', 'direction')
 
        make_attributes_from_args('name', 'tracks', 'rate', 'direction')
 
        self.current_time = 0
 
        self.last_clock_time = None
 
        self.stopped = 1
 
@@ -323,7 +347,7 @@ if __name__ == '__main__':
 
        T(18, 'blue', level=1.0),
 
        T(20, 'blue', level=0.0))
 

	
 
    tl = Timeline([track1, track2, track3])
 
    tl = Timeline('test', [track1, track2, track3])
 

	
 
    tl.play()
 

	
flax/TimelineDMX.py
Show inline comments
 
import sys, time, socket
 
sys.path.append("../light8")
 
import Tix as tk
 

	
 
import Patch, Timeline, dmxclient, xmlrpclib
 
import TheShow
 

	
 
Patch.reload_data()
 

	
 
class ShowRunner(tk.Frame):
 
    def __init__(self, master, show):
 
        tk.Frame.__init__(self, master)
 
        self.master = master
 

	
 
class ShowRunner:
 
    def __init__(self, show):
 
        self.show = show
 
        self.find_player()
 
        self.build_timeline_list()
 
    def build_timeline_list(self):
 
        self.tl_list = tk.Frame(self)
 
        for tl in self.show.get_timelines():
 
            b=tk.Button(self.tl_list,text=tl,
 
                        anchor='w',pady=1)
 
            b.config(command=lambda tl=tl: self.set_timeline(tl))
 
            b.pack(side='top',fill='x')
 
        self.tl_list.pack()
 
    def set_timeline(self, tlname):
 
        print "TimelineDMX: set timeline to", tlname
 
        self.show.set_timeline(tlname)
 
    def find_player(self):
 
        self.player = xmlrpclib.Server("http://localhost:8040")
 
    def send_levels(self):
 
        """
 
        sub = self.show.get_levels() # gets levels of subs
 
        leveldict = sub.get_levels() # gets levels of sub contents
 
        print 'resolved levels', leveldict
 

	
 
        levels = [0] * 68
 
        for k, v in leveldict.items():
 
            levels[Patch.get_dmx_channel(k)] = v
 
        """
 
        levels = self.show.calc_active_submaster().get_dmx_list()
 
        
 
        dmxclient.outputlevels(levels)
 
@@ -45,13 +38,10 @@ class ShowRunner(tk.Frame):
 
                self.sync_times()
 
                self.send_levels()
 
                time.sleep(0.01)
 
                self.master.update()
 
        except KeyboardInterrupt:
 
            sys.exit(0)
 

	
 
if __name__ == "__main__":
 
    root = tk.Tk()
 
    s = ShowRunner(root, TheShow.show)
 
    s = ShowRunner(TheShow.show)
 
    s.show.set_timeline('strobe test')
 
    s.pack()
 
    s.mainloop()
0 comments (0 inline, 0 general)