summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xinterface/channelbank.py2
-rwxr-xr-xinterface/dummyserver.py2
-rwxr-xr-xinterface/input/parsers.py4
-rw-r--r--interface/interface.py72
-rw-r--r--interface/render.py20
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