Initial pure C++ implementation
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
// JOC Huffman trees required by the bitstream parser.
|
||||
|
||||
static const int joc_huff_code_coarse_generic[][2] =
|
||||
{
|
||||
{ -1, 1}, { 2, -2}, { -96, 3}, { 4, -3}, { -95, 5}, { 6, 7}, { -4, -94}, { 8, 9}, { -5, -93}, { 10, 11},
|
||||
{ -6, -92}, { 12, 13}, { -7, -91}, { 14, 15}, { 16, -90}, { -8, 17}, { 18, -89}, { -9, 19}, { 20, 21}, { -88, -10},
|
||||
{ 22, 23}, { -11, -87}, { 24, 25}, { 26, -86}, { -12, 27}, { 28, -85}, { -13, 29}, { 30, 31}, { 32, -84}, { -14, 33},
|
||||
{ 34, -15}, { -83, 35}, { 36, 37}, { -16, 38}, { -17, -82}, { 39, 40}, { 41, -81}, { 42, 43}, { 44, 45}, { 46, 47},
|
||||
{ 48, 49}, { 50, 51}, { 52, -18}, { -78, 53}, { -19, 54}, { 55, 56}, { 57, 58}, { -22, 59}, { 60, 61}, { 62, 63},
|
||||
{ 64, 65}, { 66, 67}, { 68, -20}, { -21, -79}, { -80, -25}, { 69, 70}, { -26, 71}, { 72, 73}, { 74, 75}, { 76, 77},
|
||||
{ 78, 79}, { 80, 81}, { 82, 83}, { 84, 85}, { 86, 87}, { 88, 89}, { 90, 91}, { 92, 93}, { 94, -23}, { -74, -75},
|
||||
{ -72, -73}, { -76, -77}, { -34, -35}, { -32, -33}, { -38, -39}, { -36, -37}, { -30, -31}, { -28, -29}, { -50, -51}, { -48, -49},
|
||||
{ -54, -55}, { -52, -53}, { -42, -43}, { -40, -41}, { -46, -47}, { -44, -45}, { -66, -67}, { -64, -65}, { -70, -71}, { -68, -69},
|
||||
{ -58, -59}, { -56, -57}, { -62, -63}, { -60, -61}, { -24, -27}
|
||||
};
|
||||
|
||||
static const int joc_huff_code_fine_generic[][2] =
|
||||
{
|
||||
{ -1, 1}, { 2, 3}, { -2,-192}, { 4, 5}, { 6, -3}, {-191, 7}, { 8, 9}, { -4,-190}, { 10, 11}, { -5,-189},
|
||||
{ 12, 13}, { -6, 14}, {-188, 15}, { 16, -7}, {-187, 17}, { 18, -8}, {-186, 19}, { 20, -9}, {-185, 21}, { 22, -10},
|
||||
{-184, 23}, { 24, -11}, { 25,-183}, { 26, 27}, { -12,-182}, { 28, 29}, { -13,-181}, { 30, 31}, {-180, -14}, { 32, 33},
|
||||
{ 34,-179}, { -15, 35}, { 36,-178}, { -16, 37}, { 38,-177}, { 39, -17}, { 40, 41}, {-176, 42}, { -18, 43}, { -19, 44},
|
||||
{-175, 45}, { 46,-174}, { -20, 47}, {-173, 48}, { 49, -21}, { 50, 51}, { 52, -22}, { 53, 54}, {-172, 55}, {-171, -23},
|
||||
{ 56, 57}, { 58,-170}, { 59, -24}, { -25, 60}, {-169, 61}, { 62, 63}, { 64, 65}, { 66, 67}, {-168, 68}, { -26, 69},
|
||||
{-167, -27}, { 70,-166}, {-165, 71}, { -29, 72}, { 73, 74}, { -30, 75}, { 76, 77}, { 78, 79}, { 80, -28}, { 81, 82},
|
||||
{ 83,-163}, { -31, -33}, {-164,-161}, { 84, 85}, { 86, 87}, { 88, 89}, { 90, 91}, { 92, 93}, { 94, 95}, { 96, 97},
|
||||
{ 98, 99}, { -32,-162}, { 100, 101}, { 102, 103}, { 104, 105}, { 106, 107}, { 108, 109}, { 110, 111}, {-160, 112}, { -36, -38},
|
||||
{ 113, 114}, { 115, 116}, { 117, 118}, { 119, 120}, { 121, 122}, { 123, 124}, { 125, 126}, { 127, 128}, { 129, 130}, { 131, 132},
|
||||
{ 133, -35}, {-158, 134}, {-155,-156}, { -37, -42}, { 135, 136}, { 137, 138}, { 139, 140}, { 141, 142}, { 143, 144}, { 145, 146},
|
||||
{ 147, 148}, { 149, 150}, { 151, 152}, { 153, 154}, { 155, 156}, { 157, 158}, { 159, 160}, { 161, 162}, { 163, 164}, { 165, 166},
|
||||
{ 167, 168}, { 169, 170}, { 171, 172}, { 173, 174}, { 175, 176}, { 177, 178}, { 179, 180}, { 181, 182}, { 183, 184}, {-157, 185},
|
||||
{ -45, -48}, { 186, 187}, { 188, 189}, { -34, -41}, { 190, -39}, { -60, -61}, { -58, -59}, { -64, -65}, { -62, -63}, { -52, -53},
|
||||
{ -50, -51}, { -56, -57}, { -54, -55}, { -76, -77}, { -74, -75}, { -80, -81}, { -78, -79}, { -68, -69}, { -66, -67}, { -72, -73},
|
||||
{ -70, -71}, { -47, -49}, { -44, -46}, {-124,-125}, {-122,-123}, {-128,-129}, {-126,-127}, {-116,-117}, {-114,-115}, {-120,-121},
|
||||
{-118,-119}, {-140,-141}, {-138,-139}, {-144,-145}, {-142,-143}, {-132,-133}, {-130,-131}, {-136,-137}, {-134,-135}, { -92, -93},
|
||||
{ -90, -91}, { -96, -97}, { -94, -95}, { -84, -85}, { -82, -83}, { -88, -89}, { -86, -87}, {-108,-109}, {-106,-107}, {-112,-113},
|
||||
{-110,-111}, {-100,-101}, { -98, -99}, {-104,-105}, {-102,-103}, {-154,-159}, {-148,-149}, {-146,-147}, {-152,-153}, {-150,-151},
|
||||
{ -40, -43}
|
||||
};
|
||||
|
||||
static const int joc_huff_code_coarse_coeff_sparse[][2] =
|
||||
{
|
||||
{ -1, 1}, { 2, 3}, { -2, -96}, { 4, 5}, { 6, -95}, { -3, 7}, { 8, 9}, { -4, 10}, { -94, 11}, { 12, -5},
|
||||
{ -93, 13}, { 14, 15}, { -6, -92}, { 16, 17}, { 18, -7}, { -91, 19}, { 20, -8}, { -90, 21}, { 22, 23}, { -9, -89},
|
||||
{ 24, 25}, { 26, -10}, { -88, 27}, { 28, 29}, { 30, -11}, { -87, 31}, { 32, 33}, { 34, 35}, { -12, -86}, { 36, 37},
|
||||
{ 38, -13}, { 39, -85}, { 40, 41}, { 42, 43}, { -14, -84}, { 44, 45}, { 46, 47}, { -83, -15}, { 48, 49}, { 50, -16},
|
||||
{ 51, 52}, { -82, 53}, { 54, -81}, { 55, 56}, { -17, 57}, { 58, -80}, { 59, 60}, { -18, 61}, { 62, 63}, { -79, 64},
|
||||
{ -19, -78}, { 65, 66}, { 67, 68}, { 69, -20}, { -77, -21}, { 70, 71}, { 72, 73}, { 74, -76}, { 75, -22}, { 76, 77},
|
||||
{ -75, 78}, { 79, 80}, { -54, -74}, { -73, 81}, { -23, 82}, { -50, -24}, { -55, -25}, { 83, -47}, { -49, -44}, { -71, 84},
|
||||
{ -48, -51}, { 85, -72}, { -26, -53}, { -70, -27}, { 86, -45}, { 87, 88}, { -68, 89}, { -29, -43}, { 90, -30}, { -46, -69},
|
||||
{ 91, -28}, { -52, -31}, { 92, -32}, { 93, -64}, { -67, 94}, { -36, -33}, { -63, -37}, { -65, -61}, { -66, -59}, { -34, -38},
|
||||
{ -41, -42}, { -35, -60}, { -39, -57}, { -56, -40}, { -62, -58}
|
||||
};
|
||||
|
||||
static const int joc_huff_code_fine_coeff_sparse[][2] =
|
||||
{
|
||||
{ 1, -1}, { 2, 3}, { 4, -2}, {-192, 5}, { 6, 7}, { 8, -3}, {-191, 9}, { 10, 11}, { 12,-190}, { -4, 13},
|
||||
{ 14, 15}, {-189, -5}, { 16, 17}, { 18, -6}, {-188, 19}, { 20, 21}, { -7,-187}, { 22, 23}, { -8, 24}, {-186, 25},
|
||||
{ -9, 26}, { 27,-185}, { 28, -10}, { 29, 30}, {-184, 31}, { -11, 32}, { 33,-183}, { 34, -12}, { 35,-182}, { 36, 37},
|
||||
{ 38, -13}, {-181, 39}, { 40, -14}, { 41,-180}, { 42, 43}, {-179, -15}, { 44, -16}, { 45,-178}, { 46, 47}, { 48, 49},
|
||||
{ 50,-177}, { -17, 51}, { -18, 52}, {-176, 53}, { 54, 55}, {-175, -19}, { 56, 57}, { 58, -20}, { 59,-174}, { 60, 61},
|
||||
{ -21, 62}, { 63,-173}, { 64, 65}, { 66,-172}, { 67, 68}, { -22, 69}, { 70, 71}, { -23, 72}, {-171, 73}, { 74, 75},
|
||||
{ 76, -24}, { 77,-170}, { -25, 78}, { 79, 80}, { 81,-169}, { 82, 83}, { 84, -26}, { 85,-168}, { 86, 87}, { 88, 89},
|
||||
{-167, 90}, { -27, 91}, { 92, -28}, { 93,-166}, { 94, -29}, { 95, 96}, { 97, 98}, {-165, 99}, { 100, -30}, {-164, 101},
|
||||
{ 102, 103}, { 104, 105}, {-163, 106}, { -31, 107}, { -32, 108}, { 109, 110}, {-161, 111}, {-160,-162}, { 112, -34}, { -33, 113},
|
||||
{ 114, 115}, { 116, 117}, { 118, 119}, { 120,-159}, { 121, 122}, { 123,-158}, { 124, 125}, { -36,-155}, { 126, 127}, { -35, 128},
|
||||
{ 129, 130}, {-157, 131}, {-156, 132}, { -37, 133}, { 134, 135}, {-154, -38}, { 136, 137}, { -39, -41}, { 138,-153}, { 139, -40},
|
||||
{-149, 140}, { 141, 142}, { 143, 144}, {-151, 145}, { 146, 147}, { 148, -42}, { -43, 149}, { 150, 151}, {-152, 152}, { -46, -98},
|
||||
{ 153, 154}, { 155,-147}, { 156, 157}, { 158,-107}, { 159, 160}, {-145,-150}, { -96, 161}, { 162, -45}, {-146, 163}, { 164, -97},
|
||||
{-108,-105}, {-148,-106}, { -44, 165}, { -94,-141}, { -99, 166}, { -89, 167}, { -50, -95}, {-100, -48}, {-144, 168}, { 169, 170},
|
||||
{ -51,-142}, { -90, -91}, { -47, -49}, { 171, -53}, { -93,-143}, {-137,-138}, { -55,-101}, { 172, 173}, { -54, -86}, { -88, -87},
|
||||
{-103, 174}, { 175, -61}, {-109, 176}, { 177, 178}, { -52,-139}, { -57,-140}, { 179, 180}, { -56,-136}, { -58,-102}, { 181, 182},
|
||||
{ -60,-135}, { 183,-104}, {-128,-134}, { -92, 184}, { -59, -62}, { 185, 186}, { -71,-133}, { 187,-127}, {-126, 188}, { -63, -64},
|
||||
{ -85,-132}, { 189, -66}, {-121,-125}, { 190, -68}, { -74, -75}, { -70, -73}, { -81, -65}, {-118,-131}, { -72,-110}, {-119,-120},
|
||||
{ -76, -84}, {-122,-130}, { -83,-117}, { -69, -78}, { -80, -82}, {-123,-124}, { -67,-116}, {-129, -77}, {-113,-114}, {-112,-115},
|
||||
{ -79,-111}
|
||||
};
|
||||
|
||||
static const int joc_huff_code_5ch_pos_index_sparse[][2] =
|
||||
{
|
||||
{ -1, 1}, { 2, 3}, { -4, -3}, { -2, -5}
|
||||
};
|
||||
|
||||
static const int joc_huff_code_7ch_pos_index_sparse[][2] =
|
||||
{
|
||||
{ -1, 1}, { 2, 3}, { 4, 5}, { -4, -3}, { -2, -5}, { -6, -7}
|
||||
};
|
||||
@@ -0,0 +1,415 @@
|
||||
#include "joc_bitstream/joc_parser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "foundation/bit_reader.h"
|
||||
#include "joc_huffman_tables.h"
|
||||
|
||||
namespace joc::joc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct NumChannelsEntry {
|
||||
std::uint32_t config;
|
||||
std::int16_t channels;
|
||||
};
|
||||
constexpr NumChannelsEntry kNumChannels[] = {
|
||||
{0u, 5}, {1u, 7}, {2u, 7}, {3u, 5}, {4u, 7},
|
||||
};
|
||||
|
||||
struct NumBandsEntry {
|
||||
std::uint32_t index;
|
||||
std::int16_t bands;
|
||||
};
|
||||
constexpr NumBandsEntry kNumBands[] = {
|
||||
{0u, 1}, {1u, 3}, {2u, 5}, {3u, 7}, {4u, 9}, {5u, 12}, {6u, 15}, {7u, 23},
|
||||
};
|
||||
|
||||
// Floored modulo: Python's % operator semantics, so that the ported
|
||||
inline std::int64_t floored_mod(std::int64_t value, std::int64_t modulus) {
|
||||
const std::int64_t remainder = value % modulus;
|
||||
return remainder < 0 ? remainder + modulus : remainder;
|
||||
}
|
||||
|
||||
enum class SymbolKind { Mtx, Idx, Vec };
|
||||
|
||||
struct Tree {
|
||||
const int (*nodes)[2] = nullptr;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
Tree select_tree(std::uint32_t quant_idx, SymbolKind kind, int n_channels) {
|
||||
Tree tree;
|
||||
switch (kind) {
|
||||
case SymbolKind::Idx:
|
||||
if (n_channels == 5) {
|
||||
tree.nodes = joc_huff_code_5ch_pos_index_sparse;
|
||||
tree.count = static_cast<int>(sizeof(joc_huff_code_5ch_pos_index_sparse) /
|
||||
sizeof(joc_huff_code_5ch_pos_index_sparse[0]));
|
||||
} else {
|
||||
tree.nodes = joc_huff_code_7ch_pos_index_sparse;
|
||||
tree.count = static_cast<int>(sizeof(joc_huff_code_7ch_pos_index_sparse) /
|
||||
sizeof(joc_huff_code_7ch_pos_index_sparse[0]));
|
||||
}
|
||||
break;
|
||||
case SymbolKind::Vec:
|
||||
if (quant_idx == 0u) {
|
||||
tree.nodes = joc_huff_code_coarse_coeff_sparse;
|
||||
tree.count = static_cast<int>(sizeof(joc_huff_code_coarse_coeff_sparse) /
|
||||
sizeof(joc_huff_code_coarse_coeff_sparse[0]));
|
||||
} else {
|
||||
tree.nodes = joc_huff_code_fine_coeff_sparse;
|
||||
tree.count = static_cast<int>(sizeof(joc_huff_code_fine_coeff_sparse) /
|
||||
sizeof(joc_huff_code_fine_coeff_sparse[0]));
|
||||
}
|
||||
break;
|
||||
case SymbolKind::Mtx:
|
||||
default:
|
||||
if (quant_idx == 0u) {
|
||||
tree.nodes = joc_huff_code_coarse_generic;
|
||||
tree.count = static_cast<int>(sizeof(joc_huff_code_coarse_generic) /
|
||||
sizeof(joc_huff_code_coarse_generic[0]));
|
||||
} else {
|
||||
tree.nodes = joc_huff_code_fine_generic;
|
||||
tree.count = static_cast<int>(sizeof(joc_huff_code_fine_generic) /
|
||||
sizeof(joc_huff_code_fine_generic[0]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
// infinite loop (plan 40.4 BL-6).
|
||||
bool huff_decode(const Tree& tree, bits::BitReader& reader, std::int16_t* out_value) {
|
||||
int node = 0;
|
||||
int steps = 0;
|
||||
while (node >= 0) {
|
||||
if (node >= tree.count || steps > tree.count) {
|
||||
reader.fail(JOC_ERR_JOC_SYNTAX, "Huffman tree walk left the valid node range");
|
||||
return false;
|
||||
}
|
||||
++steps;
|
||||
const std::uint32_t bit = reader.read(1);
|
||||
if (reader.failed()) {
|
||||
return false;
|
||||
}
|
||||
node = tree.nodes[node][bit];
|
||||
}
|
||||
*out_value = static_cast<std::int16_t>(-node - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
Status syntax_fail(const std::string& message) {
|
||||
return Status::fail(JOC_ERR_JOC_SYNTAX, stage::kJoc, message);
|
||||
}
|
||||
|
||||
Status truncated_fail(const bits::BitReader& reader) {
|
||||
if (reader.error() == JOC_ERR_JOC_SYNTAX) {
|
||||
return syntax_fail(reader.error_message());
|
||||
}
|
||||
return Status::fail(JOC_ERR_BITSTREAM_TRUNCATED, stage::kJoc,
|
||||
std::string("JOC bitstream truncated: ") + reader.error_message());
|
||||
}
|
||||
|
||||
void reconstruct_dense(const ObjectSymbols& symbols, std::uint32_t dp, int n_channels, std::int64_t nquant,
|
||||
std::int64_t offset,
|
||||
std::int64_t q[JOC_MAX_DPOINTS][JOC_MAX_CORE_CHANNELS][JOC_MAX_PARAMETER_BANDS]) {
|
||||
for (int ch = 0; ch < n_channels; ++ch) {
|
||||
q[dp][ch][0] =
|
||||
floored_mod(offset + static_cast<std::int64_t>(symbols.mtx[dp][ch][0]), nquant);
|
||||
for (int pb = 1; pb < symbols.n_bands; ++pb) {
|
||||
q[dp][ch][pb] = floored_mod(
|
||||
q[dp][ch][pb - 1] + static_cast<std::int64_t>(symbols.mtx[dp][ch][pb]), nquant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// across parameter bands and is deliberately NOT reset when the active channel
|
||||
Status reconstruct_sparse(
|
||||
const ObjectSymbols& symbols, std::uint32_t dp, int n_channels, std::int64_t nquant, std::int64_t offset,
|
||||
std::int64_t q[JOC_MAX_DPOINTS][JOC_MAX_CORE_CHANNELS][JOC_MAX_PARAMETER_BANDS]) {
|
||||
if (n_channels != 5 && n_channels != 7) {
|
||||
return Status::fail(JOC_ERR_JOC_UNSUPPORTED_VARIANT, stage::kJoc,
|
||||
"sparse JOC requires 5 or 7 core channels, got " +
|
||||
std::to_string(n_channels));
|
||||
}
|
||||
const int initial_channel = symbols.idx[dp][0];
|
||||
if (initial_channel < 0 || initial_channel >= n_channels) {
|
||||
return syntax_fail("sparse JOC initial channel " + std::to_string(initial_channel) +
|
||||
" out of range for " + std::to_string(n_channels) + " channels");
|
||||
}
|
||||
// Non-active entries take nquant/2, which dequantizes to exactly zero.
|
||||
for (int ch = 0; ch < n_channels; ++ch) {
|
||||
for (int pb = 0; pb < symbols.n_bands; ++pb) {
|
||||
q[dp][ch][pb] = nquant / 2;
|
||||
}
|
||||
}
|
||||
int active = initial_channel;
|
||||
std::int64_t coefficient = offset;
|
||||
for (int pb = 0; pb < symbols.n_bands; ++pb) {
|
||||
if (pb != 0) {
|
||||
active = static_cast<int>(
|
||||
floored_mod(static_cast<std::int64_t>(active) + symbols.idx[dp][pb], n_channels));
|
||||
}
|
||||
coefficient = floored_mod(coefficient + static_cast<std::int64_t>(symbols.vec[dp][pb]), nquant);
|
||||
q[dp][active][pb] = coefficient;
|
||||
}
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::int16_t num_channels_for_config(std::uint32_t dmx_config_idx) {
|
||||
for (const NumChannelsEntry& entry : kNumChannels) {
|
||||
if (entry.config == dmx_config_idx) {
|
||||
return entry.channels;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::int16_t num_bands_for_index(std::uint32_t num_bands_idx) {
|
||||
for (const NumBandsEntry& entry : kNumBands) {
|
||||
if (entry.index == num_bands_idx) {
|
||||
return entry.bands;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Status parse_id14(const std::uint8_t* payload, std::size_t payload_size, joc_frame_params* out,
|
||||
FrameSymbols* symbols) {
|
||||
if (payload == nullptr || out == nullptr) {
|
||||
return Status::fail(JOC_ERR_INVALID_ARGUMENT, stage::kJoc, "null payload or output");
|
||||
}
|
||||
std::memset(out, 0, sizeof(*out));
|
||||
out->struct_size = sizeof(joc_frame_params);
|
||||
out->struct_version = JOC_FRAME_PARAMS_VERSION;
|
||||
// capture is purely additive and never changes the parse result.
|
||||
FrameSymbols local_symbols{};
|
||||
FrameSymbols& capture = (symbols != nullptr) ? *symbols : local_symbols;
|
||||
std::memset(&capture, 0, sizeof(capture));
|
||||
|
||||
bits::BitReader reader(payload, payload_size);
|
||||
|
||||
out->dmx_config_idx = static_cast<std::uint8_t>(reader.read(3));
|
||||
out->num_objects_bits = static_cast<std::uint8_t>(reader.read(6));
|
||||
out->ext_config_idx = static_cast<std::uint8_t>(reader.read(3));
|
||||
|
||||
const std::uint32_t n_objects = static_cast<std::uint32_t>(out->num_objects_bits) + 1u;
|
||||
const std::int16_t n_channels = num_channels_for_config(out->dmx_config_idx);
|
||||
if (n_channels < 0) {
|
||||
return syntax_fail("unknown JOC downmix configuration " +
|
||||
std::to_string(out->dmx_config_idx));
|
||||
}
|
||||
out->n_channels = static_cast<std::uint8_t>(n_channels);
|
||||
if (n_objects > JOC_MAX_OBJECTS) {
|
||||
// The reference implementation has no check here and fails later inside
|
||||
// NumPy; the port reports it explicitly (plan 28.2).
|
||||
return Status::fail(JOC_ERR_JOC_UNSUPPORTED_VARIANT, stage::kJoc,
|
||||
"JOC frame declares " + std::to_string(n_objects) +
|
||||
" objects, the ABI supports at most " +
|
||||
std::to_string(static_cast<int>(JOC_MAX_OBJECTS)));
|
||||
}
|
||||
out->n_objects = static_cast<std::uint8_t>(n_objects);
|
||||
|
||||
out->clipgain_x_bits = static_cast<std::uint8_t>(reader.read(3));
|
||||
out->clipgain_y_bits = static_cast<std::uint8_t>(reader.read(5));
|
||||
out->seq_count = reader.read(10);
|
||||
// clipgain = 1 + (y/32) * 2^(x-4). The reference multiplies by an exact
|
||||
// exactly for the whole legal range.
|
||||
out->clipgain = 1.0 + static_cast<double>(out->clipgain_y_bits) / 32.0 *
|
||||
std::ldexp(1.0, static_cast<int>(out->clipgain_x_bits) - 4);
|
||||
|
||||
for (std::uint32_t obj = 0; obj < n_objects; ++obj) {
|
||||
joc_object_params& info = out->objects[obj];
|
||||
info.present = static_cast<std::uint8_t>(reader.read(1));
|
||||
if (info.present == 0u) {
|
||||
continue;
|
||||
}
|
||||
info.num_bands_idx = static_cast<std::uint8_t>(reader.read(3));
|
||||
const std::int16_t bands = num_bands_for_index(info.num_bands_idx);
|
||||
if (bands < 0) {
|
||||
return syntax_fail("unknown JOC num_bands index " +
|
||||
std::to_string(info.num_bands_idx));
|
||||
}
|
||||
info.n_bands = static_cast<std::uint8_t>(bands);
|
||||
info.sparse = static_cast<std::uint8_t>(reader.read(1));
|
||||
info.quant_idx = static_cast<std::uint8_t>(reader.read(1));
|
||||
info.slope_idx = static_cast<std::uint8_t>(reader.read(1));
|
||||
info.num_dpoints_bits = static_cast<std::uint8_t>(reader.read(1));
|
||||
info.n_dpoints = static_cast<std::uint8_t>(info.num_dpoints_bits + 1u);
|
||||
if (info.slope_idx == 1u) {
|
||||
for (std::uint32_t dp = 0; dp < info.n_dpoints; ++dp) {
|
||||
info.offset_ts[dp] = static_cast<std::uint8_t>(reader.read(5) + 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reader.failed()) {
|
||||
return truncated_fail(reader);
|
||||
}
|
||||
|
||||
for (std::uint32_t obj = 0; obj < n_objects; ++obj) {
|
||||
const joc_object_params& info = out->objects[obj];
|
||||
if (info.present == 0u) {
|
||||
continue;
|
||||
}
|
||||
ObjectSymbols* symbol = &capture.objects[obj];
|
||||
symbol->present = 1;
|
||||
symbol->sparse = info.sparse;
|
||||
symbol->n_bands = info.n_bands;
|
||||
symbol->n_dpoints = info.n_dpoints;
|
||||
symbol->n_channels = out->n_channels;
|
||||
for (std::uint32_t dp = 0; dp < info.n_dpoints; ++dp) {
|
||||
if (info.sparse == 1u) {
|
||||
const Tree idx_tree = select_tree(info.quant_idx, SymbolKind::Idx, n_channels);
|
||||
const std::uint32_t first = reader.read(3);
|
||||
if (reader.failed()) {
|
||||
return truncated_fail(reader);
|
||||
}
|
||||
symbol->idx[dp][0] = static_cast<std::uint8_t>(first);
|
||||
for (int pb = 1; pb < info.n_bands; ++pb) {
|
||||
std::int16_t value = 0;
|
||||
if (!huff_decode(idx_tree, reader, &value)) {
|
||||
return truncated_fail(reader);
|
||||
}
|
||||
symbol->idx[dp][pb] = static_cast<std::uint8_t>(value);
|
||||
}
|
||||
const Tree vec_tree = select_tree(info.quant_idx, SymbolKind::Vec, n_channels);
|
||||
for (int pb = 0; pb < info.n_bands; ++pb) {
|
||||
std::int16_t value = 0;
|
||||
if (!huff_decode(vec_tree, reader, &value)) {
|
||||
return truncated_fail(reader);
|
||||
}
|
||||
symbol->vec[dp][pb] = value;
|
||||
}
|
||||
} else {
|
||||
const Tree mtx_tree = select_tree(info.quant_idx, SymbolKind::Mtx, n_channels);
|
||||
for (int ch = 0; ch < n_channels; ++ch) {
|
||||
for (int pb = 0; pb < info.n_bands; ++pb) {
|
||||
std::int16_t value = 0;
|
||||
if (!huff_decode(mtx_tree, reader, &value)) {
|
||||
return truncated_fail(reader);
|
||||
}
|
||||
symbol->mtx[dp][ch][pb] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out->data_end_bits = static_cast<std::uint32_t>(reader.position());
|
||||
out->trailing_bits = static_cast<std::uint32_t>(payload_size * 8u - reader.position());
|
||||
const std::size_t tail_offset = reader.position() / 8u;
|
||||
if (tail_offset < payload_size) {
|
||||
const std::size_t tail_bytes = std::min<std::size_t>(8u, payload_size - tail_offset);
|
||||
std::memcpy(out->tail_bytes, payload + tail_offset, tail_bytes);
|
||||
}
|
||||
|
||||
for (std::uint32_t obj = 0; obj < n_objects; ++obj) {
|
||||
const joc_object_params& info = out->objects[obj];
|
||||
if (info.present == 0u) {
|
||||
continue;
|
||||
}
|
||||
std::uint32_t mask_bit = 1u << obj;
|
||||
out->present_mask |= mask_bit;
|
||||
|
||||
const std::int64_t nquant = (info.quant_idx == 0u) ? 96 : 192;
|
||||
std::int64_t q[JOC_MAX_DPOINTS][JOC_MAX_CORE_CHANNELS][JOC_MAX_PARAMETER_BANDS] = {};
|
||||
ObjectSymbols* symbol = &capture.objects[obj];
|
||||
|
||||
for (std::uint32_t dp = 0; dp < info.n_dpoints; ++dp) {
|
||||
if (info.sparse == 1u) {
|
||||
const std::int64_t offset = (info.quant_idx == 0u) ? 50 : 100;
|
||||
const Status status =
|
||||
reconstruct_sparse(*symbol, dp, n_channels, nquant, offset, q);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
const std::int64_t offset = (info.quant_idx == 0u) ? 48 : 96;
|
||||
reconstruct_dense(*symbol, dp, n_channels, nquant, offset, q);
|
||||
}
|
||||
}
|
||||
|
||||
// Operand order and types are kept identical to the reference so the
|
||||
// result is bit-exact, not merely close.
|
||||
const double nquant_half = static_cast<double>(nquant) / 2.0;
|
||||
const double denominator = 4096.0 * static_cast<double>(1 + static_cast<int>(info.quant_idx));
|
||||
for (std::uint32_t dp = 0; dp < info.n_dpoints; ++dp) {
|
||||
for (int ch = 0; ch < n_channels; ++ch) {
|
||||
for (int pb = 0; pb < info.n_bands; ++pb) {
|
||||
const double value = static_cast<double>(q[dp][ch][pb]) - nquant_half;
|
||||
out->objects[obj].dq[dp][ch][pb] = value * 820.0 / denominator;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::uint32_t dp = 0; dp < info.n_dpoints; ++dp) {
|
||||
for (int ch = 0; ch < n_channels; ++ch) {
|
||||
for (int pb = 0; pb < info.n_bands; ++pb) {
|
||||
symbol->q[dp][ch][pb] = q[dp][ch][pb];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
capture.n_objects = out->n_objects;
|
||||
capture.n_channels = out->n_channels;
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
Status parse_eac3_frame(const std::uint8_t* frame, std::size_t frame_size, joc_frame_params* out,
|
||||
emdf::Container* container, FrameSymbols* symbols) {
|
||||
if (frame == nullptr || out == nullptr) {
|
||||
return Status::fail(JOC_ERR_INVALID_ARGUMENT, stage::kJoc, "null frame or output");
|
||||
}
|
||||
emdf::Container local;
|
||||
const Status status = emdf::find_joc_emdf(frame, frame_size, &local);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (container != nullptr) {
|
||||
*container = local;
|
||||
}
|
||||
const emdf::Payload* payload = local.find(emdf::kIdJoc);
|
||||
if (payload == nullptr) {
|
||||
return Status::fail(JOC_ERR_EMDF_TRANSPORT, stage::kEmdf,
|
||||
"EMDF container has no ID14 (JOC) payload");
|
||||
}
|
||||
std::vector<std::uint8_t> bytes;
|
||||
const Status extract = emdf::extract_payload_bytes(frame, frame_size, *payload, &bytes);
|
||||
if (!extract.ok()) {
|
||||
return extract;
|
||||
}
|
||||
return parse_id14(bytes.data(), bytes.size(), out, symbols);
|
||||
}
|
||||
|
||||
Status check_id14_padding(const std::uint8_t* payload, std::size_t payload_size,
|
||||
std::uint32_t* out_trailing_bits) {
|
||||
joc_frame_params params;
|
||||
FrameSymbols symbols;
|
||||
const Status status = parse_id14(payload, payload_size, ¶ms, &symbols);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
if (out_trailing_bits != nullptr) {
|
||||
*out_trailing_bits = params.trailing_bits;
|
||||
}
|
||||
if (params.trailing_bits > 7u) {
|
||||
return Status::fail(JOC_ERR_BITSTREAM_PADDING, stage::kJoc,
|
||||
"more than 7 bits left after joc_data (" +
|
||||
std::to_string(params.trailing_bits) + ")");
|
||||
}
|
||||
for (std::size_t bit = params.data_end_bits; bit < payload_size * 8u; ++bit) {
|
||||
if (((payload[bit >> 3] >> (7u - (bit & 7u))) & 1u) != 0u) {
|
||||
return Status::fail(JOC_ERR_BITSTREAM_PADDING, stage::kJoc,
|
||||
"non-zero trailing padding bit at " + std::to_string(bit));
|
||||
}
|
||||
}
|
||||
return Status::success();
|
||||
}
|
||||
|
||||
} // namespace joc::joc
|
||||
@@ -0,0 +1,46 @@
|
||||
// Port of src/joc_decode.py.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "joc_core.h"
|
||||
#include "emdf/emdf_parser.h"
|
||||
#include "foundation/status.h"
|
||||
|
||||
namespace joc::joc {
|
||||
|
||||
struct ObjectSymbols {
|
||||
std::uint8_t present = 0;
|
||||
std::uint8_t sparse = 0;
|
||||
std::uint8_t n_bands = 0;
|
||||
std::uint8_t n_dpoints = 0;
|
||||
std::uint8_t n_channels = 0;
|
||||
std::int64_t q[JOC_MAX_DPOINTS][JOC_MAX_CORE_CHANNELS][JOC_MAX_PARAMETER_BANDS] = {};
|
||||
std::int16_t mtx[JOC_MAX_DPOINTS][JOC_MAX_CORE_CHANNELS][JOC_MAX_PARAMETER_BANDS] = {};
|
||||
std::uint8_t idx[JOC_MAX_DPOINTS][JOC_MAX_PARAMETER_BANDS] = {};
|
||||
std::int16_t vec[JOC_MAX_DPOINTS][JOC_MAX_PARAMETER_BANDS] = {};
|
||||
};
|
||||
|
||||
struct FrameSymbols {
|
||||
std::uint8_t n_objects = 0;
|
||||
std::uint8_t n_channels = 0;
|
||||
ObjectSymbols objects[JOC_MAX_OBJECTS];
|
||||
};
|
||||
|
||||
Status parse_id14(const std::uint8_t* payload, std::size_t payload_size, joc_frame_params* out,
|
||||
FrameSymbols* symbols);
|
||||
|
||||
Status parse_eac3_frame(const std::uint8_t* frame, std::size_t frame_size, joc_frame_params* out,
|
||||
emdf::Container* container, FrameSymbols* symbols);
|
||||
|
||||
Status check_id14_padding(const std::uint8_t* payload, std::size_t payload_size,
|
||||
std::uint32_t* out_trailing_bits);
|
||||
|
||||
std::int16_t num_channels_for_config(std::uint32_t dmx_config_idx);
|
||||
|
||||
std::int16_t num_bands_for_index(std::uint32_t num_bands_idx);
|
||||
|
||||
} // namespace joc::joc
|
||||
Reference in New Issue
Block a user