Mercurial > code > home > repos > light9
changeset 1938:60c5acfe4f5a
move asco client into light9/ascoltami
Ignore-this: ef3ef6375a6f43fff8599073b268feab
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Sun, 02 Jun 2019 21:36:57 +0000 |
parents | f29e26811206 |
children | 6f49dc917aa3 |
files | light9/ascoltami/musictime_client.py light9/effect/sequencer.py light9/vidref/main.py light9/vidref/musictime.py |
diffstat | 4 files changed, 142 insertions(+), 142 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/light9/ascoltami/musictime_client.py Sun Jun 02 21:36:57 2019 +0000 @@ -0,0 +1,138 @@ +import time, json, logging +from typing import Dict + +from twisted.internet import reactor +from twisted.internet.defer import inlineCallbacks +import treq + +from light9 import networking + +log = logging.getLogger() + + +class MusicTime(object): + """ + fetch times from ascoltami in a background thread; return times + upon request, adjusted to be more precise with the system clock + """ + + def __init__(self, + period=.2, + onChange=lambda position: None, + pollCurvecalc=True): + """period is the seconds between http time requests. + + We call onChange with the time in seconds and the total time + + The choice of period doesn't need to be tied to framerate, + it's more the size of the error you can tolerate (since we + make up times between the samples, and we'll just run off the + end of a song) + """ + self.period = period + self.hoverPeriod = .05 + self.onChange = onChange + + self.position: Dict[str, float] = {} + # driven by our pollCurvecalcTime and also by Gui.incomingTime + self.lastHoverTime = None # None means "no recent value" + self.pollMusicTime() + if pollCurvecalc: + self.pollCurvecalcTime() + + def getLatest(self, frameTime=None): + """ + dict with 't' and 'song', etc. + + frameTime is the timestamp from the camera, which will be used + instead of now. + + Note that this may be called in a gst camera capture thread. Very often. + """ + if not hasattr(self, 'position'): + return {'t': 0, 'song': None} + pos = self.position.copy() + now = frameTime or time.time() + if pos.get('playing'): + pos['t'] = pos['t'] + (now - self.positionFetchTime) + else: + if self.lastHoverTime is not None: + pos['hoverTime'] = self.lastHoverTime + return pos + + def pollMusicTime(self): + + @inlineCallbacks + def cb(response): + + if response.code != 200: + raise ValueError("%s %s", response.code, + (yield response.content())) + + position = yield response.json() + + # this is meant to be the time when the server gave me its + # report, and I don't know if that's closer to the + # beginning of my request or the end of it (or some + # fraction of the way through) + self.positionFetchTime = time.time() + + self.position = position + self.onChange(position) + + reactor.callLater(self.period, self.pollMusicTime) + + def eb(err): + log.warn("talking to ascoltami: %s", err.getErrorMessage()) + reactor.callLater(2, self.pollMusicTime) + + d = treq.get(networking.musicPlayer.path("time").toPython()) + d.addCallback(cb) + d.addErrback(eb) # note this includes errors in cb() + + def pollCurvecalcTime(self): + """ + poll the curvecalc position when music isn't playing, so replay + can track it. + + This would be better done via rdfdb sync, where changes to the + curvecalc position are written to the curvecalc session and we + can pick them up in here + """ + if self.position.get('playing'): + # don't need this position during playback + self.lastHoverTime = None + reactor.callLater(.2, self.pollCurvecalcTime) + return + + def cb(response): + if response.code == 404: + # not hovering + self.lastHoverTime = None + reactor.callLater(.2, self.pollCurvecalcTime) + return + if response.code != 200: + raise ValueError("%s %s" % (response.code, response.body)) + self.lastHoverTime = json.loads(response.body)['hoverTime'] + + reactor.callLater(self.hoverPeriod, self.pollCurvecalcTime) + + def eb(err): + if self.lastHoverTime: + log.warn("talking to curveCalc: %s", err.getErrorMessage()) + self.lastHoverTime = None + reactor.callLater(2, self.pollCurvecalcTime) + + d = treq.get(networking.curveCalc.path("hoverTime")) + d.addCallback(cb) + d.addErrback(eb) # note this includes errors in cb() + + def sendTime(self, t): + """request that the player go to this time""" + treq.post( + networking.musicPlayer.path('time'), + data=json.dumps({ + "t": time + }).encode('utf8'), + headers={b"content-type": [b"application/json"]}, + )
--- a/light9/effect/sequencer.py Sun Jun 02 21:19:57 2019 +0000 +++ b/light9/effect/sequencer.py Sun Jun 02 21:36:57 2019 +0000 @@ -13,12 +13,12 @@ import traceback from typing import Any, Callable, Dict, List, Tuple, cast, Union -from light9.namespaces import L9, RDF -from light9.newtypes import DeviceUri, DeviceAttr, NoteUri, Curve, Song -from light9.vidref.musictime import MusicTime +from light9.ascoltami.musictime_client import MusicTime from light9.effect import effecteval from light9.effect.settings import DeviceSettings from light9.effect.simple_outputs import SimpleOutputs +from light9.namespaces import L9, RDF +from light9.newtypes import DeviceUri, DeviceAttr, NoteUri, Curve, Song from rdfdb.syncedgraph import SyncedGraph from greplin import scales
--- a/light9/vidref/main.py Sun Jun 02 21:19:57 2019 +0000 +++ b/light9/vidref/main.py Sun Jun 02 21:36:57 2019 +0000 @@ -9,7 +9,7 @@ import gtk from twisted.python.util import sibpath from light9.vidref.replay import ReplayViews, framerate -from light9.vidref.musictime import MusicTime +from light9.ascoltami.musictime_client import MusicTime from light9.vidref.videorecorder import Pipeline from light9.vidref import remotepivideo log = logging.getLogger()
--- a/light9/vidref/musictime.py Sun Jun 02 21:19:57 2019 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -import time, json, logging -from typing import Dict - -from twisted.internet import reactor -from twisted.internet.defer import inlineCallbacks -import treq - -from light9 import networking - -log = logging.getLogger() - - -class MusicTime(object): - """ - fetch times from ascoltami in a background thread; return times - upon request, adjusted to be more precise with the system clock - """ - - def __init__(self, - period=.2, - onChange=lambda position: None, - pollCurvecalc=True): - """period is the seconds between http time requests. - - We call onChange with the time in seconds and the total time - - The choice of period doesn't need to be tied to framerate, - it's more the size of the error you can tolerate (since we - make up times between the samples, and we'll just run off the - end of a song) - """ - self.period = period - self.hoverPeriod = .05 - self.onChange = onChange - - self.position: Dict[str, float] = {} - # driven by our pollCurvecalcTime and also by Gui.incomingTime - self.lastHoverTime = None # None means "no recent value" - self.pollMusicTime() - if pollCurvecalc: - self.pollCurvecalcTime() - - def getLatest(self, frameTime=None): - """ - dict with 't' and 'song', etc. - - frameTime is the timestamp from the camera, which will be used - instead of now. - - Note that this may be called in a gst camera capture thread. Very often. - """ - if not hasattr(self, 'position'): - return {'t': 0, 'song': None} - pos = self.position.copy() - now = frameTime or time.time() - if pos.get('playing'): - pos['t'] = pos['t'] + (now - self.positionFetchTime) - else: - if self.lastHoverTime is not None: - pos['hoverTime'] = self.lastHoverTime - return pos - - def pollMusicTime(self): - - @inlineCallbacks - def cb(response): - - if response.code != 200: - raise ValueError("%s %s", response.code, - (yield response.content())) - - position = yield response.json() - - # this is meant to be the time when the server gave me its - # report, and I don't know if that's closer to the - # beginning of my request or the end of it (or some - # fraction of the way through) - self.positionFetchTime = time.time() - - self.position = position - self.onChange(position) - - reactor.callLater(self.period, self.pollMusicTime) - - def eb(err): - log.warn("talking to ascoltami: %s", err.getErrorMessage()) - reactor.callLater(2, self.pollMusicTime) - - d = treq.get(networking.musicPlayer.path("time").toPython()) - d.addCallback(cb) - d.addErrback(eb) # note this includes errors in cb() - - def pollCurvecalcTime(self): - """ - poll the curvecalc position when music isn't playing, so replay - can track it. - - This would be better done via rdfdb sync, where changes to the - curvecalc position are written to the curvecalc session and we - can pick them up in here - """ - if self.position.get('playing'): - # don't need this position during playback - self.lastHoverTime = None - reactor.callLater(.2, self.pollCurvecalcTime) - return - - def cb(response): - if response.code == 404: - # not hovering - self.lastHoverTime = None - reactor.callLater(.2, self.pollCurvecalcTime) - return - if response.code != 200: - raise ValueError("%s %s" % (response.code, response.body)) - self.lastHoverTime = json.loads(response.body)['hoverTime'] - - reactor.callLater(self.hoverPeriod, self.pollCurvecalcTime) - - def eb(err): - if self.lastHoverTime: - log.warn("talking to curveCalc: %s", err.getErrorMessage()) - self.lastHoverTime = None - reactor.callLater(2, self.pollCurvecalcTime) - - d = treq.get(networking.curveCalc.path("hoverTime")) - d.addCallback(cb) - d.addErrback(eb) # note this includes errors in cb() - - def sendTime(self, t): - """request that the player go to this time""" - treq.post( - networking.musicPlayer.path('time'), - data=json.dumps({ - "t": time - }).encode('utf8'), - headers={b"content-type": [b"application/json"]}, - )