Mercurial > code > home > repos > light9
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