#include "adm/adm_metadata.h" #include #include #include #include "foundation/py_num.h" namespace joc::adm { namespace { constexpr const char* kBedNames[10] = { "RoomCentricLeft", "RoomCentricRight", "RoomCentricCenter", "RoomCentricLFE", "RoomCentricLeftSideSurround", "RoomCentricRightSideSurround", "RoomCentricLeftRearSurround", "RoomCentricRightRearSurround", "RoomCentricLeftTopSurround", "RoomCentricRightTopSurround"}; constexpr const char* kBedLabels[10] = {"RC_L", "RC_R", "RC_C", "RC_LFE", "RC_Lss", "RC_Rss", "RC_Lrs", "RC_Rrs", "RC_Lts", "RC_Rts"}; constexpr double kBedPos[10][3] = {{-1.0, 1.0, 0.0}, {1.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {-1.0, 1.0, -1.0}, {-1.0, 0.0, 0.0}, {1.0, 0.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}}; std::string hex4(std::uint32_t value) { char buffer[16]; std::snprintf(buffer, sizeof(buffer), "%04x", value); return std::string(buffer); } std::string hex8(std::uint32_t value) { char buffer[16]; std::snprintf(buffer, sizeof(buffer), "%08x", value); return std::string(buffer); } void put_u16(std::string* out, std::uint16_t value) { char buffer[2]; std::memcpy(buffer, &value, 2); out->append(buffer, 2); } void put_u32(std::string* out, std::uint32_t value) { char buffer[4]; std::memcpy(buffer, &value, 4); out->append(buffer, 4); } std::uint8_t checksum(const std::string& segment) { int sum = static_cast(segment.size()); for (const char raw : segment) { sum += static_cast(raw); } return static_cast((~sum + 1) & 0xFF); } } // namespace std::string ts(double seconds) { long long whole = static_cast(seconds); long long fraction = pynum::py_round((seconds - static_cast(whole)) * 100000.0); if (fraction >= 100000) { whole += 1; fraction = 0; } char buffer[32]; std::snprintf(buffer, sizeof(buffer), "%02lld:%02lld:%02lld.%05lld", whole / 3600, (whole % 3600) / 60, whole % 60, fraction); return std::string(buffer); } bool binaural_mode_from_name(const char* name, BinauralMode* out) { if (name == nullptr || out == nullptr) { return false; } if (std::strcmp(name, "off") == 0) { *out = BinauralMode::Off; return true; } if (std::strcmp(name, "near") == 0) { *out = BinauralMode::Near; return true; } if (std::strcmp(name, "far") == 0) { *out = BinauralMode::Far; return true; } if (std::strcmp(name, "mid") == 0) { *out = BinauralMode::Mid; return true; } if (std::strcmp(name, "unspecified") == 0) { *out = BinauralMode::Unspecified; return true; } return false; } std::string build_chna() { std::string out; put_u16(&out, static_cast(kTrackCount)); put_u16(&out, static_cast(kTrackCount)); for (std::uint32_t i = 0; i < 10; ++i) { put_u16(&out, static_cast(i + 1)); out += "ATU_" + hex8(i + 1); out += "AT_0001" + hex4(0x1001 + i) + "_01"; out += "AP_00011001"; out.push_back('\0'); } for (std::uint32_t i = 0; i < kObjectCount; ++i) { put_u16(&out, static_cast(i + 11)); out += "ATU_" + hex8(i + 11); out += "AT_0003" + hex4(0x1001 + i) + "_01"; out += "AP_0003" + hex4(0x1001 + i); out.push_back('\0'); } return out; } Status build_dbmd(std::uint32_t object_count, BinauralMode mode, std::string* out) { if (out == nullptr) { return Status::fail(JOC_ERR_INVALID_ARGUMENT, stage::kOutput, "null output"); } const std::uint32_t mode_value = static_cast(mode); if (mode_value > 4u) { return Status::fail(JOC_ERR_INVALID_ARGUMENT, stage::kOutput, "invalid JOC binaural render mode"); } out->clear(); put_u32(out, 0x01000006u); std::string segment7(96, '\0'); segment7[1] = static_cast(0x47); segment7[5] = static_cast(0x60); segment7[8] = static_cast(0x24); segment7[9] = static_cast(0x24); out->push_back(7); put_u16(out, 96); out->append(segment7); out->push_back(static_cast(checksum(segment7))); std::string segment9(248, '\0'); const std::string creator = "Created with EAC3JOC"; const std::string renderer = "EAC3JOC Python Renderer"; std::memcpy(&segment9[0], creator.data(), creator.size()); std::memcpy(&segment9[32], renderer.data(), renderer.size()); segment9[96] = 2; segment9[97] = 1; segment9[98] = 0; segment9[103] = 0x03; segment9[106] = 0x01; segment9[111] = 0x22; segment9[112] = static_cast(0xFF); out->push_back(9); put_u16(out, 248); out->append(segment9); out->push_back(static_cast(checksum(segment9))); // The reference allocates the body zeroed and then fills only the trailing // `object_count` bytes with 0x84, so the template region stays zero. const std::size_t object_body = 5u + 262u + object_count; std::string segment10(object_body, '\0'); const std::uint32_t sync = 0xF8726FBDu; std::memcpy(&segment10[0], &sync, 4); segment10[4] = static_cast(object_count); for (std::size_t i = 5u + 262u; i < segment10.size(); ++i) { segment10[i] = static_cast(0x84); } const std::size_t object_modes = 4u + 2u + 1u + 9u * 15u + object_count; for (std::uint32_t i = 10; i < std::min(object_count, 10u + kObjectCount); ++i) { const std::size_t index = object_modes + i; if (index >= segment10.size()) { return Status::fail(JOC_ERR_INTERNAL, stage::kOutput, "dbmd object slot out of range"); } segment10[index] = static_cast((static_cast(segment10[index]) & 0xF8u) | mode_value); } out->push_back(10); put_u16(out, static_cast(segment10.size())); out->append(segment10); out->push_back(static_cast(checksum(segment10))); out->append("\0\0", 2); return Status::success(); } std::string build_axml(const std::vector& tracks, double duration_sec, std::uint32_t rate) { const double scale = static_cast(rate); std::string out; out.reserve(64u * 1024u); const std::string duration_ts = ts(duration_sec); out += ""; out += ""; out += ""; out += ""; out += "ACO_1001"; out += "ACO_1002"; out += ""; out += ""; out += "AO_1001"; out += "2"; out += ""; out += ""; for (std::uint32_t i = 0; i < kObjectCount; ++i) { out += "AO_" + hex4(0x100b + i) + ""; } out += "2"; out += ""; out += ""; out += "AP_00011001"; for (std::uint32_t i = 0; i < 10; ++i) { out += "ATU_" + hex8(i + 1) + ""; } out += ""; for (std::uint32_t i = 0; i < kObjectCount; ++i) { out += ""; out += "AP_0003" + hex4(0x1001 + i) + ""; out += "ATU_" + hex8(11 + i) + ""; out += ""; } out += ""; for (std::uint32_t i = 0; i < 10; ++i) { out += "AC_0001" + hex4(0x1001 + i) + ""; } out += ""; for (std::uint32_t i = 0; i < kObjectCount; ++i) { out += ""; out += "AC_0003" + hex4(0x1001 + i) + ""; out += ""; } for (std::uint32_t i = 0; i < 10; ++i) { out += ""; out += ""; out += "1"; out += "" + pynum::format_fixed(kBedPos[i][0], 10) + ""; out += "" + pynum::format_fixed(kBedPos[i][1], 10) + ""; if (kBedPos[i][2] != 0.0) { out += "" + pynum::format_fixed(kBedPos[i][2], 10) + ""; } out += std::string("") + kBedLabels[i] + ""; out += ""; out += ""; } for (std::size_t i = 0; i < tracks.size(); ++i) { const Track& track = tracks[i]; out += "(i)) + "\" audioChannelFormatName=\"" + track.name + "\" typeDefinition=\"Objects\" typeLabel=\"0003\">"; for (std::size_t k = 0; k < track.blocks.size(); ++k) { const Keyframe& block = track.blocks[k]; out += "(i)) + "_" + hex8(static_cast(k + 1)) + "\" rtime=\"" + ts(static_cast(block.rtime_samples) / scale) + "\" duration=\"" + ts(static_cast(block.duration_samples) / scale) + "\">"; out += "1"; out += "" + pynum::format_fixed(block.x, 10) + ""; out += "" + pynum::format_fixed(block.y, 10) + ""; if (block.z != 0.0) { out += "" + pynum::format_fixed(block.z, 10) + ""; } out += "(block.interpolation_samples) / scale, 5) + "\">1"; out += ""; } out += ""; } for (std::uint32_t i = 0; i < 10; ++i) { out += ""; out += "AT_0001" + hex4(0x1001 + i) + "_01"; out += "AP_00011001"; out += ""; } for (std::uint32_t i = 0; i < kObjectCount; ++i) { out += ""; out += "AT_0003" + hex4(0x1001 + i) + "_01"; out += "AP_0003" + hex4(0x1001 + i) + ""; out += ""; } for (std::uint32_t i = 0; i < 10; ++i) { out += ""; out += "AS_0001" + hex4(0x1001 + i) + ""; out += ""; } for (std::uint32_t i = 0; i < kObjectCount; ++i) { out += ""; out += "AS_0003" + hex4(0x1001 + i) + ""; out += ""; } for (std::uint32_t i = 0; i < 10; ++i) { out += ""; out += "AC_0001" + hex4(0x1001 + i) + ""; out += "AP_00011001"; out += "AT_0001" + hex4(0x1001 + i) + "_01"; out += ""; } for (std::uint32_t i = 0; i < kObjectCount; ++i) { out += ""; out += "AC_0003" + hex4(0x1001 + i) + ""; out += "AP_0003" + hex4(0x1001 + i) + ""; out += "AT_0003" + hex4(0x1001 + i) + "_01"; out += ""; } out += ""; out += ""; return out; } } // namespace joc::adm