Files
UnrealEngine/Engine/Source/Runtime/RadAudioCodec/SDK/Src/RadAudio/radaudio_common.inl
2025-05-18 13:04:45 +08:00

596 lines
22 KiB
C++

// Copyright Epic Games Tools, LLC. All Rights Reserved.
#include "radaudio_common.h"
#include "rrCore.h"
#include <string.h>
#include <stdio.h>
static int sample_rates[4] =
{
48000,
44100,
32000,
24000,
};
#include "radaudio_data_tables.inl"
#include "radaudio_huffman_tables.inl"
static radaudio_huffman *rada_nonzero_bitflags_huff[6] =
{
&rada_nonzero_bitflags_huff0,
&rada_nonzero_bitflags_huff1,
&rada_nonzero_bitflags_huff2,
&rada_nonzero_bitflags_huff3,
&rada_nonzero_bitflags_huff4,
&rada_nonzero_bitflags_huff5,
};
static radaudio_huffman *rada_nonzero_coefficient_pair_huff[4] =
{
&rada_nonzero_coefficient_pair_huff0,
&rada_nonzero_coefficient_pair_huff1,
&rada_nonzero_coefficient_pair_huff2,
&rada_nonzero_coefficient_pair_huff3,
};
static radaudio_huffman *rada_nonzero_coefficient_big_huff[4] =
{
&rada_nonzero_coefficient_big_huff0,
&rada_nonzero_coefficient_big_huff1,
&rada_nonzero_coefficient_big_huff2,
&rada_nonzero_coefficient_big_huff3,
};
// each block picks between 4 modes using two bits in the block header.
//
// the meaning of the 4 modes is specified in the stream header, which is currently 128 bytes.
// (a 1 second stereo sample at 100 kbps uses 12500 bytes, so stream header is 1% in this extreme case; not worth worrying about)
//
// for each of the four modes, the header specifies:
// - which of 4 huffman tables to compress pairs of coefficients packed as nibbles (-7..7, plus overflow code)
// - which of 4 huffman tables to compress coefficients whose magnitude exceeds 7
// - how to encode the nonzero/zero information about the coefficients:
// - when to transition between bit-encoding into 8 zero/non-zero flags per byte and using run length compression
// - for each group of 8 bytes, which of 6 huffman tables to use to compress those 8 bytes
// - whether to xor each byte with 255 before encoding
// - in theory this effectively doubles the available huffman tables
// - it was very effective in the original design that had one global huffman table
// - it's used almost never in the current scheme with 6 huffman tables
// slots in huffman selector array
enum
{
HS_COEFF_PAIR=0, // nibble pairs
HS_COEFF_BIG=1 // values whose magnitude exceeeds 7
};
// compute derived data about the bitflag byte packing to make decoding not require
// any branching. all 8-byte blocks that use the same huffman table will be packed
// together, and a table is built for how to reorder those to original order.
static void compute_packorder(radaudio_nonzero_blockmode_descriptor *nz_desc)
{
for (int i=0; i < nz_desc->num_8byte_chunks; ++i)
++nz_desc->num_chunks_per_huff[nz_desc->huffman_table_for_chunk[i]];
// current output position for each huff; 0 is a placeholder for numbering convention
U8 pos[NUM_NZ_HUFF] = { 0 };
// mono
pos[0] = 0;
for (int i=1; i < NUM_NZ_HUFF; ++i) {
// starting position for each huff output is after all previous output, i.e. previous start + previous len
pos[i] = pos[i-1] + nz_desc->num_chunks_per_huff[i-1];
}
for (int i=0; i < nz_desc->num_8byte_chunks; ++i) {
int ht = nz_desc->huffman_table_for_chunk[i];
nz_desc->source_pos[0][i] = pos[ht];
++pos[ht];
}
// stereo
pos[0] = 0;
for (int i=1; i < NUM_NZ_HUFF; ++i) {
// starting position for each huff output is after all previous output, i.e. previous start + previous len
pos[i] = pos[i-1] + 2*nz_desc->num_chunks_per_huff[i-1];
}
for (int c=0; c < MAX_RADAUD_CHANNELS; ++c) {
for (int i=0; i < nz_desc->num_8byte_chunks; ++i) {
int ht = nz_desc->huffman_table_for_chunk[i];
nz_desc->source_pos[1+c][i] = pos[ht];
++pos[ht];
}
}
}
static void radaudio_init_nz_desc(radaudio_nonzero_blockmode_descriptor nz_desc[4])
{
for (int i=0; i < 3; ++i)
compute_packorder(&nz_desc[i]);
// slot 3 is always all run-length
}
// mdct windows
static float *radaudio_windows [2] = { window_long , window_short };
static radaudio_cpu_features cpu_detect(void)
{
radaudio_cpu_features f = { 0 };
#ifdef __RADX86__
// this function caches results so won't be an issue to call multiple times.
rrCPUx86_detect();
f.has_sse2 = rrCPUx86_feature_present(RRX86_CPU_SSE2);
f.has_ssse3 = rrCPUx86_feature_present(RRX86_CPU_SSSE3);
f.has_sse4_1 = rrCPUx86_feature_present(RRX86_CPU_SSE41);
f.has_popcnt = rrCPUx86_feature_present(RRX86_CPU_POPCNT);
f.has_avx2 = rrCPUx86_feature_present(RRX86_CPU_AVX2);
#endif
return f;
}
// values to scale band energy by to "normalize" for sample rate
static int rate_scale[4] = {
235, // 48,000
256, // 44,100
353, // 32,000
470, // 24,000
};
// this function must compute same data in encoder and decoder so they stay in sync,
// so it's integer/fixed-point only
static void compute_mantissa_bitcount(int rate_mode,
int is_short_block,
S8 mantissa_param[][MAX_BANDS][2],
int *exponents,
U8 *mantissa_bitcount)
{
radaudio_rate_info *info = &radaudio_rateinfo[is_short_block][rate_mode];
int j;
int limit = 12 * rate_scale[rate_mode];
if (limit > 16*256) limit = 16*256; // never useful to have more than 16 bits of mantissa
for (j=0; j < info->num_bands; ++j) {
int be_log2 = exponents[j];
int mantissa_bits;
int base_bits = mantissa_param[is_short_block][j][0]<<5;
int bits_for_energy = be_log2 * mantissa_param[is_short_block][j][1];
if (be_log2 == BAND_EXPONENT_NONE)
mantissa_bits = 0;
else {
mantissa_bits = base_bits + bits_for_energy;
mantissa_bits = (mantissa_bits * rate_scale[rate_mode]) >> 8;
}
if (mantissa_bits < 0) mantissa_bits = 0;
if (mantissa_bits > limit) mantissa_bits = limit;
mantissa_bitcount[j] = (U8) (mantissa_bits >> 8);
}
}
static int radaudio_code_sample_rate(int rate)
{
int i;
if (rate > 0)
for (i=0; i < 4; ++i)
if (sample_rates[i] == rate)
return i;
return -1;
}
static void compute_bias_set(radaudio_block_header_biases *bc, U16 bias)
{
bc->bytes_bias[0][0] = bc->bytes_bias[0][1] = bc->bytes_bias[0][2] = 0;
bc->bytes_bias[1][0] = bc->bytes_bias[1][1] = bc->bytes_bias[1][2] = 0;
// supplied value is stereo long block
bc->bytes_bias[0][2] = bias;
// mono long block uses half the bias
bc->bytes_bias[0][1] = (bias >> 1);
}
// flags at start of block packet
// 0 overhead to use:
#define RADAUDIO_BLOCKFLAG_8BIT_PARITY (1u << 0)
#define RADAUDIO_BLOCKFLAG_THIS_SHORT (1u << 1)
#define RADAUDIO_BLOCKFLAG_NEXT_SHORT (1u << 2)
#define RADAUDIO_BLOCKFLAG_STEREO (1u << 3)
#define RADAUDIO_BLOCKFLAG_MIDSIDE_BANDS (1u << 4)
#define RADAUDIO_BLOCKFLAG_PREDICT_SUBBAND_STEREO (1u << 5)
#define RADAUDIO_BLOCKFLAG_PREDICT_EXPONENT_STEREO (1u << 6)
#define RADAUDIO_BLOCKFLAG_16BIT_FLAGS (1u << 7)
// if any of these are set, it costs one more byte:
#define RADAUDIO_BLOCKFLAG_NONZERO_BITARRAY_MASK (3u << 8)
#define RADAUDIO_BLOCKFLAG_DISABLE_SUBBAND_PREDICT (1u << 10)
#define RADAUDIO_BLOCKFLAG_MIDSIDE_ENCODED (1u << 11)
#define RADAUDIO_BLOCKFLAG_EXCESS_RUNLENGTH_ARRAY (1u << 12)
#define RADAUDIO_BLOCKFLAG_EXCESS_VBSTREAM0_LENGTH (1u << 13)
#define RADAUDIO_BLOCKFLAG_EXCESS_BLOCK_BYTES (1u << 14)
#define RADAUDIO_BLOCKFLAG_FINAL (1u << 15)
#define RADAUDIO_BLOCKFLAG_NONZERO_BITARRAY_MODE_GET(x) (((x) >> 8) & 3u)
#define RADAUDIO_BLOCKFLAG_NONZERO_BITARRAY_MODE_SET(x) ((x) << 8)
// use invalid flag combination as first byte of stream header, allows us to detect looping automatically
// mono block, but stereo predict, also incorrect parity
#define RADAUDIO_STREAMHEADER_FLAGS ( \
RADAUDIO_BLOCKFLAG_PREDICT_SUBBAND_STEREO \
| RADAUDIO_BLOCKFLAG_PREDICT_EXPONENT_STEREO \
)
static int countbits8(U32 flags)
{
flags = ((flags>>1) & 0x5555) + (flags & 0x5555); // binary: (0q0s0u0w & 01010101) + (0r0t0v0x & 01010101)
flags = ((flags>>2) & 0x3333) + (flags & 0x3333); // binary: foo & 00110011) + ( bar & 00110011)
return ((flags>>4) & 0x0f0f) + (flags & 0x0f0f); // binary: baz & 00001111) + ( quux & 00001111)
}
static int put_8bits_or_16bits(U8 buffer[9], int offset, U16 value)
{
buffer[offset++] = (U8) (value & 255);
if (value >= 256)
buffer[offset++] = (U8) (value >> 8);
return offset;
}
typedef struct
{
U16 flags, bytes, runlen, vbstream0;
} block_header_info;
enum
{
COMMON_INCOMPLETE_DATA = -2,
COMMON_INVALID_DATA = -3,
COMMON_STREAM_HEADER = -4
};
static int radaudio_encode_block_header(U8 buffer[10], const radaudio_block_header_biases *bc, const radaudio_block_header_unpacked *bh)
{
int offset=0;
U32 flags = 0;
// validate limits of what can be encoded in the header
if (bh->vbstream0_length > 65535)
return COMMON_INVALID_DATA;
if (bh->block_bytes > 65535)
return COMMON_INVALID_DATA;
if (bh->num_runlength_array > 65535)
return COMMON_INVALID_DATA;
// validate other aspects of configuration
if (bh->num_channels_encoded != 1 && bh->num_channels_encoded != 2)
return COMMON_INVALID_DATA;
if (bh->num_runlength_array > 2u * (bh->this_block_short ? RADAUDIO_SHORT_BLOCK_LEN+1 : RADAUDIO_LONG_BLOCK_LEN+1))
return COMMON_INVALID_DATA;
if (bh->final_block) {
if (bh->this_block_short) {
if (bh->final_samples_discard > RADAUDIO_SHORT_BLOCK_LEN)
return COMMON_INVALID_DATA;
} else {
// if we would ever discard final samples from a long block,
// it should have been sent as a series of short blocks instead
if (bh->final_samples_discard != 0)
return COMMON_INVALID_DATA;
}
}
U16 bias = bc->bytes_bias[bh->this_block_short][bh->num_channels_encoded];
U16 block_bytes_biased = (U16) ((bh->block_bytes - bias) & 0xffff);
U16 num_runlength_biased = (U16) ((bh->num_runlength_array - 0 ) & 0xffff);
U16 vbstream0_length_biased = (U16) ((bh->vbstream0_length - 0 ) & 0xffff);
if ( bh->this_block_short ) flags |= RADAUDIO_BLOCKFLAG_THIS_SHORT;
if ( bh->next_block_short ) flags |= RADAUDIO_BLOCKFLAG_NEXT_SHORT;
if ( bh->num_channels_encoded == 2 ) flags |= RADAUDIO_BLOCKFLAG_STEREO;
if ( bh->predict_stereo_subband ) flags |= RADAUDIO_BLOCKFLAG_PREDICT_SUBBAND_STEREO;
if ( bh->predict_stereo_exponent ) flags |= RADAUDIO_BLOCKFLAG_PREDICT_EXPONENT_STEREO;
flags |= RADAUDIO_BLOCKFLAG_NONZERO_BITARRAY_MODE_SET(bh->nonzero_bitarray_mode);
if ( bh->final_block ) flags |= RADAUDIO_BLOCKFLAG_FINAL;
if ( bh->disable_final_subband_predict ) flags |= RADAUDIO_BLOCKFLAG_DISABLE_SUBBAND_PREDICT;
if ( bh->mid_side_encoded ) flags |= RADAUDIO_BLOCKFLAG_MIDSIDE_ENCODED;
if ( bh->mid_side_bands ) flags |= RADAUDIO_BLOCKFLAG_MIDSIDE_BANDS;
if ( block_bytes_biased >= 256 ) flags |= RADAUDIO_BLOCKFLAG_EXCESS_BLOCK_BYTES;
if ( num_runlength_biased >= 256 ) flags |= RADAUDIO_BLOCKFLAG_EXCESS_RUNLENGTH_ARRAY;
if ( vbstream0_length_biased >= 256 ) flags |= RADAUDIO_BLOCKFLAG_EXCESS_VBSTREAM0_LENGTH;
if ( flags >= 256 ) flags |= RADAUDIO_BLOCKFLAG_16BIT_FLAGS;
// force odd parity
if ( (countbits8(flags&255) & 1) == 0 ) flags |= RADAUDIO_BLOCKFLAG_8BIT_PARITY;
// write 1 byte if 0..255, otherwise 2 bytes
offset = put_8bits_or_16bits(buffer, offset, (U16) flags);
offset = put_8bits_or_16bits(buffer, offset, block_bytes_biased);
offset = put_8bits_or_16bits(buffer, offset, num_runlength_biased);
offset = put_8bits_or_16bits(buffer, offset, vbstream0_length_biased);
if (bh->final_block) {
RR_PUT16_LE(&buffer[offset], (U16) bh->final_samples_discard);
offset += 2;
}
// maximum written is 5 16-bit values, i.e. 10 bytes
return offset;
}
// returns length of header in bytes
// returns RADAUDIO_INCOMPLETE_DATA if there's not enough data to decode the header
// returns RADAUDIO_INVALID_DATA if the header fails some simple validity tests
static int radaudio_decode_block_header(const U8 buffer[10], const radaudio_block_header_biases *bc, radaudio_block_header_unpacked *bh, size_t memavail)
{
int offset = 0;
// block header is a minimum of 4 bytes
if (memavail < 4)
return COMMON_INCOMPLETE_DATA;
U16 flags = buffer[offset++];
// if flags value is the magic byte that's the first byte of a stream, assume we're scanning the beginning of the stream
if (flags == RADAUDIO_STREAMHEADER_FLAGS)
return COMMON_STREAM_HEADER;
// check parity of first flag byte to catch corrupt streams as soon as possible
// can't check before above test because RADAUDIO_STREAMHEADER_FLAGS intentionally has wrong parity
if ((countbits8(flags) & 1) == 0)
return COMMON_INVALID_DATA;
// read second byte of flags if there is one
if (flags & RADAUDIO_BLOCKFLAG_16BIT_FLAGS)
flags |= buffer[offset++] << 8;
// compute header length based on flags
size_t header_length = offset;
header_length += (flags & RADAUDIO_BLOCKFLAG_EXCESS_BLOCK_BYTES) ? 2 : 1;
header_length += (flags & RADAUDIO_BLOCKFLAG_EXCESS_RUNLENGTH_ARRAY) ? 2 : 1;
header_length += (flags & RADAUDIO_BLOCKFLAG_EXCESS_VBSTREAM0_LENGTH) ? 2 : 1;
header_length += (flags & RADAUDIO_BLOCKFLAG_FINAL) ? 2 : 0;
if ((size_t)header_length > memavail)
return COMMON_INCOMPLETE_DATA;
// at this point all data should be available, so no length checking is needed
bh->this_block_short = ((flags & RADAUDIO_BLOCKFLAG_THIS_SHORT ) != 0 );
bh->next_block_short = ((flags & RADAUDIO_BLOCKFLAG_NEXT_SHORT ) != 0 );
bh->num_channels_encoded = ((flags & RADAUDIO_BLOCKFLAG_STEREO ) ? 2 : 1);
bh->predict_stereo_subband = ((flags & RADAUDIO_BLOCKFLAG_PREDICT_SUBBAND_STEREO ) != 0 );
bh->predict_stereo_exponent = ((flags & RADAUDIO_BLOCKFLAG_PREDICT_EXPONENT_STEREO ) != 0 );
bh->nonzero_bitarray_mode = RADAUDIO_BLOCKFLAG_NONZERO_BITARRAY_MODE_GET(flags);
bh->final_block = ((flags & RADAUDIO_BLOCKFLAG_FINAL ) != 0 );
bh->disable_final_subband_predict = ((flags & RADAUDIO_BLOCKFLAG_DISABLE_SUBBAND_PREDICT ) != 0 );
bh->mid_side_encoded = ((flags & RADAUDIO_BLOCKFLAG_MIDSIDE_ENCODED ) != 0 );
bh->mid_side_bands = ((flags & RADAUDIO_BLOCKFLAG_MIDSIDE_BANDS ) != 0 );
U16 block_bytes, num_runlength_array, vbstream0_length;
// if values are sent as two bytes, verify that they required two bytes.
// unlikely to catch stream corruption, but it can't hurt.
if ((flags & RADAUDIO_BLOCKFLAG_EXCESS_BLOCK_BYTES)==0) {
block_bytes = buffer[offset++];
} else {
block_bytes = RR_GET16_LE(&buffer[offset]);
if (block_bytes < 256)
return COMMON_INVALID_DATA;
offset += 2;
}
if ((flags & RADAUDIO_BLOCKFLAG_EXCESS_RUNLENGTH_ARRAY) == 0) {
num_runlength_array = buffer[offset++];
} else {
num_runlength_array = RR_GET16_LE(&buffer[offset]);
if (num_runlength_array < 256)
return COMMON_INVALID_DATA;
offset += 2;
}
if ((flags & RADAUDIO_BLOCKFLAG_EXCESS_VBSTREAM0_LENGTH) == 0) {
vbstream0_length = buffer[offset++];
} else {
vbstream0_length = RR_GET16_LE(&buffer[offset]);
if (vbstream0_length < 256)
return COMMON_INVALID_DATA;
offset += 2;
}
if (bh->final_block) {
bh->final_samples_discard = RR_GET16_LE(&buffer[offset]);
offset += 2;
}
bh->block_bytes = (U16) (block_bytes + bc->bytes_bias[bh->this_block_short][bh->num_channels_encoded]);
bh->num_runlength_array = (U16) (num_runlength_array + 0 );
bh->vbstream0_length = (U16) (vbstream0_length + 0 );
// let caller do further sanity checking
return offset;
}
// all values little-endian
typedef struct
{
char magic[8];
U32 version;
U16 sample_rate;
U16 bytes_bias;
// 16 bytes
U8 num_channels; // must be 1 or 2
U8 nzmode_data1[NUM_NZ_MODE/2]; // 4 modes, 1 nibble each, packed number of 8-byte chunks for 4 modes
U8 nzmode_data2[NUM_SELECTOR_MODES*1]; // 5 modes, 1 byte each, coding 4 2-bit huffman selectors
U8 nzmode_data3[(NUM_NZ_MODE-1)*(MAX_NZ_BLOCKS/2)]; // 3 modes, 12 nibbles each, coding 12 4-bit huffman selectors + inversion bit
// 26 bytes
U8 subband_predicted_sum[MAX_BANDS];
S8 mantissa_param[2][MAX_BANDS/4][2];
// 48 bytes
U8 padding[128-16-26-48];
} radaudio_stream_header;
RR_COMPILER_ASSERT(sizeof(radaudio_stream_header)==128);
static const char RADAUDIO_MAGIC[8] = {
RADAUDIO_STREAMHEADER_FLAGS,
'R', 'a', 'd',
'A', 'u', 'd',
'\032', // ^Z
};
// bitflag in packed and unpacked stream header signalling to if an 8-byte block should be xor-with-255
// we don't process it here, we let client process it
#define NZ_MODE_INVERT (1<<3)
static size_t radaudio_pack_stream_header(U8 *raw_header,
radaudio_stream_header_unpacked *h)
{
if (h->sample_rate <= 0 || h->sample_rate > 65535)
return 0;
if (h->version < 0 || h->version > MAX_VALID_VERSION )
return 0;
if (h->num_channels <= 0 || h->num_channels > 2 )
return 0;
if (h->sample_rate != 48000 && h->sample_rate != 44100 && h->sample_rate != 32000 && h->sample_rate != 24000)
return 0;
radaudio_stream_header *header = (radaudio_stream_header *) raw_header;
memset(header, 0, sizeof(*header));
memcpy(header->magic, RADAUDIO_MAGIC, sizeof(header->magic));
header->version = h->version;
header->num_channels = (U8 ) h->num_channels;
header->sample_rate = (U16) h->sample_rate;
memcpy(header->subband_predicted_sum, h->subband_predicted_sum, 24);
for (int i=0; i < MAX_BANDS/4; ++i) {
header->mantissa_param[0][i][0] = h->mantissa_param[0][i*4+0][0];
header->mantissa_param[0][i][1] = h->mantissa_param[0][i*4+0][1];
header->mantissa_param[1][i][0] = h->mantissa_param[1][i*4+0][0];
header->mantissa_param[1][i][1] = h->mantissa_param[1][i*4+0][1];
}
header->bytes_bias = h->bytes_bias;
// pack each mode
for (int i=0; i < NUM_NZ_MODE/2; ++i)
header->nzmode_data1[i] = h->nzmode_num64[2*i+0] | (h->nzmode_num64[2*i+1] << 4);
// pack general huff selectors
for (int i=0; i < NUM_SELECTOR_MODES; ++i) {
U8 value = 0;
for (int j=0; j < 4; ++j)
value |= h->nzmode_selectors[j][i] << (2*j);
header->nzmode_data2[i] = value;
}
// pack nz huff selectors
for (int i=0; i < NUM_NZ_MODE-1; ++i) {
for (int j=0; j < MAX_NZ_BLOCKS/2; ++j) {
U8 value;
value = h->nzmode_huff[i][j*2+0] ;
value |= h->nzmode_huff[i][j*2+1] << 4;
header->nzmode_data3[i*MAX_NZ_BLOCKS/2+j] = value;
}
}
return sizeof(*header);
}
static rrbool radaudio_check_stream_header(U8 *raw_header, size_t raw_header_bytes_valid)
{
radaudio_stream_header *header = (radaudio_stream_header *) raw_header;
if (raw_header_bytes_valid < 8) // can we check MAGIC?
return false;
return (0 == memcmp(header->magic, RADAUDIO_MAGIC, sizeof(header->magic)));
}
static size_t radaudio_unpack_stream_header(U8 *raw_header, size_t raw_header_bytes_valid, radaudio_stream_header_unpacked *h)
{
radaudio_stream_header *header = (radaudio_stream_header *) raw_header;
if (!radaudio_check_stream_header(raw_header, raw_header_bytes_valid))
return 0;
// we are going to directly address everything up to the padding
// in the header
size_t required_header_bytes = sizeof(radaudio_stream_header) - sizeof(header->padding);
if (raw_header_bytes_valid < required_header_bytes)
return 0;
h->sample_rate_mode = radaudio_code_sample_rate(header->sample_rate);
if (h->sample_rate_mode < 0)
return 0;
h->sample_rate = header->sample_rate;
h->num_channels = header->num_channels;
h->version = header->version;
h->bytes_bias = header->bytes_bias;
memcpy(h->subband_predicted_sum, header->subband_predicted_sum, 24);
memcpy(h->mantissa_param, header->mantissa_param, sizeof(header->mantissa_param));
for (int i=0; i < MAX_BANDS/4; ++i) {
for (int j=0; j < 4; ++j) {
h->mantissa_param[0][i*4+j][0] = header->mantissa_param[0][i][0];
h->mantissa_param[0][i*4+j][1] = header->mantissa_param[0][i][1];
h->mantissa_param[1][i*4+j][0] = header->mantissa_param[1][i][0];
h->mantissa_param[1][i*4+j][1] = header->mantissa_param[1][i][1];
}
}
// unpack each mode
for (int i=0; i < NUM_NZ_MODE/2; ++i) {
h->nzmode_num64[2*i+0] = header->nzmode_data1[i] & 15;
h->nzmode_num64[2*i+1] = header->nzmode_data1[i] >> 4;
}
// unpack general huff selectors
for (int i=0; i < NUM_SELECTOR_MODES; ++i) {
U8 value = header->nzmode_data2[i];
for (int j=0; j < 4; ++j) {
h->nzmode_selectors[j][i] = (value >> (2*j)) & 3;
}
}
// unpack nz huff selectors
for (int i=0; i < NUM_NZ_MODE-1; ++i) {
for (int j=0; j < MAX_NZ_BLOCKS/2; ++j) {
U8 value = header->nzmode_data3[i*MAX_NZ_BLOCKS/2+j];
h->nzmode_huff[i][j*2+0] = value & 15;
h->nzmode_huff[i][j*2+1] = value >> 4;
}
}
radaudio_rate_info *bi = &radaudio_rateinfo[0][h->sample_rate_mode];
// compute derived bias values
for (int i=0; i < bi->num_bands; ++i)
if (bi->num_subbands_for_band[i] != 1) {
h->subband_bias[i] = (S8) (SUBBAND_BIAS_CENTER - (h->subband_predicted_sum[i] / bi->num_subbands_for_band[i]));
}
else
h->subband_bias[i] = (S8) -1;
if (h->num_channels < 1 || h->num_channels > 2)
return 0;
if (h->version > MAX_VALID_VERSION)
return 0;
return sizeof(*header);
}