diff options
Diffstat (limited to 'functions')
| -rw-r--r-- | functions/__init__.py | 0 | ||||
| -rw-r--r-- | functions/audio.py | 118 | ||||
| -rw-r--r-- | functions/function.py | 88 | ||||
| -rw-r--r-- | functions/scene.py | 144 | 
4 files changed, 0 insertions, 350 deletions
diff --git a/functions/__init__.py b/functions/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/functions/__init__.py +++ /dev/null diff --git a/functions/audio.py b/functions/audio.py deleted file mode 100644 index d0e599d..0000000 --- a/functions/audio.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Audio function module. - -Contains the definition of the Audio, the audio primitive.  -""" - -import xml.etree.ElementTree as et - -from .function import Function - -from ..constants import AUDIO, BXW -from ..exceptions import LoadError - -class Audio(Function): -    """Class representing an audio cue.  - -    This is the primitive for audio, and all sound cues must be based in some manner off of  -    Audio. This function merely plays a single audio file once, starting at t=0.  - -    The duration of the audio is automatically determined and is zero if the file does not  -    exist or is unsupported by ffmpeg.  -    """ -    scope = () -    audio_scope = frozenset() -    actual_duration = 0 - -    def __init__(self, w, id_ = None, name = None, fade_in = 0, fade_out = 0, -                 filename: str = None): -        super().__init__(w=w, type_=AUDIO, id_=id_, name=name, duration=0,  -                         fade_in=fade_in, fade_out=fade_out) - -        self._filename = filename - -        if filename is not None: -            self.duration = self.w.get_audio_length(filename) -            self.audio_scope = frozenset(((filename,),)) -        else: -            self.duration = 0 -            self.audio_scope = frozenset() - -        self.actual_duration = self.duration - -    def get_data(self): -        return None - -    def copy_data(self, data): -        return None - -    def render(self, t, data = None): -        return ((),  -                ((self.id, self._filename, 0, self.fade_in, self.duration, self.fade_out),), -                None) - -    @property -    def filename(self): -        """Return the current audio filename.""" -        return self._filename - -    @filename.setter  -    def filename(self, value: str): -        """Set the current audio filename, updating the duration.""" -        if value is not None: -            self.duration = self.w.get_audio_length(value) -            self.audio_scope = frozenset(((value,),)) -        else: -            self.duration = 0 -            self.audio_scope = frozenset() - -        self._filename = value -        self.w.function_changed(self) - -    def serialize(self) -> et.Element: -        e = et.Element(BXW+"function") -        e.set("type", self.type) -        e.set("id", str(self.id)) -        e.set("name", self.name) -        e.set("fade-in", str(self.fade_in)) -        e.set("fade-out", str(self.fade_out)) -        if self.filename is not None: -            filename = et.SubElement(e, BXW+"filename") -            filename.text = self.filename - -        return e - -    @classmethod -    def deserialize(cls, w, e): -        if e.tag != BXW+"function": -            raise LoadError("Invalid function tag") -        elif e.get("type") != AUDIO: -            raise LoadError("Load delegated to wrong class (this is a bug)") -         -        id_ = cls.int_or_none(e.get("id")) -        if id_ is None: -            raise LoadError("Function tag has invalid/missing ID") - -        name = e.get("name") - -        fade_in = e.get("fade-in") -        try: -            fade_in = int(fade_in) if fade_in else 0 -        except ValueError: -            raise LoadError("Invalid fade in") - -        fade_out = e.get("fade-out") -        try: -            fade_out = int(fade_out) if fade_out else 0 -        except ValueError: -            raise LoadError("Invalid fade out") - -        if len(e) > 1: -            raise LoadError("Audio tag can have at most one filename") -        elif len(e) == 1: -            filename, = e -            filename = filename.text -        else: -            filename = None - -        return cls(w=w, id_=id_, name=name, filename=filename, fade_in=fade_in, -                   fade_out=fade_out) diff --git a/functions/function.py b/functions/function.py deleted file mode 100644 index 97c98f7..0000000 --- a/functions/function.py +++ /dev/null @@ -1,88 +0,0 @@ -"""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 -        """ diff --git a/functions/scene.py b/functions/scene.py deleted file mode 100644 index 958138f..0000000 --- a/functions/scene.py +++ /dev/null @@ -1,144 +0,0 @@ -"""Scene function module.  - -Contains the definition of the Scene, the lighting primitive.  -""" - -import xml.etree.ElementTree as et -from typing import Mapping - -from .function import Function -from ..topology import Fixture -from ..constants import INFTY, SCENE, BXW -from ..exceptions import LoadError - -class Scene(Function): -    """Class representing a lighting scene.  - -    This is the primitive for lighting, and all light cues must be based in some manner off  -    of Scene. Scenes are simple, having no fading and infinite duration.  - -    Modifying the scene can be done in a few ways: the easiest is using the dictionary- -    style accesses, e.g. setting the values using: - -        scene[channel] = value - -    Values may be removed from the scene using the ``del`` operator and retrieved in the   -    same fashion. Alternatively, values can be updated in bulk using ``Scene.update`` or -    set in bulk with ``Scene.set``. -    """ -    audio_scope = () -    scope = frozenset() -    actual_duration = INFTY -    values = frozenset() - -    def __init__(self, w, id_ = None, name = None, values: Mapping[Fixture.Channel, int] = None): -        super().__init__(w=w, type_=SCENE, id_=id_, name=name, duration=INFTY, fade_in=0, -                         fade_out=0) -        self._values = {} -        self._render = () - -        self._update(values, changed=False) - -    def _update_render(self, changed=True): -        self._render = tuple(self._values.items()) -        self.scope = frozenset(self._values.keys()) -        self.values = self._render - -        if changed: -            self.w.function_changed(self) - -    def set(self, v): -        """Set the scene's values to the given ones.  - -        Equivalent to update, except that all non-given values are deleted from the scene. -        """ -        self._values = {} -        self.update(v) - -    def _update(self, v, changed=True): -        if isinstance(v, dict): -            v = v.items() -        vn = [] -        for c, val in v: -            val = int(val) -            if val < 0 or val > 255: -                raise ValueError("Values must be integers on [0,256)") -            vn.append((c, val)) -        for c, val in vn: -            self._values[c] = val -        self._update_render(changed=changed) - -    def update(self, v): -        """Update the scene's values.""" -        self._update(v=v, changed=True) - -    def __getitem__(self, c: Fixture.Channel): -        return self._values[c] if c in self._values else None - -    def __setitem__(self, c: Fixture.Channel, v: int): -        self.update(((c, v),)) - -    def __delitem__(self, c: Fixture.Channel): -        if c in self._values: -            del self._values[c] -            self._update_render() - -    def get_data(self): -        return None - -    def copy_data(self, data): -        return None - -    def render(self, t, data = None): -        return (self._render, (), None) - -    def serialize(self): -        e = et.Element(BXW+"function") -        e.set("type", self.type) -        e.set("id", str(self.id)) -        e.set("name", self.name) -        for c, v in self.values: -            ce = et.SubElement(e, BXW+"value") -            ce.set("fixture", str(c.f.id)) -            ce.set("channel", str(c.id)) -            ce.text = str(v) - -        return e - -    @classmethod -    def deserialize(cls, w, e): -        if e.tag != BXW+"function":  -            raise LoadError("Invalid function tag") -        elif e.get("type") != SCENE: -            raise LoadError("Load delegated to wrong class (this is a bug)") -         -        id_ = cls.int_or_none(e.get("id")) -        if id_ is None: -            raise LoadError("Function tag has invalid/missing ID") - -        name = e.get("name") - -        values = {} -        for ve in e: -            if ve.tag != BXW+"value": -                raise LoadError("Invalid value tag") - -            fixture = cls.int_or_none(ve.get("fixture")) -            channel = cls.int_or_none(ve.get("channel")) - -            if None in (fixture, channel): -                raise LoadError("Missing/invalid fixture/channel value") -            elif fixture not in w.fixtures: -                raise LoadError("Missing fixture ID %d" % fixture) -            elif channel >= len(w.fixtures[fixture].channels): -                raise LoadError("Fixture %d missing channel ID %d" % (fixture, channel)) - -            channel = w.fixtures[fixture].channels[channel] - -            value = cls.int_or_none(ve.text) -            if value is None: -                raise LoadError("Missing/invalid value for channel") - -            values[channel] = value - -        return cls(w=w, id_=id_, name=name, values=values)  | 
