| 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 |
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 """