diff options
-rwxr-xr-x | interface/channelbank.py | 2 | ||||
-rwxr-xr-x | interface/dummyserver.py | 2 | ||||
-rwxr-xr-x | interface/input/parsers.py | 4 | ||||
-rw-r--r-- | interface/interface.py | 72 | ||||
-rw-r--r-- | interface/render.py | 20 |
5 files changed, 91 insertions, 9 deletions
diff --git a/interface/channelbank.py b/interface/channelbank.py index 2ca964e..6562a9c 100755 --- a/interface/channelbank.py +++ b/interface/channelbank.py @@ -158,6 +158,8 @@ class ChannelBank: self._cv[c].held = v def set_values(self, cv): + if isinstance(cv, dict): + cv = cv.items() for c, v in cv: self._cv[c].value = v diff --git a/interface/dummyserver.py b/interface/dummyserver.py index cfd1451..7311315 100755 --- a/interface/dummyserver.py +++ b/interface/dummyserver.py @@ -50,6 +50,7 @@ def handle_conn(conn, m): def socket_main(m): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(("", 6969)) s.listen() print("Listening") @@ -64,7 +65,6 @@ if __name__ == "__main__": root.wm_title("Lighting Output") - main = Main(root) main.grid(row=0, column=0, sticky=N+E+S+W) diff --git a/interface/input/parsers.py b/interface/input/parsers.py index f97dbf1..7dc7d82 100755 --- a/interface/input/parsers.py +++ b/interface/input/parsers.py @@ -1,3 +1,5 @@ +from blc2.constants import INFTY + def parse_interval(s): if not s: return None, s @@ -107,6 +109,8 @@ def parse_time(s): v, s, d = parse_num(s) if v is None: + if s[0].lower() == 'i': + return INFTY, s[1:], "infinity" return None, s, None if not s: diff --git a/interface/interface.py b/interface/interface.py index 32fca84..f9c868e 100644 --- a/interface/interface.py +++ b/interface/interface.py @@ -2,6 +2,7 @@ import curses import os import datetime as dt import threading +import traceback as tb import blc2 @@ -526,7 +527,54 @@ class Interface: self.channel_bank.audio = self.primitive def _render_callback(self, t, values): - pass + if not self.rendering: + return + with self.w_lock: + self.channel_bank.set_values(values) + self.channel_bank.title = "LIVE: %7.2fs" % t + + for d, cv in zip(self.renderer._data, self.chaser_views): + cv.selected = d.steps[-1].index if d.steps else None + + def base_run(self): + if not self.chaser_views: + return "No chasers loaded" + + self.channel_bank.set_scope(self._channels) + self.channel_bank.title = "LIVE: %7.2fs" % 0 + self.channel_bank.highlight = True + + self.rendering = True + self.renderer.set_functions(*((cv.chaser, cv.chaser.get_data()) for cv in self.chaser_views)) + self.renderer.start() + + self.input.context = self.context_run + + return "Started running" + + def run_exit(self): + with self.w_lock: + self.rendering = False + self.renderer.stop() + + self.channel_bank.title = "Channels" + self.channel_bank.highlight = False + self.channel_bank.set_scope(()) + + self.input.context = self.context_base + + for cv in self.chaser_views: + cv.selected = None + + def run_jump(self, n, p, index): + if not index: + n = [j for j, i in enumerate(self.chaser_views) if i.chaser.id == n] + if not n: + return "No such chaser loaded" + n = n[0] + if n >= len(self.chaser_views) or n < 0: + return "Index out of range" + self.renderer.advance((n, p)) def __init__(self, path, output): ## Have to do most of the actual initialization in the main method, as curses isn't @@ -562,11 +610,13 @@ class Interface: ("new audio $quoted_string", lambda n: self.base_new(n, Audio)), ("add $num", self.base_add), - ("remove $letter", lambda n: self.base_remove(n, True)), - ("remove $num", lambda n: self.base_remove(n, False)), + ("subtract $letter", lambda n: self.base_remove(n, True)), + ("subtract $num", lambda n: self.base_remove(n, False)), + + ("run", self.base_run), - ("save", self.base_save), - ("save $quoted_string", self.base_save), + ("write", self.base_save), + ("write $quoted_string", self.base_save), ("list fixtures", self.list_fixtures), ("list scenes", lambda: self.list_functions(SCENE)), @@ -680,6 +730,18 @@ class Interface: ("quit", self.audio_exit), )) + self.context_run = Input.parse_context(( + ("quit", self.run_exit), + + ("jump $letter to $num", lambda n, p: self.run_jump(n, p, True)), + ("jump $num to $num", lambda n, p: self.run_jump(n, p, False)), + + ("advance $letter", lambda n: self.run_jump(n, None, True)), + ("advance $num", lambda n: self.run_jump(n, None, False)), + )) + self.output = output self.renderer = Renderer(self.w, self.w_lock, self.output, self._render_callback) self.rendering = False + + self._channels = sum((tuple(f.channels) for f in sorted(self.w.fixtures.values(), key=lambda i: i.id)), ()) diff --git a/interface/render.py b/interface/render.py index be7d770..7f8d3f1 100644 --- a/interface/render.py +++ b/interface/render.py @@ -44,11 +44,11 @@ class Renderer: with self._lock: self._running = False - def set_functions(self, f): + def set_functions(self, *args): with self._lock: if self._running: raise ValueError("Can't change while running") - self._functions, self._data = [i[0] for i in f], [i[1] for i in f] + self._functions, self._data = [i[0] for i in args], [i[1] for i in args] @property def time(self): @@ -74,7 +74,7 @@ class Renderer: next_ap = [] if self._callback is not None: - self._callback(t, self._values) + self._callback(self._current, self._values) ## FIXME: Cleanup finished audio players? ## FIXME: Handle audio fades and jumps @@ -118,6 +118,20 @@ class Renderer: ## END locked block time.sleep(max(0, next_t - time.monotonic())) + def advance(self, *args): + with self._lock: + if self._run_thread is None: + raise ValueError("Not running") + for a in args: + if isinstance(a, int): + p = None + else: + a, p = a + f = self._functions[a] + t = int(1000*self._current) + d = f.advance(t, self._data[a], n=p) + _, _, self._data[a] = f.render(t, d) + def __init__(self, w: Workspace, w_lock: threading.RLock, output, callback=None): self.output = output self.w = w |