changeset 529:1156d3531327

new ascoltami2, using gstreamer Ignore-this: 77e59ba6ec17b86343c93c24ac38aa44
author drewp@bigasterisk.com
date Fri, 11 Jun 2010 07:14:18 +0000
parents 581dfd11d9fd
children 77b17dc36834
files bin/ascoltami2 light9/ascoltami/__init__.py light9/ascoltami/index.html light9/ascoltami/player.py light9/ascoltami/webapp.py static/style.css
diffstat 5 files changed, 324 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/ascoltami2	Fri Jun 11 07:14:18 2010 +0000
@@ -0,0 +1,37 @@
+#!/usr/bin/python
+import web, thread, gobject, sys, optparse, logging
+from rdflib import URIRef
+sys.path.append(".")
+from light9.ascoltami.player import Player
+from light9.ascoltami.webapp import makeApp
+from light9 import networking, showconfig
+
+log = logging.getLogger()
+logging.basicConfig()
+gobject.threads_init()
+
+parser = optparse.OptionParser()
+parser.add_option('--show',
+    help='show URI, like http://light9.bigasterisk.com/show/dance2008')
+parser.add_option("-v", "--verbose", action="store_true",
+                  help="logging.DEBUG")
+graph = showconfig.getGraph()
+(options, args) = parser.parse_args()
+
+
+log.setLevel(logging.DEBUG if options.verbose else logging.INFO)
+
+graph = showconfig.getGraph()
+if not options.show:
+    raise ValueError("missing --show http://...")
+        
+player = Player()
+
+# the cherrypy server would wedge when vidref pounds on it; this
+# one seems to run
+thread.start_new(web.httpserver.runbasic,
+                 (makeApp(player, graph, URIRef(options.show)).wsgifunc(),
+                  ('0.0.0.0', networking.musicPort())))
+
+mainloop = gobject.MainLoop()
+mainloop.run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/ascoltami/index.html	Fri Jun 11 07:14:18 2010 +0000
@@ -0,0 +1,111 @@
+<?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">
+  <head>
+    <title>ascoltami</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>
+
+
+    <div class="songs">
+    </div>
+
+
+    <div>Song: <span id="currentSong"/></div>
+    <div>Time: <span id="currentTime"/></div>
+    <div>Left: <span id="leftTime"/></div>
+    <div>
+      <div id="timeSlider"/>
+    </div>
+
+    <div class="commands">
+      <button id="cmd-stop">Stop C-s</button>
+      <button id="cmd-play">Play C-p</button>
+      <button id="cmd-intro">Skip intro C-i</button>
+      <button id="cmd-post">Skip to Post C-t</button>
+      <button id="cmd-go">Go  space</button>
+    </div>
+
+bind keys, spacebar, css work
+
+<script type="text/javascript">
+// <![CDATA[
+$(function () {
+
+    var times = {
+	intro: 4,
+	post: 10
+    };
+    var currentDuration = 0;
+
+    function updateCurrent() {
+	$.getJSON("time", {}, function (data, status) {
+	    $("#currentSong").text(data.song);
+	    $("#currentTime").text(data.t);
+	    $("#leftTime").text(data.duration - data.t);
+	    currentDuration = data.duration;
+	    $("#timeSlider").slider({value: data.t,
+				     max: data.duration});
+
+	});
+    }
+
+    $.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("path"), 
+		       function (data, textStatus, xhr) {
+		    
+		       });
+	    });
+	    $(".songs").append($("<div>").append(button));
+	});
+    });
+
+    var tojs = JSON.stringify;
+
+    $("#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}))});
+    $("#cmd-post").click(function () { 
+	$.post("time", tojs({t: currentDuration - times.post}))
+    });
+    $("#cmd-go").click(function () {
+	
+    });
+
+    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, 300);
+    }
+    updateLoop();
+});
+// ]]>
+</script>
+
+
+  </body>
+</html>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/ascoltami/player.py	Fri Jun 11 07:14:18 2010 +0000
@@ -0,0 +1,103 @@
+#!/usr/bin/python
+
+"""
+alternate to the mpd music player, for ascoltami
+"""
+from __future__ import division
+import time, logging
+import gst
+log = logging.getLogger()
+
+class Player(object):
+    def __init__(self):
+
+        self.playbin = self.pipeline = gst.parse_launch("playbin2 name=b")
+        self.playStartTime = 0
+        self._duration = 0
+        self.pauseTime = 0
+
+        self.setSong("file:///my/proj/light9/show/dance2010/music/07-jacksonmix-complete.wav")
+        
+        #self.pipeline = gst.parse_launch("filesrc name=file location=%s ! wavparse name=src ! audioconvert ! alsasink name=out" % songFile)
+
+        def on_any(bus, msg):
+            print bus, msg
+
+        bus = self.pipeline.get_bus()
+        bus.add_signal_watch()
+        #bus.connect('message', on_any)
+
+        def onStreamStatus(bus, message):
+            (statusType, _elem) = message.parse_stream_status()
+            if statusType == gst.STREAM_STATUS_TYPE_ENTER:
+                self.setupAutostop()
+
+            # we should run our own poller looking for crosses over the autostop threshold and pausing. When i used the pipeline.seek end-time, it caused lots of unwanted other pausing and was hard to turn off.
+                
+            #print message, bus
+        bus.connect('message::stream-status', onStreamStatus)
+
+    def seek(self, t):
+        assert self.playbin.seek_simple(
+            gst.FORMAT_TIME,
+            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE | gst.SEEK_FLAG_SKIP,
+            t * gst.SECOND)
+        self.playStartTime = time.time()
+
+    def setSong(self, songUri):
+        """
+        uri like file:///my/proj/light9/show/dance2010/music/07.wav
+        """
+        log.info("set song to %r" % songUri)
+        self.pipeline.set_state(gst.STATE_READY)
+        self.pipeline.set_property("uri", songUri)
+        self.pipeline.set_state(gst.STATE_PLAYING)
+        self.playStartTime = time.time()
+
+
+
+#        GST_MESSAGE_DURATION
+        
+
+    def currentTime(self):
+        cur, _format = self.playbin.query_position(gst.FORMAT_TIME)
+        return cur / gst.SECOND
+
+    def duration(self):
+        return self.playbin.query_duration(gst.FORMAT_TIME)[0] / gst.SECOND
+        
+    def pause(self):
+        self.pipeline.set_state(gst.STATE_PAUSED)
+
+    def resume(self):
+        self.pipeline.set_state(gst.STATE_PLAYING)
+        pos = self.currentTime()
+        autoStop = self.duration() - 10
+        if abs(pos - autoStop) < .01:
+            self.releaseAutostop()
+
+
+    def setupAutostop(self):
+        return
+        dur = self.duration()
+        autoStop = (dur - 10.0)
+        log.info("autostop will be at %s", autoStop)
+        print "seek", self.pipeline.seek(1.0, gst.FORMAT_TIME,
+                           gst.SEEK_FLAG_ACCURATE,
+                           gst.SEEK_TYPE_NONE, 0,
+                           gst.SEEK_TYPE_SET, autoStop * gst.SECOND)
+
+    def releaseAutostop(self):
+        log.info("release autostop")
+        
+        print "seek", self.pipeline.seek(
+            1.0, gst.FORMAT_TIME,
+            gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE | gst.SEEK_FLAG_SKIP,
+            gst.SEEK_TYPE_NONE, 0,
+            gst.SEEK_TYPE_END, 0)
+        print self.pipeline.get_state()
+        self.pipeline.set_state(gst.STATE_PLAYING)
+
+                           
+                           
+        
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/ascoltami/webapp.py	Fri Jun 11 07:14:18 2010 +0000
@@ -0,0 +1,64 @@
+import web, jsonlib
+from twisted.python.util import sibpath
+from light9.namespaces import L9, MUS
+
+player = None
+graph = None
+show = None
+
+class root(object):
+    def GET(self):
+        web.header("Content-type", "application/xhtml+xml")
+        return open(sibpath(__file__, "index.html")).read()
+
+class timeResource(object):
+    def GET(self):
+        return jsonlib.write({"song" : player.playbin.get_property("uri"),
+                              "started" : player.playStartTime,
+                              "duration" : player.duration(),
+                              "t" : player.currentTime()})
+
+    def POST(self):
+        params = jsonlib.read(web.data(), use_float=True)
+        if params.get('pause', False):
+            player.pause()
+        if params.get('resume', False):
+            player.resume()
+        if 't' in params:
+            player.seek(params['t'])
+        return "ok"
+
+class songs(object):
+    def GET(self):
+
+        playList = graph.value(show, L9['playList'])
+        if not playList:
+            raise ValueError("%r has no l9:playList" % show)
+        songs = list(graph.items(playList))
+
+        
+        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)"""
+        player.setSong(web.data())
+        return "ok"
+
+def makeApp(thePlayer, theGraph, theShow):
+    global player, graph, show
+    player, graph, show = thePlayer, theGraph, theShow
+
+    urls = ("/", "root",
+            "/time", "timeResource",
+            "/song", "songResource",
+            "/songs", "songs",
+            "/api/position", "timeResource", # old
+            )
+
+    app = web.application(urls, globals(), autoreload=False)
+    return app
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/static/style.css	Fri Jun 11 07:14:18 2010 +0000
@@ -0,0 +1,9 @@
+.songs button {
+  display: inline-block;
+  width: 300px;
+height: 30px;
+  text-align: left;
+}
+.songs button:hover {
+  background: yellow;
+}
\ No newline at end of file