diff options
Diffstat (limited to 'functions/audio.py')
-rw-r--r-- | functions/audio.py | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/functions/audio.py b/functions/audio.py new file mode 100644 index 0000000..d0e599d --- /dev/null +++ b/functions/audio.py @@ -0,0 +1,118 @@ +"""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) |