diff options
-rw-r--r-- | README.md | 60 | ||||
-rw-r--r-- | blc/render.py | 8 | ||||
-rwxr-xr-x | blc/workspace.py | 3 |
3 files changed, 68 insertions, 3 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e44d8d --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# BLC + +## About + +BLC is a program that reads and parses [QLC+](https://www.qlcplus.org/) workspaces and allows +for the rendering of the contained functions. The goal is to bypass QLC's rather sketchy and +inconsistent rendering system, while still using QLC+ to actually edit workspaces. + +## Modules and Requirements + +BLC itself requires a modern Python 3. "Modern" means "as new as you can get" in most cases. I +don't guarantee the success of anything less that 3.7; I believe that 3.3/3.5 is the minimum +that you can get away with but they haven't been tested. + +The `workspace` module contains the core classes and functions of BLC and handles both loading +of workspaces and rendering functions. It has no special requirements. + +The `audio` module defines the `AudioPlayer` interface for playing audio and defines a couple of +concrete implementations. The `FFPlayer` class requires [ffmpeg](https://ffmpeg.org/) and the +`MPVPlayer` class requires [mpv](https://mpv.io/) along with the Python module +[python-mpv](https://github.com/jaseg/python-mpv). Note that the dependencies for the concrete +classes are unnecessary if you don't intend to use them. + +The `image` module defines some useful functions for visualizing the lighting output of +functions by turning them into images. This was primarily used in testing BLC's rendering +functionality, but looks cool regardless. It requires PIL (tested on +[pillow](https://python-pillow.org/)) for the actual images and [ffmpeg](https://ffmpeg.org/) +for reading audio files. + +The `output` module defines the `LightingOutput` interface for sending light cues. + +The `ola` module defines the `OLAOutput` implementation of `LightingOutput`, which sends cues +using [OLA](https://www.openlighting.org/ola/)'s Python bindings (usually built with OLA). I +highly recommend using this output; it moves the responsibility of handling the actual output +devices to better-tested code (OLA). It should also allow QLC+ and BLC to operate simultaneously +on the same output (untested). + +The `render` module defines (primarily) the `BasicRenderer` class, which should be sufficient to +serve as the actual live renderer for a user interface. This supports the rendering of +"toplevel" functions, which are Chasers and Shows. + +The `tk` module defines a couple of useful Tk widgets for dealing with BLC. It requires `tk` to +be installed on your system (usually included on Windows, try `python3 -c 'import tkinter'` to +check). + +## Workspace Architecture + +BLC attempts to maintain the same structure as QLC+ wherever possible. The architecture of +a workspace is split into two parts: topological and functional. + +The topological part represents the "physical" layout of the workspace: the fixtures, the +channels, and the universes. `Channel`s are assigned to `Fixture`s and `Fixture`s are assigned +to `Universe`s, as in QLC+. + +The functional part encompasses the actual lighting functions. BLC defines a base `Function` +class, which defines a `render` method that renders the output at a given time. BLC implements +most of the QLC+ functions (the ones used by incandescent lighting anyways): `Scene`, `Chaser`, +`Show`, and `Audio`. QLC+ sequences are implemented as `Chaser`s in BLC; I have no idea why this +is not the case in QLC+. Composite functions like `Chaser` and `Show` delegate the actual +rendering to their sub-functions, demuxing the output and fading as necessary. diff --git a/blc/render.py b/blc/render.py index d87e8b6..7dbeee2 100644 --- a/blc/render.py +++ b/blc/render.py @@ -9,6 +9,7 @@ from .output import LightingOutput from .workspace import SHOW, CHASER, Advanceable, QLC_INFTY class FunctionQueue: + """Queue for executing functions in sequence.""" def after(self, t: float, f: callable): """Run the given function after t milliseconds.""" self.queue.put((time.monotonic() + t/1000, f)) @@ -23,7 +24,7 @@ class FunctionQueue: def __init__(self): self.queue = queue.SimpleQueue() -class Renderer: +class BasicRenderer: """Basic renderer for functions. Supports live-rendering Chasers and Shows. @@ -93,6 +94,11 @@ class Renderer: self.stall_lock.acquire(blocking=False) self.stall_lock.release() + @property + def current_time(self): + """Return the current time in the function.""" + return (time.monotonic() - self.start_time) if self.start_time is not None else -1 + def __init__(self, f, lo:LightingOutput, ap: AudioPlayer=DefaultAudioPlayer, minnx=-1): if f.type not in (SHOW, CHASER): raise ValueError("Only Shows and Chasers may be used as toplevel functions") diff --git a/blc/workspace.py b/blc/workspace.py index c9f9a35..03141e4 100755 --- a/blc/workspace.py +++ b/blc/workspace.py @@ -104,8 +104,7 @@ import json from multiprocessing.pool import ThreadPool import subprocess as subp import logging - -from lxml import etree +import xml.etree.ElementTree as etree ## BEGIN Constants |