Skip to content

Commit d179bd6

Browse files
authored
Merge pull request #50 from timlehr/audioPan_tlehr
Writer: default audio pan to mid, can now be set via metadata
2 parents b18abe2 + 264fdaa commit d179bd6

File tree

2 files changed

+98
-11
lines changed

2 files changed

+98
-11
lines changed

src/otio_aaf_adapter/adapters/aaf_adapter/aaf_writer.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -762,20 +762,37 @@ def aaf_sourceclip(self, otio_clip):
762762
"LinearInterp",
763763
"LinearInterp")
764764
self.aaf_file.dictionary.register_def(interp_def)
765-
# PointList
766-
length = int(otio_clip.duration().value)
767-
c1 = self.aaf_file.create.ControlPoint()
768-
c1["ControlPointSource"].value = 2
769-
c1["Time"].value = aaf2.rational.AAFRational(f"0/{length}")
770-
c1["Value"].value = 0
771-
c2 = self.aaf_file.create.ControlPoint()
772-
c2["ControlPointSource"].value = 2
773-
c2["Time"].value = aaf2.rational.AAFRational(f"{length - 1}/{length}")
774-
c2["Value"].value = 0
765+
766+
# generate PointList for pan
775767
varying_value = self.aaf_file.create.VaryingValue()
776768
varying_value.parameterdef = param_def
777769
varying_value["Interpolation"].value = interp_def
778-
varying_value["PointList"].extend([c1, c2])
770+
771+
length = int(otio_clip.duration().value)
772+
773+
# default pan points are mid pan
774+
default_points = [
775+
{
776+
"ControlPointSource": 2,
777+
"Time": f"0/{length}",
778+
"Value": "1/2",
779+
},
780+
{
781+
"ControlPointSource": 2,
782+
"Time": f"{length - 1}/{length}",
783+
"Value": "1/2",
784+
}
785+
]
786+
cp_dict_list = otio_clip.metadata.get("AAF", {}).get("Pan", {}).get(
787+
"ControlPoints", default_points)
788+
789+
for cp_dict in cp_dict_list:
790+
point = self.aaf_file.create.ControlPoint()
791+
point["Time"].value = aaf2.rational.AAFRational(cp_dict["Time"])
792+
point["Value"].value = aaf2.rational.AAFRational(cp_dict["Value"])
793+
point["ControlPointSource"].value = cp_dict["ControlPointSource"]
794+
varying_value["PointList"].append(point)
795+
779796
opgroup = self.timeline_mobslot.segment
780797
opgroup.parameters.append(varying_value)
781798

tests/test_aaf_adapter.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,76 @@ def test_aaf_writer_global_start_time(self):
19371937

19381938
self._verify_aaf(tmp_aaf_path)
19391939

1940+
def test_aaf_writer_audio_pan(self):
1941+
"""Test Clip with custom audio pan values"""
1942+
tl = otio.schema.Timeline()
1943+
1944+
# Add an audio clip with pan metadata
1945+
clip = otio.schema.Clip(
1946+
name="Panned Audio Clip",
1947+
metadata={},
1948+
source_range=otio.opentime.TimeRange(
1949+
start_time=otio.opentime.RationalTime(0, 24),
1950+
duration=otio.opentime.RationalTime(100, 24),
1951+
)
1952+
)
1953+
clip.media_reference = otio.schema.MissingReference(
1954+
available_range=otio.opentime.TimeRange(
1955+
start_time=otio.opentime.RationalTime(0, 24),
1956+
duration=otio.opentime.RationalTime(100, 24),
1957+
))
1958+
1959+
# Add pan metadata
1960+
clip.metadata["AAF"] = {
1961+
"Pan": {
1962+
"ControlPoints": [
1963+
{
1964+
"ControlPointSource": 2,
1965+
"Time": "0",
1966+
"Value": "0",
1967+
},
1968+
{
1969+
"ControlPointSource": 2,
1970+
"Time": "100",
1971+
"Value": "1",
1972+
}
1973+
]
1974+
},
1975+
"SourceID": "urn:smpte:umid:060a2b34.01010101.01010f00."
1976+
"13000000.060e2b34.7f7f2a80.5c9e6a3b.ace913a2"
1977+
}
1978+
1979+
tl.tracks.append(
1980+
otio.schema.Track(children=[clip], kind=otio.schema.TrackKind.Audio)
1981+
)
1982+
1983+
_, tmp_aaf_path = tempfile.mkstemp(suffix='.aaf')
1984+
otio.adapters.write_to_file(tl, tmp_aaf_path)
1985+
print(tmp_aaf_path)
1986+
1987+
# verify pan values in AAF file
1988+
with aaf2.open(tmp_aaf_path) as aaf_file:
1989+
mob = next(aaf_file.content.compositionmobs())
1990+
slot = mob.slots[0]
1991+
parameter = list(slot.segment.parameters)[0]
1992+
1993+
# extract the pan values
1994+
param_dicts = [
1995+
{k: v.value for k, v in dict(p).items()}
1996+
for p in parameter.pointlist
1997+
]
1998+
1999+
expected = [
2000+
{'ControlPointSource': 2,
2001+
'Time': aaf2.rational.AAFRational(0, 1),
2002+
'Value': aaf2.rational.AAFRational(0, 1)},
2003+
{'ControlPointSource': 2,
2004+
'Time': aaf2.rational.AAFRational(100, 1),
2005+
'Value': aaf2.rational.AAFRational(1, 1)}
2006+
]
2007+
2008+
self.assertEqual(param_dicts, expected)
2009+
19402010
def _verify_aaf(self, aaf_path):
19412011
otio_timeline = otio.adapters.read_from_file(aaf_path, simplify=True)
19422012
fd, tmp_aaf_path = tempfile.mkstemp(suffix='.aaf')

0 commit comments

Comments
 (0)