mirror of
https://github.com/zeldaret/oot.git
synced 2024-11-15 06:06:04 +00:00
120 lines
3.3 KiB
Python
120 lines
3.3 KiB
Python
|
# SPDX-FileCopyrightText: © 2024 ZeldaRET
|
||
|
# SPDX-License-Identifier: CC0-1.0
|
||
|
#
|
||
|
# Implements envelopes and envelope point structures
|
||
|
#
|
||
|
|
||
|
import collections
|
||
|
|
||
|
from .util import XMLWriter
|
||
|
|
||
|
class EnvDelay(int):
|
||
|
def __str__(self):
|
||
|
return {
|
||
|
0 : "ADSR_DISABLE",
|
||
|
-1 : "ADSR_HANG",
|
||
|
-2 : "ADSR_GOTO",
|
||
|
-3 : "ADSR_RESTART",
|
||
|
}.get(self, super().__str__())
|
||
|
|
||
|
class Envelope:
|
||
|
"""
|
||
|
Array of envelope points
|
||
|
"""
|
||
|
|
||
|
class EnvelopePoint:
|
||
|
"""
|
||
|
typedef struct {
|
||
|
/* 0x0 */ s16 delay;
|
||
|
/* 0x2 */ s16 arg;
|
||
|
} EnvelopePoint; // size = 0x4
|
||
|
"""
|
||
|
|
||
|
def __init__(self, delay, arg):
|
||
|
self.delay = EnvDelay(delay)
|
||
|
self.arg = arg
|
||
|
|
||
|
def __repr__(self):
|
||
|
return str(self)
|
||
|
|
||
|
def __str__(self):
|
||
|
return f"{{ {self.delay}, {self.arg} }}"
|
||
|
|
||
|
def is_disable(self):
|
||
|
return self.delay == 0 and self.arg == 0
|
||
|
|
||
|
def is_hang(self):
|
||
|
return self.delay == -1 and self.arg == 0
|
||
|
|
||
|
def to_xml(self, xml : XMLWriter):
|
||
|
if self.delay == 0: # Disable
|
||
|
assert self.arg == 0
|
||
|
xml.write_element("Disable")
|
||
|
elif self.delay == -1: # Hang
|
||
|
assert self.arg == 0
|
||
|
xml.write_element("Hang")
|
||
|
elif self.delay == -2: # Goto
|
||
|
xml.write_element("Goto",
|
||
|
{ "Arg" : self.arg }
|
||
|
)
|
||
|
elif self.delay == -3: # Restart
|
||
|
assert self.arg == 0
|
||
|
xml.write_element("Restart")
|
||
|
else:
|
||
|
assert self.delay >= 0
|
||
|
xml.write_element("Point",
|
||
|
{
|
||
|
"Delay" : self.delay,
|
||
|
"Arg" : self.arg,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
def __init__(self, points, is_zero=False):
|
||
|
self.name = None # Assigned when bank is finalized
|
||
|
|
||
|
self.is_zero = is_zero
|
||
|
self.release_rates = []
|
||
|
self._release_rate = None # cached
|
||
|
|
||
|
assert len(points) != 0
|
||
|
assert type(points[0]) == Envelope.EnvelopePoint
|
||
|
self.points = points
|
||
|
|
||
|
if not self.is_zero:
|
||
|
while self.points[-1].is_disable():
|
||
|
self.points.pop()
|
||
|
|
||
|
assert self.points[-1].is_hang()
|
||
|
|
||
|
def __str__(self):
|
||
|
out = "{\n"
|
||
|
out += " " + ", ".join([str(point) for point in self.points]) + "\n"
|
||
|
out += "}\n"
|
||
|
return out
|
||
|
|
||
|
def release_rate(self):
|
||
|
if self._release_rate is not None:
|
||
|
return self._release_rate
|
||
|
|
||
|
rates = collections.Counter(self.release_rates).most_common()
|
||
|
assert len(rates) in [0, 1], rates # TODO handle ties?
|
||
|
|
||
|
self._release_rate = 0 if len(rates) == 0 else rates[0][0]
|
||
|
return self._release_rate
|
||
|
|
||
|
def to_xml(self, xml : XMLWriter, name : str):
|
||
|
if self.is_zero:
|
||
|
return xml.write_element("Envelope")
|
||
|
|
||
|
xml.write_start_tag("Envelope",
|
||
|
{
|
||
|
"Name" : name,
|
||
|
"Release" : self.release_rate(),
|
||
|
}
|
||
|
)
|
||
|
|
||
|
for point in self.points[:-1]: # exclude final hang command, will be added by the soundfont compiler on build
|
||
|
point.to_xml(xml)
|
||
|
|
||
|
xml.write_end_tag()
|