changeset 135:5670f66845ce

- results of work from 6.13 rehearsal
author dmcc
date Sat, 14 Jun 2003 15:00:47 +0000
parents f2f73a2171e6
children 7e91c49fb2d6
files flax/Submaster.py flax/TheShow.py flax/Timeline.py flax/TimelineDMX.py
diffstat 4 files changed, 67 insertions(+), 115 deletions(-) [+]
line wrap: on
line diff
--- a/flax/Submaster.py	Sat Jun 14 14:59:09 2003 +0000
+++ b/flax/Submaster.py	Sat Jun 14 15:00:47 2003 +0000
@@ -8,17 +8,14 @@
 
 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 @@
         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 @@
         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 @@
         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 @@
 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
--- a/flax/TheShow.py	Sat Jun 14 14:59:09 2003 +0000
+++ b/flax/TheShow.py	Sat Jun 14 15:00:47 2003 +0000
@@ -1,3 +1,5 @@
+
+
 from Timeline import *
 from Submaster import Submasters, sub_maxes
 
@@ -34,9 +36,6 @@
         "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()
--- a/flax/Timeline.py	Sat Jun 14 14:59:09 2003 +0000
+++ b/flax/Timeline.py	Sat Jun 14 15:00:47 2003 +0000
@@ -22,16 +22,20 @@
 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 @@
     """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 @@
         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 @@
     """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 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 @@
     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 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 @@
             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 @@
         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 @@
         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()
 
--- a/flax/TimelineDMX.py	Sat Jun 14 14:59:09 2003 +0000
+++ b/flax/TimelineDMX.py	Sat Jun 14 15:00:47 2003 +0000
@@ -1,34 +1,27 @@
 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 @@
                 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()