diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..823f5e2d
Binary files /dev/null and b/.DS_Store differ
diff --git a/ear/.DS_Store b/ear/.DS_Store
new file mode 100644
index 00000000..2a5e7d2f
Binary files /dev/null and b/ear/.DS_Store differ
diff --git a/ear/core/.DS_Store b/ear/core/.DS_Store
new file mode 100644
index 00000000..10b2579f
Binary files /dev/null and b/ear/core/.DS_Store differ
diff --git a/ear/fileio/.DS_Store b/ear/fileio/.DS_Store
new file mode 100644
index 00000000..d84f8d58
Binary files /dev/null and b/ear/fileio/.DS_Store differ
diff --git a/ear/fileio/adm/.DS_Store b/ear/fileio/adm/.DS_Store
new file mode 100644
index 00000000..2fdb0a8a
Binary files /dev/null and b/ear/fileio/adm/.DS_Store differ
diff --git a/ear/fileio/adm/elements/__init__.py b/ear/fileio/adm/elements/__init__.py
index 504e0794..91213ae0 100644
--- a/ear/fileio/adm/elements/__init__.py
+++ b/ear/fileio/adm/elements/__init__.py
@@ -1,10 +1,12 @@
# flake8: noqa
from .main_elements import (AudioChannelFormat, AudioPackFormat, AudioTrackFormat,
- AudioStreamFormat, AudioProgramme, AudioContent, AudioObject, AudioTrackUID,
- FormatDefinition, TypeDefinition, Frequency)
+ AudioStreamFormat, AudioProgramme, AudioContent,
+ AudioObject, AudioTrackUID, FormatDefinition, TypeDefinition,
+ Frequency)
from .block_formats import (AudioBlockFormatObjects, ChannelLock, ObjectDivergence,
- JumpPosition, AudioBlockFormatDirectSpeakers, AudioBlockFormatBinaural, AudioBlockFormatHoa,
- AudioBlockFormatMatrix, MatrixCoefficient,
+ JumpPosition, AudioBlockFormatDirectSpeakers, AudioBlockFormatBinaural,
+ AudioBlockFormatHoa, AudioBlockFormatMatrix, MatrixCoefficient,
CartesianZone, PolarZone)
from .geom import (DirectSpeakerPolarPosition, DirectSpeakerCartesianPosition, BoundCoordinate,
ObjectPolarPosition, ObjectCartesianPosition, ScreenEdgeLock)
+from .interaction import (AudioObjectInteraction, GainInteractionRange, PositionInteractionRange)
diff --git a/ear/fileio/adm/elements/interaction.py b/ear/fileio/adm/elements/interaction.py
new file mode 100644
index 00000000..b18d9973
--- /dev/null
+++ b/ear/fileio/adm/elements/interaction.py
@@ -0,0 +1,37 @@
+from attr import attrs, attrib, Factory
+from attr.validators import instance_of, optional
+
+
+@attrs(slots=True)
+class PositionInteractionRange(object):
+ minAzimuth = attrib(default=None, validator=optional(instance_of(float)))
+ maxAzimuth = attrib(default=None, validator=optional(instance_of(float)))
+ minElevation = attrib(default=None, validator=optional(instance_of(float)))
+ maxElevation = attrib(default=None, validator=optional(instance_of(float)))
+ minDistance = attrib(default=None, validator=optional(instance_of(float)))
+ maxDistance = attrib(default=None, validator=optional(instance_of(float)))
+ minX = attrib(default=None, validator=optional(instance_of(float)))
+ maxX = attrib(default=None, validator=optional(instance_of(float)))
+ minY = attrib(default=None, validator=optional(instance_of(float)))
+ maxY = attrib(default=None, validator=optional(instance_of(float)))
+ minZ = attrib(default=None, validator=optional(instance_of(float)))
+ maxZ = attrib(default=None, validator=optional(instance_of(float)))
+
+
+@attrs(slots=True)
+class GainInteractionRange(object):
+ min = attrib(default=None, validator=optional(instance_of(float)))
+ max = attrib(default=None, validator=optional(instance_of(float)))
+
+
+@attrs(slots=True)
+class AudioObjectInteraction(object):
+ onOffInteract = attrib(validator=instance_of(bool))
+ gainInteract = attrib(default=None, validator=optional(instance_of(bool)))
+ positionInteract = attrib(default=None, validator=optional(instance_of(bool)))
+ gainInteractionRange = attrib(default=None,
+ validator=optional(instance_of(GainInteractionRange)),
+ )
+ positionInteractionRange = attrib(default=None,
+ validator=optional(instance_of(PositionInteractionRange)),
+ )
diff --git a/ear/fileio/adm/elements/main_elements.py b/ear/fileio/adm/elements/main_elements.py
index 58c4e65d..acc29a1f 100644
--- a/ear/fileio/adm/elements/main_elements.py
+++ b/ear/fileio/adm/elements/main_elements.py
@@ -6,6 +6,7 @@
from ..exceptions import AdmError
from ....common import CartesianScreen, PolarScreen, default_screen, list_of
+from .interaction import AudioObjectInteraction
def _lookup_elements(adm, idRefs):
@@ -83,7 +84,6 @@ def lazy_lookup_references(self, adm):
self.audioObjects = _lookup_elements(adm, self.audioObjectIDRef)
self.audioObjectIDRef = None
-
@attrs(slots=True)
class AudioObject(ADMElement):
audioObjectName = attrib(default=None, validator=instance_of(string_types))
@@ -97,6 +97,8 @@ class AudioObject(ADMElement):
audioTrackUIDs = attrib(default=Factory(list), repr=False)
audioObjects = attrib(default=Factory(list), repr=False)
audioComplementaryObjects = attrib(default=Factory(list), repr=False)
+ audioObjectInteraction = attrib(default=None,
+ validator=optional(instance_of(AudioObjectInteraction)))
audioPackFormatIDRef = attrib(default=None)
audioTrackUIDRef = attrib(default=None)
diff --git a/ear/fileio/adm/test/.DS_Store b/ear/fileio/adm/test/.DS_Store
new file mode 100644
index 00000000..cebb471e
Binary files /dev/null and b/ear/fileio/adm/test/.DS_Store differ
diff --git a/ear/fileio/adm/test/test_adm_files/base.xml b/ear/fileio/adm/test/test_adm_files/base.xml
index 61b0329d..f2a58977 100644
--- a/ear/fileio/adm/test/test_adm_files/base.xml
+++ b/ear/fileio/adm/test/test_adm_files/base.xml
@@ -12,6 +12,14 @@
AP_00031001
ATU_00000001
+
+ 0.5
+ 2.0
+ -10.0
+ +10.0
+ -30.0
+ +30.0
+
AC_00031001
diff --git a/ear/fileio/adm/test/test_xml.py b/ear/fileio/adm/test/test_xml.py
index d4109310..9198def1 100644
--- a/ear/fileio/adm/test/test_xml.py
+++ b/ear/fileio/adm/test/test_xml.py
@@ -111,6 +111,8 @@ def get_acf(adm):
bf_path = "//adm:audioBlockFormat"
+def test_interact(base):
+ assert base.adm_after_mods()
def test_gain(base):
assert base.bf_after_mods(add_children(bf_path, E.gain("0"))).gain == 0.0
@@ -719,6 +721,7 @@ def check_round_trip(adm):
xml = adm_to_xml(adm)
xml_str = lxml.etree.tostring(xml, pretty_print=True)
parsed_adm = parse_string(xml_str)
+ print(xml_str)
assert len(list(parsed_adm.elements)) == len(list(adm.elements))
diff --git a/ear/fileio/adm/xml.py b/ear/fileio/adm/xml.py
index 2405a4bf..c56af471 100644
--- a/ear/fileio/adm/xml.py
+++ b/ear/fileio/adm/xml.py
@@ -13,14 +13,13 @@
AudioBlockFormatObjects, AudioBlockFormatDirectSpeakers, AudioBlockFormatBinaural, AudioBlockFormatHoa, AudioBlockFormatMatrix,
ChannelLock, BoundCoordinate, JumpPosition, ObjectDivergence, CartesianZone, PolarZone, ScreenEdgeLock, MatrixCoefficient)
from .elements import (
- AudioProgramme, AudioContent, AudioObject, AudioChannelFormat, AudioPackFormat, AudioStreamFormat, AudioTrackFormat, AudioTrackUID,
- FormatDefinition, TypeDefinition, Frequency)
+ AudioProgramme, AudioContent, AudioObject, AudioObjectInteraction, AudioChannelFormat, AudioPackFormat, AudioStreamFormat, AudioTrackFormat, AudioTrackUID,
+ FormatDefinition, GainInteractionRange, PositionInteractionRange, TypeDefinition, Frequency)
from .elements.geom import (DirectSpeakerPolarPosition, DirectSpeakerCartesianPosition,
ObjectPolarPosition, ObjectCartesianPosition)
from .time_format import parse_time, unparse_time
from ...common import PolarPosition, CartesianPosition, CartesianScreen, PolarScreen
-
namespaces = [None,
"urn:ebu:metadata-schema:ebuCore_2014",
"urn:ebu:metadata-schema:ebuCore_2015",
@@ -875,6 +874,65 @@ def frequency_to_xml(parent, obj):
parent.append(element)
+def handle_objectInteraction(kwargs, el):
+ objectInteraction = kwargs.setdefault("audioObjectInteraction",
+ AudioObjectInteraction(onOffInteract=BoolType.loads_func(el.attrib["onOffInteract"])))
+ if objectInteraction.gainInteract is not None:
+ objectInteraction.gainInteract = BoolType.loads_func(el.attrib["gainInteract"])
+ if objectInteraction.positionInteract is not None:
+ objectInteraction.positionInteract = BoolType.loads_func(el.attrib["positionInteract"])
+
+ if any([e.text for e in el.getiterator() if e.tag in qnames("gainInteractionRange")]):
+ objectInteraction.gainInteractionRange = GainInteractionRange()
+ if any([e.text for e in el.getiterator() if e.tag in qnames("positionInteractionRange")]):
+ objectInteraction.positionInteractionRange = PositionInteractionRange()
+ for element in el.getiterator():
+ if element.tag in qnames("gainInteractionRange"):
+ if element.attrib['bound'] == 'min':
+ objectInteraction.gainInteractionRange.min = float(element.text)
+ elif element.attrib['bound'] == 'max':
+ objectInteraction.gainInteractionRange.max = float(element.text)
+ elif element.tag in qnames("positionInteractionRange"):
+ attribute = str(element.attrib['bound'])+str(element.attrib['coordinate']).capitalize()
+ if attribute in [a for a in dir(objectInteraction.positionInteractionRange) if not a.startswith('__')]:
+ setattr(objectInteraction.positionInteractionRange, attribute, float(element.text))
+
+def objectInteraction_to_xml(parent, obj):
+ if obj.audioObjectInteraction is not None:
+ if obj.audioObjectInteraction.gainInteract is not None and obj.audioObjectInteraction.positionInteract is not None:
+ element = parent.makeelement(QName(default_ns, "audioObjectInteraction"),
+ onOffInteract=BoolType.dumps(obj.audioObjectInteraction.onOffInteract),
+ gainInteract=BoolType.dumps(obj.audioObjectInteraction.gainInteract),
+ positionInteract=BoolType.dumps(obj.audioObjectInteraction.positionInteract))
+ elif obj.audioObjectInteraction.gainInteract is not None and obj.audioObjectInteraction.positionInteract is None:
+ element = parent.makeelement(QName(default_ns, "audioObjectInteraction"),
+ onOffInteract=BoolType.dumps(obj.audioObjectInteraction.onOffInteract),
+ gainInteract=BoolType.dumps(obj.audioObjectInteraction.gainInteract))
+ elif obj.audioObjectInteraction.gainInteract is None and obj.audioObjectInteraction.positionInteract is not None:
+ element = parent.makeelement(QName(default_ns, "audioObjectInteraction"),
+ onOffInteract=BoolType.dumps(obj.audioObjectInteraction.onOffInteract),
+ positionInteract=BoolType.dumps(obj.audioObjectInteraction.positionInteract))
+ else:
+ element = parent.makeelement(QName(default_ns, "audioObjectInteraction"),
+ onOffInteract=BoolType.dumps(obj.audioObjectInteraction.onOffInteract))
+
+ if obj.audioObjectInteraction.gainInteractionRange is not None:
+ for attribute in [a for a in dir(obj.audioObjectInteraction.gainInteractionRange) if not a.startswith('__')]:
+ if getattr(obj.audioObjectInteraction.gainInteractionRange, attribute) is not None:
+ element_c = element.makeelement(QName(default_ns, "gainInteractionRange"),
+ bound=attribute)
+ element_c.text = FloatType.dumps(getattr(obj.audioObjectInteraction.gainInteractionRange, attribute))
+ element.append(element_c)
+ if obj.audioObjectInteraction.positionInteractionRange is not None:
+ for attribute in [a for a in dir(obj.audioObjectInteraction.positionInteractionRange) if not a.startswith('__')]:
+ if getattr(obj.audioObjectInteraction.positionInteractionRange, attribute) is not None:
+ element_c = element.makeelement(QName(default_ns, "positionInteractionRange"),
+ coordinate=attribute[3:].lower(), bound=attribute[:3])
+ element_c.text = FloatType.dumps(getattr(obj.audioObjectInteraction.positionInteractionRange, attribute))
+ element.append(element_c)
+ parent.append(element)
+
+
channel_format_handler = ElementParser(AudioChannelFormat, "audioChannelFormat", [
Attribute(adm_name="audioChannelFormatID", arg_name="id", required=True),
Attribute(adm_name="audioChannelFormatName", arg_name="audioChannelFormatName", required=True),
@@ -1052,6 +1110,8 @@ def make_audio_programme(referenceScreen=None, **kwargs):
RefList("audioObject"),
])
+
+
object_handler = ElementParser(AudioObject, "audioObject", [
Attribute(adm_name="audioObjectID", arg_name="id", required=True),
Attribute(adm_name="audioObjectName", arg_name="audioObjectName", required=True),
@@ -1065,6 +1125,7 @@ def make_audio_programme(referenceScreen=None, **kwargs):
RefList("audioObject"),
RefList("audioComplementaryObject"),
ListElement(adm_name="audioTrackUIDRef", arg_name="audioTrackUIDRef", attr_name="audioTrackUIDs", type=TrackUIDRefType),
+ CustomElement("audioObjectInteraction", handle_objectInteraction, to_xml=objectInteraction_to_xml),
])
track_uid_handler = ElementParser(AudioTrackUID, "audioTrackUID", [