Changeset - 70959bb51ab3
[Not reviewed]
default
0 4 0
Drew Perttula - 9 years ago 2016-06-12 12:37:24
drewp@bigasterisk.com
full page, 4-row TL. start doing static fx settings on notes
Ignore-this: b581abc51d10e0c2dc62943e4fb3bebb
4 files changed with 84 insertions and 66 deletions:
0 comments (0 inline, 0 general)
light9/effect/effecteval.py
Show inline comments
 
@@ -43,15 +43,12 @@ class EffectEval(object):
 
        self.graph = graph
 
        self.effect = effect 
 

	
 
        # effect : [(dev, attr, value, isScaled)]
 
        self.effectOutputs = {}
 
        
 
        #for ds in g.objects(g.value(uri, L9['effectClass']), L9['deviceSetting']):
 
        #    self.setting = (g.value(ds, L9['device']), g.value(ds, L9['attr']))
 

	
 
        self.graph.addHandler(self.updateEffectsFromGraph)
 

	
 
    def updateEffectsFromGraph(self):
 
        self.effectOutputs = {}
 
        for effect in self.graph.subjects(RDF.type, L9['Effect']):
 
            settings = []
 
@@ -64,76 +61,93 @@ class EffectEval(object):
 
                    raise NotImplementedError
 

	
 
                settings.append((d, a, v if v is not None else sv, bool(sv)))
 

	
 
            if settings:
 
                self.effectOutputs[effect] = settings
 
            # also have to read eff :effectAttr [ :tint x; :tintStrength y ]
 
        
 
    def outputFromEffect(self, effectSettings, songTime):
 
        """
 
        From effect attr settings, like strength=0.75, to output device
 
        settings like light1/bright=0.72;light2/bright=0.78. This runs
 
        the effect code.
 
        """
 
        attr, strength = effectSettings[0]
 
        strength = float(strength)
 
        assert attr == L9['strength']
 
        # both callers need to apply note overrides
 
        effectSettings = dict(effectSettings) # we should make everything into nice float and Color objects too
 

	
 
        strength = float(effectSettings[L9['strength']])
 
        if strength <= 0:
 
            return []
 

	
 
        out = {} # (dev, attr): value
 

	
 
        out.update(self.simpleOutput(strength))
 

	
 
        out = []
 
        if self.effect.startswith(L9['effect/']):
 
            tail = 'effect_' + self.effect[len(L9['effect/']):]
 
            try:
 
                func = globals()[tail]
 
            except KeyError:
 
                pass
 
            else:
 
                out.update(func(effectSettings, strength, songTime))
 

	
 
        # todo: callers should prefer the dict form too
 
        outList = [(d, a, v) for (d, a), v in out.iteritems()]
 
        outList.sort()
 
        #import pprint; pprint.pprint(outList, width=170)
 
        return outList
 
                            
 
    def simpleOutput(self, strength):
 
        out = {}
 
        for dev, attr, value, isScaled in self.effectOutputs.get(self.effect, []):
 
            if isScaled:
 
                value = scale(value, strength)
 
            out.append((dev, attr, value))
 
            
 
        if self.effect == L9['effect/RedStripzzz']: # throwaway
 
            mov = URIRef('http://light9.bigasterisk.com/device/moving1')
 
            col = [
 
                    ((songTime + .0) % 1.0),
 
                    ((songTime + .4) % 1.0),
 
                    ((songTime + .8) % 1.0),
 
                ]
 
            out.extend([
 
                # device, attr, lev
 
                
 
                (mov, L9['color'], Literal(rgb_to_hex([strength*x*255 for x in col]))),
 
                (mov, L9['rx'], Literal(100 + 70 * math.sin(songTime*2))),
 
            ] * (strength>0))
 
        elif self.effect == L9['effect/Curtain']:
 
            out.extend([
 
                (L9['device/lowPattern%s' % n], L9['color'],
 
                 literalColor(0*strength, strength, strength))
 
                for n in range(301,308+1)
 
                ])
 
        elif self.effect == L9['effect/animRainbow']:
 
            for n in range(1, 5+1):
 
                scl = strength * nsin(songTime + n * .3)**3
 
                col = literalColor(
 
                        scl * nsin(songTime + n * .2),
 
                        scl * nsin(songTime + n * .2 + .3),
 
                        scl * nsin(songTime + n * .3 + .6))
 
                col = literalColor(scl * .6, scl * 0, scl * 1)
 
                dev = L9['device/aura%s' % n]
 
                out.append((dev, L9['color'], col))
 
                out.append((dev, L9['zoom'], .9))
 
                ang = songTime * 4
 
                out.append((dev, L9['rx'], lerp(.27, .7, (n-1)/4) + .2 * math.sin(ang+n)))
 
                out.append((dev, L9['ry'], lerp(.46, .52, (n-1)/4) + .5 * math.cos(ang+n)))
 
                    
 
        elif self.effect == L9['effect/Strobe']:
 
            attr, value = effectSettings[0]
 
            assert attr == L9['strength']
 
            strength = float(value)
 
            rate = 2
 
            duty = .3
 
            offset = 0
 
            f = (((songTime + offset) * rate) % 1.0)
 
            c = (f < duty) * strength
 
            col = rgb_to_hex([c * 255, c * 255, c * 255])
 
            out.extend([
 
                (L9['device/colorStrip'], L9['color'], Literal(col)),
 
            ])
 

	
 
            out[(dev, attr)] = value
 
        return out
 
        
 

	
 

	
 
    
 

	
 
def effect_Curtain(effectSettings, strength, songTime):
 
    return {
 
        (L9['device/lowPattern%s' % n], L9['color']):
 
        literalColor(0*strength, strength, strength)
 
        for n in range(301,308+1)
 
        }
 
    
 
def effect_animRainbow(effectSettings, strength, songTime):
 
    out = {}
 
    tint = effectSettings.get(L9['tint'], '#ffffff')
 
    tintStrength = float(effectSettings.get(L9['tintStrength'], 0))
 
    print tint, tintStrength
 
    tr, tg, tb = hex_to_rgb(tint)
 
    for n in range(1, 5+1):
 
        scl = strength * nsin(songTime + n * .3)**3
 
        col = literalColor(
 
            scl * lerp(nsin(songTime + n * .2), tr/255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .2 + .3), tg/255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .3 + .6), tb/255, tintStrength))
 

	
 
        dev = L9['device/aura%s' % n]
 
        out.update({
 
            (dev, L9['color']): col,
 
            (dev, L9['zoom']): .9,
 
            })
 
        ang = songTime * 4
 
        out.update({
 
        (dev, L9['rx']): lerp(.27, .7, (n-1)/4) + .2 * math.sin(ang+n),
 
        (dev, L9['ry']): lerp(.46, .52, (n-1)/4) + .5 * math.cos(ang+n),
 
            })
 
    return out
 

	
 
def effect_Strobe(effectSettings, strength, songTime):
 
    rate = 2
 
    duty = .3
 
    offset = 0
 
    f = (((songTime + offset) * rate) % 1.0)
 
    c = (f < duty) * strength
 
    col = rgb_to_hex([c * 255, c * 255, c * 255])
 
    return {(L9['device/colorStrip'], L9['color']): Literal(col)}
 

	
light9/effect/sequencer.py
Show inline comments
 
@@ -137,13 +137,10 @@ class Sequencer(object):
 
            return
 
        t = musicState['t']
 

	
 
        settings = []
 
        
 
        for note in self.notes.get(song, []):
 
            # we have to send zeros to make past settings go
 
            # away. might be better for collector not to merge our
 
            # past requests, and then we can omit zeroed notes?
 
            outs = note.outputSettings(t)
 
            #print 'out', outs
 
            settings.extend(outs)
 
        self.sendToCollector(settings)
light9/web/timeline/index.html
Show inline comments
 
<!doctype html>
 
<html>
 
  <head>
 
    <title>timeline</title>
 
    <meta charset="utf-8" />
 
    <script src="/lib/webcomponentsjs/webcomponents-lite.min.js"></script>
 
    <link rel="import" href="timeline-elements.html"
 
    <link rel="import" href="timeline-elements.html">
 
  </head>
 
  <body>
 
    <light9-timeline-editor style="width: 100%; height: 400px">
 
    <light9-timeline-editor style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px;">
 
    </light9-timeline-editor>
 
  </body>
 
</html>
light9/web/timeline/timeline.coffee
Show inline comments
 
log = console.log
 
RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'
 

	
 
ROW_COUNT = 4
 

	
 
# polymer dom-repeat is happy to shuffle children by swapping their
 
# attribute values, and it's hard to correctly setup/teardown your
 
# side effects if your attributes are changing before the detach
 
# call. This alternative to dom-repeat never reassigns
 
# attributes. But, it can't set up property bindings.
 
@@ -247,13 +248,13 @@ Polymer
 
  behaviors: [ Polymer.IronResizableBehavior ]
 
  properties:
 
    graph: { type: Object, notify: true }
 
    dia: { type: Object, notify: true }
 
    song: { type: String, notify: true }
 
    zoomInX: { type: Object, notify: true }
 
    rows: { value: [0] }
 
    rows: { value: [0...ROW_COUNT] }
 
    zoom: { type: Object, notify: true, observer: 'onZoom' }
 
    zoomFlattened: { type: Object, notify: true }
 
  onZoom: ->
 
    updateZoomFlattened = ->
 
      @zoomFlattened = ko.toJS(@zoom)
 
    ko.computed(updateZoomFlattened.bind(@))
 
@@ -323,22 +324,28 @@ Polymer
 
    song:  { type: String, notify: true }
 
    zoomInX: { type: Object, notify: true }
 
    noteUris: { type: Array, notify: true }
 
    rowIndex: { type: Object, notify: true }
 
  observers: [
 
    'onGraph(graph, dia, setAdjuster, song, zoomInX)'
 
    'update(song)'
 
    'update(song, rowIndex)'
 
    'onZoom(zoomInX)'
 
    ]
 
  onGraph: ->
 
    @graph.runHandler(@update.bind(@), "row notes #{@rowIndex}")
 
  update: ->
 
    U = (x) -> @graph.Uri(x)
 
    log("row #{@rowIndex} updating")
 

	
 
    notesForThisRow = @graph.objects(@song, U(':note'))
 
    notesForThisRow = []
 
    i = 0
 
    for n in _.sortBy(@graph.objects(@song, U(':note')))
 
      if (i % ROW_COUNT) == @rowIndex
 
        notesForThisRow.push(n)
 
      i++
 
    log("row #{@rowIndex} gets", notesForThisRow)
 

	
 
    updateChildren @, notesForThisRow, (newUri) =>
 
      child = document.createElement('light9-timeline-note')
 
      child.graph = @graph
 
      child.dia = @dia
 
      child.uri = newUri
 
@@ -417,13 +424,13 @@ Polymer
 
      tMax = @graph.floatValue(worldPts[3].uri, U(':time'))
 
      tMax - tMin            
 

	
 
    screenPts = ($V([@zoomInX(pt.e(1)), @offsetTop + (1 - pt.e(2)) * @offsetHeight]) for pt in worldPts)
 
    @dia.setNote(@uri, screenPts, label)
 

	
 
    leftX = screenPts[1].e(1) + 5
 
    leftX = Math.max(2, screenPts[1].e(1) + 5)
 
    rightX = screenPts[2].e(1) - 5
 
    w = 120
 
    h = 45
 
    @inlineRect = {
 
      left: leftX,
 
      top: @offsetTop + @offsetHeight - h - 5,
0 comments (0 inline, 0 general)