Changeset - ad41fdc33a91
[Not reviewed]
default
0 3 0
drewp@bigasterisk.com - 14 years ago 2011-06-15 05:32:23
drewp@bigasterisk.com
asco displays hostname and sends better content-type headers
Ignore-this: a25ba775fa44991c2166740410297eae
3 files changed with 12 insertions and 7 deletions:
0 comments (0 inline, 0 general)
light9/ascoltami/index.html
Show inline comments
 
<?xml version="1.0" encoding="iso-8859-1"?>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
 
<html xmlns="http://www.w3.org/1999/xhtml" 
 
      xmlns:py="http://genshi.edgewall.org/">
 
  <head>
 
    <title>ascoltami</title>
 
    <title>ascoltami on ${host}</title>
 
    <script type="text/javascript" src="static/jquery-1.4.2.min.js"></script>
 
      <script type="text/javascript" src="static/jquery-ui-1.8.2.custom/js/jquery-ui-1.8.2.custom.min.js"></script>
 
      <link rel="Stylesheet" type="text/css" href="static/jquery-ui-1.8.2.custom/css/smoothness/jquery-ui-1.8.2.custom.css"/>
 
      <link rel="Stylesheet" type="text/css" href="static/style.css"/>
 
  </head>
 
  <body>
 

	
 
    <h1>ascoltami on ${host}</h1>
 
    <div class="songs"/>
 

	
 
    <div>Song: <span id="currentSong"/></div>
 
    <div>Time: <span id="currentTime"/></div>
 
    <div>Left: <span id="leftTime"/></div>
 
    <div>Until autostop: <span id="leftAutoStopTime"/></div>
 
    <div class="timeRow">
 
      <div id="timeSlider"/>
 
    </div>
 

	
 
    <div class="commands">
 
      <button id="cmd-stop" class="playMode">Stop<div class="key">C-s</div></button>
 
      <button id="cmd-play" class="playMode">Play <div class="key">C-p</div></button>
 
      <button id="cmd-intro">Skip intro <div class="key">C-i</div></button>
 
      <button id="cmd-post">Skip to Post <div class="key">C-t</div></button>
 
      <button id="cmd-go">Go  <div class="key">space</div></button>
 
    </div>
 

	
 
    todo: go button actions, display next action
 
    <a href="">reload</a>
 

	
 
<script type="text/javascript">
 
// <![CDATA[
 
$(function () {
 

	
 
    var times = { // need to get these from server
 
	intro: 4,
 
	post: 4
 
    };
 

	
 
    var currentDuration = 0;
 
    var currentHighlightedSong = "";
 
    var lastPlaying;
 
    function updateCurrent() {
 
	$.getJSON("time", {}, function (data, status) {
 
	    $("#currentSong").text(data.song);
 
	    if (data.song != currentHighlightedSong) {
 
		showCurrentSong(data.song);
 
	    }
 
	    $("#currentTime").text(data.t.toFixed(1));
 
	    $("#leftTime").text((data.duration - data.t).toFixed(1));
 
	    $("#leftAutoStopTime").text(
 
		Math.max(0, data.duration - times.post - data.t).toFixed(1));
 
	    currentDuration = data.duration;
 
	    $("#timeSlider").slider({value: data.t,
 
				     max: data.duration});
 
	    if (data.playing != lastPlaying) {
 
		$(".playMode").removeClass("active");
 
		$(data.playing ? "#cmd-play" : "#cmd-stop").addClass("active");
 
		lastPlaying = data.playing;
 
	    }
 
	});
 
    }
 
    function showCurrentSong(uri) {
 
	$(".songs div").each(function (i, row) {
 
	    row = $(row);
 
	    if (row.find("button").data("uri") == uri) {
 
		row.addClass("currentSong");
 
	    } else {
 
		row.removeClass("currentSong");
 
	    }
 
	});
 
	currentHighlightedSong = uri;
 
    }
 
    $.getJSON("songs", {}, function (data, status) {
 
	$.each(data.songs, function (i, song) {
 
	    var button = $("<button>");
 
	    button.text(song.label);
 
	    button.data(song);
 
	    button.click(function () {
 
		$.post("song", button.data("uri"), 
 
		       function (data, textStatus, xhr) {
 
			   showCurrentSong(song.uri);
 
		       });
 
	    });
 
	    $(".songs").append($("<div>").append(button));
 
	});
 
    });
 

	
 
    var tojs = JSON.stringify;
 

	
 
    $(document).keypress(function (ev) {
 
	if (1 || ev.ctrlKey) {
 
	    if (ev.which == 115) { $("#cmd-stop").click(); return false; }
 
	    if (ev.which == 112) { $("#cmd-play").click(); return false; }
 
	    if (ev.which == 105) { $("#cmd-intro").click(); return false; }
 
	    if (ev.which == 116) { $("#cmd-post").click(); return false; }
 
	}
 
	if (ev.which == 32) { $("#cmd-go").click(); return false; }
 
	return true;
 
    });
 

	
 
    $("#cmd-stop").click(function () { $.post("time", tojs({pause: true})); });
 
    $("#cmd-play").click(function () { $.post("time", tojs({resume: true})); });
 
    $("#cmd-intro").click(function () { 
 
	$.post("time", tojs({t: times.intro, resume: true}))
 
    });
 
    $("#cmd-post").click(function () { 
 
	$.post("time", tojs({t: currentDuration - times.post, resume: true}))
 
    });
 
    $("#cmd-go").click(function () {
 
	// todo
 
    });
 

	
 
    var pendingSlide = false;
 
    $("#timeSlider").slider({
 
	step: .01,
 
	slide: function (event, ui) {
 
	    if (pendingSlide) {
 
		return;
 
	    }
 
	    pendingSlide = true;
 
	    $.post("time", '{"t" : '+ui.value+'}', 
 
		   function (data, status, xhr) {
 
		       pendingSlide = false;
 
		   });
 
	},
 
    });
 
    
 
    function updateLoop() {
 
	updateCurrent();
 
	setTimeout(updateLoop, 200);
 
    }
 
    updateLoop();
 
});
 
// ]]>
 
</script>
 

	
 

	
 
  </body>
 
</html>
 
\ No newline at end of file
light9/ascoltami/webapp.py
Show inline comments
 
import web, jsonlib
 
import web, jsonlib, socket
 
from twisted.python.util import sibpath
 
from light9.namespaces import L9
 
from light9.showconfig import getSongsFromShow
 
from rdflib import URIRef
 

	
 
from web.contrib.template import render_genshi
 
render = render_genshi([sibpath(__file__, ".")])
 
app = None
 

	
 
def songLocation(graph, songUri):
 
    loc = graph.value(songUri, L9['showPath'])
 
    if loc is None:
 
        raise ValueError("no showPath for %r" % songUri)
 
    return loc
 
    
 
def songUri(graph, locationUri):
 
    try:
 
        return graph.subjects(L9['showPath'], locationUri).next()
 
    except StopIteration:
 
        raise ValueError("no song has :showPath of %r" % locationUri)
 

	
 
class root(object):
 
    def GET(self):
 
        web.header("Content-type", "application/xhtml+xml")
 
        # todo: use a template; embed the show name and the intro/post
 
        # times into the page
 
        return open(sibpath(__file__, "index.html")).read()
 
        return render.index(host=socket.gethostname())
 

	
 
class timeResource(object):
 
    def GET(self):
 
        player = app.player
 
        graph = app.graph
 

	
 
        playingLocation = player.getSong()
 
        if playingLocation:
 
            song = songUri(graph, URIRef(playingLocation))
 
        else:
 
            song = None
 
        web.header("content-type", "application/json")
 
        return jsonlib.write({
 
            "song" : song,
 
            "started" : player.playStartTime,
 
            "duration" : player.duration(),
 
            "playing" : player.isPlaying(),
 
            "t" : player.currentTime()})
 

	
 
    def POST(self):
 
        """
 
        post a json object with {pause: true} or {resume: true} if you
 
        want those actions. Use {t: <seconds>} to seek, optionally
 
        with a pause/resume command too.
 
        """
 
        params = jsonlib.read(web.data(), use_float=True)
 
        player = app.player
 
        if params.get('pause', False):
 
            player.pause()
 
        if params.get('resume', False):
 
            player.resume()
 
        if 't' in params:
 
            player.seek(params['t'])
 
        web.header("content-type", "text/plain")
 
        return "ok"
 

	
 
class songs(object):
 
    def GET(self):
 
        graph = app.graph
 

	
 
        songs = getSongsFromShow(graph, app.show)
 

	
 
        web.header("Content-type", "application/json")
 
        return jsonlib.write({"songs" : [
 
            {"uri" : s,
 
             "path" : graph.value(s, L9['showPath']),
 
             "label" : graph.label(s)} for s in songs]})
 

	
 
class songResource(object):
 
    def POST(self):
 
        """post a uri of song to switch to (and start playing)"""
 
        graph = app.graph
 

	
 
        app.player.setSong(songLocation(graph, URIRef(web.data())))
 
        web.header("content-type", "text/plain")
 
        return "ok"
 
    
 
class seekPlayOrPause(object):
 
    def POST(self):
 
        player = app.player
 

	
 
        data = jsonlib.read(web.data(), use_float=True)
 
        if player.isPlaying():
 
            player.pause()
 
        else:
 
            player.seek(data['t'])
 
            player.resume()
 

	
 
def makeWebApp(theApp):
 
    global app
 
    app = theApp
 

	
 
    urls = (r"/", "root",
 
            r"/time", "timeResource",
 
            r"/song", "songResource",
 
            r"/songs", "songs",
 
            r"/seekPlayOrPause", "seekPlayOrPause",
 
            )
 

	
 
    return web.application(urls, globals(), autoreload=False)
show/dance2010/readme
Show inline comments
 
for local testing on plus:
 

	
 
sudo -u mpd -s
 
export LD_LIBRARY_PATH=/home/drewp/score
 
./mpd --no-daemon
 

	
 
fix networking.py to look for player on plus
 

	
 
PYTHONPATH=../pympd bin/ascoltami --show http://light9.bigasterisk.com/show/dance2010
 

	
 
plus(pts/3):~/projects/light9% bin/dmxserver -n
 

	
 
vidref
 
OK grab time from asco
 
OK always record picture against {songuri}/vid/{take}/{songtime}, so we never miss a recording (but at night when they're all gone, we don't need any recordings?)
 
OK play prev videos
 
OK let me tag a good rehearsal or toss junk rehearsals. if we juggle the playback time too much, you can be sure it's not a good pass
 
OK qt window with one live pane and any number of synced playback panes.
 
XX get twisted qt wrapper for our networking
 
OK new take for every single restart? i guess so, since they could pass over the same song time.
 
XX check if mpd has a working precise-time system yet, so we could get off the patched one
 
Do we need to detach from current song+time to view something else?
 
curvecalc should be able to fetch a sample of a lit frame to stick in its timeline
 
need to move curvecalc to qt?
 
can i dynamically change the output filename of a filesink? that might be the way to steer the ouptut correctly. But, i might want to append one take's frames into one file. Maybe use a standard compressor like mjpeg, and separately map the in-movie timestamp to our playback timestamp in case they drift.
 

	
 
show is 70min of music: at 8fps and 50k/pic, 1.6MB of image data per pass.
 

	
 

	
 

	
 
==================================================
 
rewrites!
 

	
 
after you get all the music files into config.n3, run bin/musicPad
 
once, and then update all their paths to the new pad/ directory.
 

	
 
THEN run bin/wavecurve
 

	
 
todo:
 
make new CDs with corrected songs
 
sync songs across computers
 
test dmx dongle, sliders box, sound playback
 

	
 

	
 
for 2011:
 
- bring a mouse for the curvecalc laptop
 
- vidref should have a time slider
 
- factor out music dir so it can be different per machine
 
- all settings should be in RDF and live-shared across all apps, maybe with anzo for updates
 
- when curvecalc says 'Slider 1', the X coordinates of the curve get misaligned
 
- asco should say clearly what machine it's running on
 
OK asco should say clearly what machine it's running on
 
- asco should have GUI for picking which sound output you want, and store the setting
 
- vidref should have snapshot button for taking pics of the stage
 
- bring mini tripod or beanbag for the vidref camera
 
- more remote contol stuff
 
- gel sample pack
 
- vidref adjustable crop to the stage aspect
 
- vidref should have an 'in progress' row to prove that it's recording something new
 
OK curvecalc still has a problem making empty-name subterms. that should never happen, even once
 
- remote-run GUIs should say what machine they're on. Or, everyone should say what RDF server it's talking tox
 
- scrub inside curvecalc, preview a video in it
 
- bring curvecalc's logging inside the GUI window
 
- vidref stopped here once:
 
        File "/usr/lib/python2.6/threading.py", line 484, in run
 
          self.__target(*self.__args, **self.__kwargs)
 
        File "/home/drewp/projects/light9/light9/vidref/main.py", line 54, in _timeUpdate
 
          position = jsonlib.loads(self.musicResource.get("time").body,
 
        File "/usr/local/lib/python2.6/dist-packages/restkit-1.3.1-py2.6.egg/restkit/resource.py", line 135, in get
 
          return self.request("GET", path=path, headers=headers, **params)
 
        File "/usr/local/lib/python2.6/dist-packages/restkit-1.3.1-py2.6.egg/restkit/resource.py", line 231, in request
 
          raise RequestError(e)
 
      RequestError: [Errno 111] Connection refused
 
- asco stops considerably after the autostop time (according to curvecalc)
 
- CC should support zoom-crushing towards the left as well
 
- vidref's replay 'enabled' button should be completely sticky across sessions
 
- audio process pipeline; RDF graph should track versions of songs (padded, normalized)
 
OK vidref can loop like this if asco goes down:
 
       File "/home/drewp/projects/light9/light9/vidref/main.py", line 168, in updateLoop
 
         self.replayViews.update(position)
 
       File "/home/drewp/projects/light9/light9/vidref/replay.py", line 42, in update
 
         self.loadViewsForSong(position['song'])
 
       File "/home/drewp/projects/light9/light9/vidref/replay.py", line 57, in loadViewsForSong
 
         d = songDir(song)
 
       File "/home/drewp/projects/light9/light9/vidref/replay.py", line 10, in songDir
 
         safeUri = song.split('://')[-1].replace('/','_')
 
     AttributeError: 'NoneType' object has no attribute 'split'
 
- CC subterms that are just curve(t) should not appear as rows; they should be described in their curverow
 
- all machines should have this control panel: set audio output to house or local; what's the song time; scrub around in this song; switches for emergency lights; make a checkpoint (if we dont have complete history yet); "it looks bad" button, to make a todo note for that time
 
- hardest thing is seeing an area on stage and getting light onto it. lightsim could help
 
- compute whole-stage brightness and show that as a track, so we can adjust the light mix to create overall looks
 

	
 
#3: 306 horns- strobe.
 
255 light the sides. r first
 

	
 
#4 louder
 

	
 
#5 test blacklight
 

	
 
#7 
 
start cyc, with just 5
 
245 light change as they turn
 

	
 
#8 test BL
 

	
 
#10 louder
 

	
 
#11 test BL
 
112 strobe? with the sound effect
 
180 challenge R then L
 
191 busy before the end, then light their line
 
-  2nd req for BL
 

	
 
#14 test BL
 
146 darker before kick line, so we can get brighter on it at this time
 

	
 
#16 softer beginning
 
587 strobe to 595
 
655 fade as they sit
 

	
 
  how to normalize audio:
 
     plus(pts/0):~/projects/light9/show/dance2008/music/norm% normalize-audio -v --mix *.wav
 

	
0 comments (0 inline, 0 general)