blc2 - Python library and frontend for running theatrical lighting and SFX. Written for the English 2041F fall 2019 theatre production of "The Cenci".

git clone https://benconnors.ca/git-repos/blc2

Log | Files | Refs

function.py (4490B) - raw


      1 """Base function module. 
      2 
      3 Contains the generic Function interface. 
      4 """
      5 
      6 from abc import ABCMeta, abstractmethod, abstractproperty
      7 from typing import Set, Any
      8 
      9 from ..topology import Fixture
     10 from ..constants import EXTERNAL, FUNCTION
     11 from ..interfaces import XMLSerializable
     12 
     13 class Function(XMLSerializable, metaclass=ABCMeta):
     14     """Class representing a generic function.
     15     
     16     Many of the properties here should not be implemented as properties for performance 
     17     reasons.
     18 
     19     Functions and properties required for rendering, e.g. ``Function.render`` and 
     20     ``Function.actual_duration``, should be written to be performant.
     21 
     22     Any change to the scope, audio scope, or actual duration of the function must be 
     23     reported using ``Workspace.function_changed`` with the new values. This should also be 
     24     extended to any change in e.g. values for lighting and audio primitives. 
     25 
     26     It is an error to change Function.type or Function.fade_out_mode in user code. These 
     27     values are public for informational purposes.
     28 
     29     Type checking and implicit conversion is done wherever possible on functions that  
     30     change state (e.g. setting the fades, setting values on a Scene). However, no type 
     31     checking or conversion is done on read-only or rendering functions to save time.
     32     """
     33     type = FUNCTION
     34     fade_out_mode = EXTERNAL
     35 
     36     def __init__(self, w: "Workspace", id_: int = None, name: str = None):
     37         self.w = w
     38         self._id = int(id_) if id_ is not None else w.next_function_id 
     39         self._name = str(name) if name else "%s %s" % (self.type, self.id)
     40         
     41         self.w.register_function(self)
     42 
     43     @property
     44     def id(self):
     45         """Return the function's ID."""
     46         return self._id
     47 
     48     @property
     49     def name(self):
     50         """Return the function's name."""
     51         return self._name
     52 
     53     @name.setter
     54     def name(self, v):
     55         if v != self._name:
     56             self._name = v if v else "%s %s" % (self.type, self.id)
     57             self.w.function_changed(self)
     58 
     59     def delete(self):
     60         """Delete the function from the Workspace."""
     61         self.w.function_deleted(self)
     62 
     63     @abstractproperty
     64     def duration(self) -> int:
     65         """Return the function's duration (excluding fades)."""
     66 
     67     @abstractproperty
     68     def fade_in(self) -> int:
     69         """Return the function's fade in time."""
     70 
     71     @abstractproperty
     72     def fade_out(self) -> int:
     73         """Return the function's fade out time."""
     74 
     75     @abstractproperty 
     76     def scope(self) -> Set[Fixture.Channel]:
     77         """Return the set of channels affected by this function."""
     78 
     79     @abstractproperty 
     80     def audio_scope(self) -> Set[str]:
     81         """Return the set of audio filenames that may be used by this function."""
     82 
     83     @abstractmethod
     84     def get_data(self) -> Any:
     85         """Return the default data for this function."""
     86 
     87     @abstractmethod 
     88     def copy_data(self, data: Any) -> Any:
     89         """Duplicate the given data."""
     90 
     91     @abstractproperty
     92     def actual_duration(self):
     93         """Return the actual duration of the function, including all fades."""
     94 
     95     @abstractmethod 
     96     def render(self, t: int, data: Any = None):
     97         """Render the function at the given time.
     98 
     99         This function must return a 3-tuple: 
    100 
    101             (light_cues, audio_cues, new_data)
    102 
    103         Where ``light_cues`` is a iterable of (channel, value) pairs. It is an error for 
    104         ``light_cues`` to contain channels other than exactly this Function's scope. 
    105         ``audio_cues`` is an iterable of audio cues of the form:
    106 
    107             (guid, filename, start_time, fade_in, end_time, fade_out)
    108 
    109         The ``guid`` is globally unique: that is, it is unique to that specific audio cue, 
    110         even if the same file is played multiple times or in different places. Note that 
    111         ``end_time`` may be ``INFTY``, in which case the file should be played to its end. 
    112         In the case of infinite-duration chasers, the ``end_time`` may change over 
    113         subsequent calls to this function.
    114 
    115         Note that ``data`` is not necessarily mutable: ``render`` should be called like: 
    116 
    117             lights, sound, data = f.render(t, data)
    118 
    119         Once a specific ``data`` instance has been used to render at a time, it must not be 
    120         used to render at a previous time: this is undefined behaviour and will break at 
    121         least Chasers.
    122 
    123         :param t: the time to render at, in milliseconds
    124         :param data: the function data to use
    125         """