"""Base function module. Contains the generic Function interface. """ from abc import ABCMeta, abstractmethod, abstractproperty from typing import Set, Any from ..topology import Fixture from ..constants import INFTY from ..interfaces import XMLSerializable class Function(XMLSerializable, metaclass=ABCMeta): """Class representing a generic function. Many of the properties here should not be implemented as properties for performance reasons. Functions and properties required for rendering, e.g. ``Function.render`` and ``Function.actual_duration``, should be written to be performant. Any change to the scope, audio scope, or actual duration of the function must be reported using ``Workspace.function_changed`` with the new values. This should also be extended to any change in e.g. values for lighting and audio primitives. """ def __init__(self, w: "Workspace", type_: str, id_: int = None, name: str = None, duration: int = INFTY, fade_in: int = 0, fade_out: int = 0): self.w = w self.id = id_ if id_ is not None else w.next_function_id self.name = name if name else "%s %s" % (type_, self.id) self.type = type_ self.duration = duration self.fade_in = fade_in self.fade_out = fade_out self.w.register_function(self) def delete(self): """Delete the function from the Workspace.""" self.w.function_deleted(self) @abstractproperty def scope(self) -> Set[Fixture.Channel]: """Return the set of channels affected by this function.""" @abstractproperty def audio_scope(self) -> Set[str]: """Return the set of audio filenames that may be used by this function.""" @abstractmethod def get_data(self) -> Any: """Return the default data for this function.""" @abstractmethod def copy_data(self, data: Any) -> Any: """Duplicate the given data.""" @abstractproperty def actual_duration(self): """Return the actual duration of the function, including all fades.""" @abstractmethod def render(self, t: int, data: Any = None): """Render the function at the given time. This function must return a 3-tuple: (light_cues, audio_cues, new_data) Where ``light_cues`` is a iterable of (channel, value) pairs. It is an error for ``light_cues`` to contain channels other than exactly this Function's scope. ``audio_cues`` is an iterable of audio cues of the form: (guid, filename, start_time, fade_in, end_time, fade_out) The ``guid`` is globally unique: that is, it is unique to that specific audio cue, even if the same file is played multiple times or in different places. Note that ``end_time`` may be ``INFTY``, in which case the file should be played to its end. In the case of infinite-duration chasers, the ``end_time`` may change over subsequent calls to this function. Note that ``data`` is not necessarily mutable: ``render`` should be called like: lights, sound, data = f.render(t, data) :param t: the time to render at, in milliseconds :param data: the function data to use """