summaryrefslogtreecommitdiff
path: root/functions/audio.py
blob: d0e599df14a499fe88b108b454d5300885e2f718 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
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)