Add experimental JOC binaural mode controls

This commit is contained in:
2026-09-03 02:59:44 +08:00
parent 887b3e317f
commit 329445ed25
5 changed files with 66 additions and 8 deletions
+8 -4
View File
@@ -14,7 +14,7 @@ import adm_atmos
def assemble_from_raw(raw16_path, out_path, scale=1.0, kf_tracks=None,
duration_sec=None, rate=48000):
duration_sec=None, rate=48000, joc_binaural_mode=4):
"""16ch f32 交织 raw → 25ch ADM BWF(空 7.1.2 bed + LFE + 15 对象)。
raw16: (n, 16) 交织(ch0 = LFE,ch1-15 = 对象)。
@@ -54,7 +54,8 @@ def assemble_from_raw(raw16_path, out_path, scale=1.0, kf_tracks=None,
kf_tracks.append(("JOC_Object_%d" % (oi + 1),
[(0.0, 0.0, 0.0, 0.0, max(duration_sec, 1e-6))]))
adm_atmos.build_master(out_path, BedView(), ObjView(), kf_tracks,
duration_sec, rate=rate)
duration_sec, rate=rate,
joc_binaural_mode=joc_binaural_mode)
# 及时释放 Windows 文件句柄,允许 TemporaryDirectory 删除中间 raw。
raw._mmap.close()
return out_path
@@ -69,12 +70,14 @@ class StreamingMaster:
channels 10..24.
"""
def __init__(self, out_path, duration_sec, rate=48000, block_samples=131072):
def __init__(self, out_path, duration_sec, rate=48000, block_samples=131072,
joc_binaural_mode=4):
if block_samples < 1536:
raise ValueError("block_samples must be at least one E-AC-3 frame")
self.out_path = os.fspath(out_path)
self.duration_sec = float(duration_sec)
self.rate = int(rate)
self.joc_binaural_mode = joc_binaural_mode
self._sink = adm_atmos.Sink25(self.out_path, 25, self.rate)
self._buffer = np.empty((int(block_samples), 25), dtype=np.float32)
self._used = 0
@@ -112,7 +115,8 @@ class StreamingMaster:
import adm_serializer
axml = adm_serializer.build_axml(kf_tracks, self.duration_sec)
chna = adm_atmos.build_chna()
dbmd = adm_atmos.build_dbmd(25)
dbmd = adm_atmos.build_dbmd(
25, joc_binaural_mode=self.joc_binaural_mode)
trajectory_blocks = sum(len(track[1]) for track in kf_tracks)
self.metadata_info = {
"axml_bytes": len(axml),
+22 -3
View File
@@ -2,6 +2,7 @@
输出由 10 声道 7.1.2 bed 和 15 路对象组成;RF64 尺寸字段在写入完成后回填。
"""
import operator
import struct
import numpy as np
import xml.etree.ElementTree as ET
@@ -21,6 +22,14 @@ BED_POS = [(-1.0, 1.0, 0.0), (1.0, 1.0, 0.0), (0.0, 1.0, 0.0),
(-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (-1.0, 0.0, 1.0), (1.0, 0.0, 1.0)]
N_OBJ = 15
JOC_BINAURAL_MODES = {
"off": 0,
"near": 1,
"far": 2,
"mid": 3,
"unspecified": 4,
}
JOC_BINAURAL_MODE_DEFAULT = "unspecified"
def q_to_adm_xyz(q1, q2, q3):
posX = min(1.0, round(q1 * 62 / 32767.0) / 62.0)
@@ -186,7 +195,11 @@ def _checksum(seg):
s += b
return (~s + 1) & 0xFF
def build_dbmd(object_count=25):
def build_dbmd(object_count=25, joc_binaural_mode=4):
"""仅覆盖 segment 10 中 JOC object slots 10..24 的 mode 低 3 bit。"""
mode = operator.index(joc_binaural_mode)
if mode not in JOC_BINAURAL_MODES.values():
raise ValueError(f"invalid JOC binaural render mode: {mode}")
out = bytearray(struct.pack("<I", 0x01000006))
dd = bytearray(96)
dd[1] = 0x47
@@ -209,6 +222,12 @@ def build_dbmd(object_count=25):
ob[4] = object_count
for i in range(5 + 262, len(ob)):
ob[i] = 0x84
# sync (4), count (2), reserved (1), nine 15-byte config trims,
# then one trim-bypass byte per track before the headphone modes.
# Preserve the existing template's bed fields and trailing bytes.
object_modes = 4 + 2 + 1 + 9 * 15 + object_count
for i in range(10, min(object_count, 10 + N_OBJ)):
ob[object_modes + i] = (ob[object_modes + i] & 0xF8) | mode
out.append(10); out += struct.pack("<H", len(ob)); out += bytes(ob)
out.append(_checksum(ob))
out += b"\x00\x00"
@@ -252,7 +271,7 @@ class Sink25:
self.fp.close()
def build_master(out_path, bed_mm, obj_mm, kf_tracks, duration_sec, rate=48000,
block=480000):
block=480000, joc_binaural_mode=4):
n = min(bed_mm.shape[0], obj_mm.shape[0])
try:
from . import adm_serializer
@@ -261,7 +280,7 @@ def build_master(out_path, bed_mm, obj_mm, kf_tracks, duration_sec, rate=48000,
serial_axml = adm_serializer.build_axml
axml = serial_axml(kf_tracks, duration_sec)
chna = build_chna()
dbmd = build_dbmd(25)
dbmd = build_dbmd(25, joc_binaural_mode=joc_binaural_mode)
sink = Sink25(out_path, 25, rate)
for st in range(0, n, block):
en = min(n, st + block)