Changeset - 460bc5ebcaaf
[Not reviewed]
default
0 2 0
David McClosky - 20 years ago 2005-06-17 07:00:51
dmcc@bigasterisk.com
gyrocontroller can use patch names, Submasters gets get_all_sub_names()
2 files changed with 18 insertions and 2 deletions:
0 comments (0 inline, 0 general)
bin/gyrocontroller
Show inline comments
 
#!/usr/bin/env python
 
# vi: syntax=python
 

	
 
import run_local
 
from light9.Submaster import Submasters, Submaster, combine_subdict
 
from light9.subclient import SubClient
 
import light9.Patch as Patch
 

	
 
import Tix as Tk
 

	
 
# TODO: move to Utility?
 
class circcycle:
 
    """Like itertools.cycle, but with a prev() method too.  You lose
 
    all iterator benefits by using this, since it will store the whole
 
    sequence/iterator in memory.  Also, do not run this on an infinite
 
    iterator, as it tuple'ifies the input."""
 
    def __init__(self, sequence):
 
        self.sequence = tuple(sequence)
 
        self.index = None
 
    def __iter__(self):
 
        return self
 
    def change_index(self, delta):
 
        if self.index is None:
 
            if delta > 0:
 
                self.index = (-1 + delta) % len(self.sequence)
 
            elif delta < 0:
 
                self.index = delta % len(self.sequence)
 
        else:
 
            self.index = (self.index + delta) % len(self.sequence)
 
    def next(self):
 
        self.change_index(1)
 
        return self.sequence[self.index]
 
    def prev(self):
 
        self.change_index(-1)
 
        return self.sequence[self.index]
 

	
 
class AbstractSimpleController(SubClient):
 
    """Simple controller with minimal input and output:
 
    
 
    Input is 4 directions and 3 buttons.
 
    Output is an integer and a color and maybe more.
 
    
 
    Left + B1/right + B1: prev/next sub
 
    Y-axis + B3: set current level
 
    B2: toggle keep/solo mode
 
    Triple-B2: clear kept levels"""
 
    def __init__(self, subnames):
 
        SubClient.__init__(self)
 
        self.subnames = subnames
 
        self.refresh()
 
    def get_sub(self, name):
 
        if name in self.submasters.get_all_sub_names():
 
            return self.submasters.get_sub_by_name(name)
 

	
 
        try:
 
            val = int(name)
 
            s = Submaster("Ch%d" % val, {val : 1.0}, temporary=True)
 
            s = Submaster("#%d" % val, {val : 1.0}, temporary=True)
 
            return s
 
        except ValueError:
 
            return self.submasters.get_sub_by_name(name)
 
            pass
 

	
 
        try:
 
            subnum = Patch.get_dmx_channel(name)
 
            s = Submaster("'%s'" % name, {subnum : 1.0}, temporary=True)
 
            return s
 
        except ValueError:
 
            pass
 

	
 
        # make an error sub
 
        return Submaster('%s!?' % name)
 
    def refresh(self):
 
        # reload subs from disk
 
        self.submasters = Submasters()
 
        self.all_subs = circcycle(self.subnames)
 
        self.current_sub = self.get_sub(self.all_subs.next())
 
        self.current_level = 1.0
 
        self.kept_levels = {} # subname : level [0..1]
 
        self.keep_solo_mode = 'solo' # either 'keep' or 'solo'
 
    def clear_kept_levels(self):
 
        self.kept_levels.clear()
 
    def next(self):
 
        if self.keep_solo_mode == 'keep':
 
            self.kept_levels[self.current_sub] = self.current_level
 

	
 
        self.current_sub = self.get_sub(self.all_subs.next())
 
    def prev(self):
 
        if self.keep_solo_mode == 'keep':
 
            self.kept_levels[self.current_sub] = self.current_level
 

	
 
        self.current_sub = self.get_sub(self.all_subs.prev())
 
    def toggle_keep_mode(self):
 
        if self.keep_solo_mode == 'keep':
 
            self.kept_levels[self.current_sub] = self.current_level
 
            self.keep_solo_mode = 'solo'
 
        else:
 
            self.keep_solo_mode = 'keep'
 

	
 
    def get_levels_as_sub(self):
 
        if self.keep_solo_mode == 'keep':
 
            # send all levels in self.kept_levels
 
            self.kept_levels[self.current_sub] = self.current_level
 
            levelsub = combine_subdict(self.kept_levels)
 
        else:
 
            levelsub = self.current_sub * self.current_level
 

	
 
        return levelsub
 

	
 
class TkGyro(Tk.Canvas, AbstractSimpleController):
 
    def __init__(self, master, subnames):
 
        Tk.Canvas.__init__(self, master, bg='black', bd=0, highlightthickness=0,
 
            confine=None)
 
        AbstractSimpleController.__init__(self, subnames)
 
        height = int(self.winfo_screenheight())
 
        width = int(self.winfo_screenwidth())
 
        self.left = self.create_rectangle((0, 0, width / 2, height),
 
            tags='left', fill='black')
 
        self.right = self.create_rectangle((width / 2, 0, width, height),
 
            tags='right', fill='black')
 
        self.levelbar = self.create_rectangle(0, 0, width, 5, tags='level',
 
            fill='yellow', state='disabled', outline='')
 

	
 
        # the text is disabled so that it doesn't receive events
 
        self.modetext = self.create_text((width / 2, height / 2), 
 
            font='Courier 200', fill='white', text=self.keep_solo_mode,
 
            state='disabled')
 
        self.flashtextafter = '' # current after timer for displaying text
 

	
 
        def setfill(item, color):
 
            self.itemconfig(item, fill=color)
 
        def setlevel(evt):
 
            if evt.state & 0x400 or evt.num == 3:
 
                y = (height - evt.y) / float(height)
 
                self.flash_text('<%d>' % (y * 100))
 
                self.current_level = y
 
                self.coords(self.levelbar, 0, evt.y, width, evt.y + 5)
 
                self.send_levels()
 

	
 
        data = ((self.left, 'left', 'blue', self.prev), 
 
                (self.right, 'right', 'red', self.next))
 
        for item, tag, color, method in data:
 
            self.tag_bind(tag, '<Enter>', 
 
                lambda evt, item=item, color=color: setfill(item, color))
 
            self.tag_bind(tag, '<Leave>', 
 
                lambda evt, item=item, color=color: setfill(item, 'black'))
 
            self.tag_bind(tag, '<ButtonPress-1>', 
 
                lambda evt, item=item, color=color: setfill(item, 'green'), '+')
 
            self.tag_bind(tag, '<ButtonRelease-1>', 
 
                lambda evt, item=item, color=color: setfill(item, color), '+')
 
            self.tag_bind(tag, '<Button-1>', 
 
                lambda evt, method=method: method(), '+')
 

	
 
        # B2+drag sets current level, double-B2 resets kept levels
 
        self.tag_bind('all', '<Motion>', setlevel, '+')
 
        self.tag_bind('all', '<ButtonPress-3>', setlevel, '+')
 
        self.tag_bind('all', '<Triple-Button-2>', 
 
            lambda evt: self.clear_kept_levels())
 
        # B3 toggles between keep and solo mode
 
        self.tag_bind('all', '<Button-2>', lambda evt: self.toggle_keep_mode())
 

	
 
        self.send_levels_loop()
 
    def toggle_keep_mode(self):
 
        AbstractSimpleController.toggle_keep_mode(self)
 
        self.show_current_mode()
 
        self.send_levels()
 
    def show_current_mode(self):
 
        if self.keep_solo_mode == 'keep':
light9/Submaster.py
Show inline comments
 
@@ -80,111 +80,113 @@ class Submaster:
 
                print "error trying to compute dmx levels for submaster %s" % self.name
 
                raise
 
            levels[dmxchan] = max(v, levels[dmxchan])
 

	
 
        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 __cmp__(self, other):
 
        return cmp(repr(self), repr(other))
 
    def __hash__(self):
 
        return hash(repr(self))
 
                                            
 
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):
 
    nonzero_subs = [s for s in subs if not s.no_nonzero()]
 
    name = "max(%s)" % ", ".join([repr(s) for s in nonzero_subs])
 
    return Submaster(name,
 
                     dict_max(*[sub.levels for sub in nonzero_subs]),
 
                     temporary=1)
 

	
 
def combine_subdict(subdict, name=None, permanent=False):
 
    """A subdict is { Submaster objects : levels }.  We combine all
 
    submasters first by multiplying the submasters by their corresponding
 
    levels and then max()ing them together.  Returns a new Submaster
 
    object.  You can give it a better name than the computed one that it
 
    will get or make it permanent if you'd like it to be saved to disk.
 
    Serves 8."""
 
    scaledsubs = [sub * level for sub, level in subdict.items()]
 
    maxes = sub_maxes(*scaledsubs)
 
    if name:
 
        maxes.name = name
 
    if permanent:
 
        maxes.temporary = False
 

	
 
    return maxes
 

	
 
class Submasters:
 
    "Collection o' Submaster objects"
 
    def __init__(self):
 
        self.submasters = {}
 

	
 
        files = os.listdir(showconfig.subsDir())
 

	
 
        for filename in files:
 
            # we don't want these files
 
            if filename.startswith('.') or filename.endswith('~') or \
 
               filename.startswith('CVS'):
 
                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
 
    def get_all_sub_names(self):
 
        return [s.name for s in self.get_all_subs()]
 
    def get_sub_by_name(self, name):
 
        "Makes a new sub if there isn't one."
 
        return self.submasters.get(name, Submaster(name))
 
    __getitem__ = get_sub_by_name
 

	
 
if __name__ == "__main__":
 
    Patch.reload_data()
 
    s = Submasters()
 
    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
0 comments (0 inline, 0 general)