TAD: back to twobitmap

This commit is contained in:
minjaesong
2025-10-28 04:04:41 +09:00
parent c6de68291d
commit 86de627734
2 changed files with 71 additions and 90 deletions

View File

@@ -21,10 +21,8 @@ static const float TAD32_COEFF_SCALARS[] = {64.0f, 45.255f, 32.0f, 22.627f, 16.0
// Forward declarations for internal functions
static void dwt_dd4_forward_1d(float *data, int length);
static void dwt_dd4_forward_multilevel(float *data, int length, int levels);
static void ms_decorrelate_16(const float *left, const float *right, float *mid, float *side, size_t count);
static void get_quantization_weights(int dwt_levels, float *weights);
static void quantize_dwt_coefficients(const float *coeffs, int16_t *quantized, size_t count, int apply_deadzone, int chunk_size, int dwt_levels, int quant_bits, int *current_subband_index);
static size_t encode_sigmap_2bit(const int16_t *values, size_t count, uint8_t *output);
static void quantize_dwt_coefficients(const float *coeffs, int8_t *quantized, size_t count, int apply_deadzone, int chunk_size, int dwt_levels, int quant_bits, int *current_subband_index);
static size_t encode_twobitmap(const int8_t *values, size_t count, uint8_t *output);
static inline float FCLAMP(float x, float min, float max) {
return x < min ? min : (x > max ? max : x);
@@ -235,8 +233,8 @@ static void compress_mu_law(float *left, float *right, size_t count) {
// Lambda-based companding encoder (based on Laplacian distribution CDF)
// val must be normalised to [-1,1]
// Returns quantized index in range [-(2^quant_bits-1), +(2^quant_bits-1)]
static int16_t lambda_companding(float val, int max_index) {
// Returns quantized index in range [-127, +127]
static int8_t lambda_companding(float val, int max_index) {
// Handle zero
if (fabsf(val) < 1e-9f) {
return 0;
@@ -263,10 +261,10 @@ static int16_t lambda_companding(float val, int max_index) {
if (index < 0) index = 0;
if (index > max_index) index = max_index;
return (int16_t)(sign * index);
return (int8_t)(sign * index);
}
static void quantize_dwt_coefficients(const float *coeffs, int16_t *quantized, size_t count, int apply_deadzone, int chunk_size, int dwt_levels, int max_index, int *current_subband_index) {
static void quantize_dwt_coefficients(const float *coeffs, int8_t *quantized, size_t count, int apply_deadzone, int chunk_size, int dwt_levels, int max_index, int *current_subband_index) {
int first_band_size = chunk_size >> dwt_levels;
int *sideband_starts = malloc((dwt_levels + 2) * sizeof(int));
@@ -300,59 +298,58 @@ static void quantize_dwt_coefficients(const float *coeffs, int16_t *quantized, s
}
//=============================================================================
// Bitplane Encoding with Delta Prediction
// Twobit-map Significance Map Encoding
//=============================================================================
// Pure bitplane encoding with delta prediction: each coefficient uses exactly (quant_bits + 1) bits
// Bit layout: 1 sign bit + quant_bits magnitude bits
// Sign bit: 0 = positive/zero, 1 = negative
// Magnitude: unsigned value [0, 2^quant_bits - 1]
// Delta prediction: plane[i] ^= plane[i-1] for better compression
static size_t encode_bitplanes(const int16_t *values, size_t count, uint8_t *output, int max_index) {
int bits_per_coeff = ((int)ceilf(log2f(max_index))) + 1; // 1 sign bit + quant_bits magnitude bits
size_t plane_bytes = (count + 7) / 8; // Bytes needed for one bitplane
size_t output_bytes = plane_bytes * bits_per_coeff;
// Twobit-map encoding: 2 bits per coefficient for common values
// 00 = 0
// 01 = +1
// 10 = -1
// 11 = other value (followed by int8_t in separate array)
static size_t encode_twobitmap(const int8_t *values, size_t count, uint8_t *output) {
// Calculate size needed for twobit map
size_t map_bytes = (count * 2 + 7) / 8; // 2 bits per coefficient
memset(output, 0, output_bytes);
// First pass: create significance map and count "other" values
uint8_t *map = output;
memset(map, 0, map_bytes);
// Separate bitplanes (sign + magnitude)
uint8_t **bitplanes = malloc(bits_per_coeff * sizeof(uint8_t*));
for (int plane = 0; plane < bits_per_coeff; plane++) {
bitplanes[plane] = output + (plane * plane_bytes);
}
// Extract coefficients into bitplanes
size_t other_count = 0;
for (size_t i = 0; i < count; i++) {
int16_t val = values[i];
int8_t val = values[i];
uint8_t code;
// Extract sign and magnitude
uint8_t sign_bit = (val < 0) ? 1 : 0;
uint16_t magnitude = (val < 0) ? -val : val;
// Clamp magnitude to max value for quant_bits
uint16_t max_magnitude = max_index;
if (magnitude > max_magnitude) {
magnitude = max_magnitude;
if (val == 0) {
code = 0; // 00
} else if (val == 1) {
code = 1; // 01
} else if (val == -1) {
code = 2; // 10
} else {
code = 3; // 11
other_count++;
}
size_t byte_idx = i / 8;
size_t bit_offset = i % 8;
// Write 2-bit code into map
size_t bit_offset = i * 2;
size_t byte_idx = bit_offset / 8;
size_t bit_in_byte = bit_offset % 8;
// Sign bitplane (plane 0)
if (sign_bit) {
bitplanes[0][byte_idx] |= (1 << bit_offset);
map[byte_idx] |= (code << bit_in_byte);
}
// Magnitude bitplanes (planes 1 to quant_bits)
for (int b = 0; b < bits_per_coeff - 1; b++) {
if (magnitude & (1 << b)) {
bitplanes[b + 1][byte_idx] |= (1 << bit_offset);
}
// Second pass: write "other" values
int8_t *other_values = (int8_t*)(output + map_bytes);
size_t other_idx = 0;
for (size_t i = 0; i < count; i++) {
int8_t val = values[i];
if (val != 0 && val != 1 && val != -1) {
other_values[other_idx++] = val;
}
}
free(bitplanes);
return output_bytes;
return map_bytes + other_count;
}
//=============================================================================
@@ -383,7 +380,7 @@ typedef struct {
} CoeffAccumulator;
typedef struct {
int16_t *data;
int8_t *data;
size_t count;
size_t capacity;
} QuantAccumulator;
@@ -418,11 +415,11 @@ static void init_statistics(int dwt_levels) {
side_accumulators[i].count = 0;
mid_quant_accumulators[i].capacity = 1024;
mid_quant_accumulators[i].data = malloc(mid_quant_accumulators[i].capacity * sizeof(int16_t));
mid_quant_accumulators[i].data = malloc(mid_quant_accumulators[i].capacity * sizeof(int8_t));
mid_quant_accumulators[i].count = 0;
side_quant_accumulators[i].capacity = 1024;
side_quant_accumulators[i].data = malloc(side_quant_accumulators[i].capacity * sizeof(int16_t));
side_quant_accumulators[i].data = malloc(side_quant_accumulators[i].capacity * sizeof(int8_t));
side_quant_accumulators[i].count = 0;
}
@@ -460,7 +457,7 @@ static void accumulate_coefficients(const float *coeffs, int dwt_levels, int chu
free(sideband_starts);
}
static void accumulate_quantized(const int16_t *quant, int dwt_levels, int chunk_size, QuantAccumulator *accumulators) {
static void accumulate_quantized(const int8_t *quant, int dwt_levels, int chunk_size, QuantAccumulator *accumulators) {
int first_band_size = chunk_size >> dwt_levels;
int *sideband_starts = malloc((dwt_levels + 2) * sizeof(int));
@@ -479,12 +476,12 @@ static void accumulate_quantized(const int16_t *quant, int dwt_levels, int chunk
while (accumulators[s].count + band_size > accumulators[s].capacity) {
accumulators[s].capacity *= 2;
accumulators[s].data = realloc(accumulators[s].data,
accumulators[s].capacity * sizeof(int16_t));
accumulators[s].capacity * sizeof(int8_t));
}
// Copy coefficients
memcpy(accumulators[s].data + accumulators[s].count,
quant + start, band_size * sizeof(int16_t));
quant + start, band_size * sizeof(int8_t));
accumulators[s].count += band_size;
}
@@ -581,7 +578,7 @@ static void print_histogram(const float *coeffs, size_t count, const char *title
}
typedef struct {
int16_t value;
int8_t value;
size_t count;
float percentage;
} ValueFrequency;
@@ -595,40 +592,27 @@ static int compare_value_frequency(const void *a, const void *b) {
return 0;
}
static void print_top5_quantized_values(const int16_t *quant, size_t count, const char *title) {
static void print_top5_quantized_values(const int8_t *quant, size_t count, const char *title) {
if (count == 0) {
fprintf(stderr, " %s: No data\n", title);
return;
}
// Create a hash map to count frequencies
// For simplicity, we'll use an array with a reasonable range
// Find min/max first
int16_t min_val = quant[0];
int16_t max_val = quant[0];
for (size_t i = 1; i < count; i++) {
if (quant[i] < min_val) min_val = quant[i];
if (quant[i] > max_val) max_val = quant[i];
}
// For int8_t range is at most 256, so we can use direct indexing
// Map from [-128, 127] to [0, 255]
size_t freq[256] = {0};
int range = max_val - min_val + 1;
if (range > 100000) {
fprintf(stderr, " %s: Value range too large for analysis\n", title);
return;
}
// Count frequencies
size_t *freq = calloc(range, sizeof(size_t));
for (size_t i = 0; i < count; i++) {
freq[quant[i] - min_val]++;
int idx = (int)quant[i] + 128;
freq[idx]++;
}
// Find all unique values with their frequencies
ValueFrequency *values = malloc(range * sizeof(ValueFrequency));
ValueFrequency values[256];
int unique_count = 0;
for (int i = 0; i < range; i++) {
for (int i = 0; i < 256; i++) {
if (freq[i] > 0) {
values[unique_count].value = min_val + i;
values[unique_count].value = (int8_t)(i - 128);
values[unique_count].count = freq[i];
values[unique_count].percentage = (float)(freq[i] * 100.0) / count;
unique_count++;
@@ -638,17 +622,14 @@ static void print_top5_quantized_values(const int16_t *quant, size_t count, cons
// Sort by frequency
qsort(values, unique_count, sizeof(ValueFrequency), compare_value_frequency);
// Print top 5
fprintf(stderr, " %s Top 5 Values:\n", title);
int print_count = (unique_count < 5) ? unique_count : 10;
// Print top 10
fprintf(stderr, " %s Top 10 Values:\n", title);
int print_count = (unique_count < 10) ? unique_count : 10;
for (int i = 0; i < print_count; i++) {
fprintf(stderr, " %6d: %8zu occurrences (%5.2f%%)\n",
values[i].value, values[i].count, values[i].percentage);
}
fprintf(stderr, "\n");
free(freq);
free(values);
}
void tad32_print_statistics(void) {
@@ -797,8 +778,8 @@ size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples,
float *dwt_mid = malloc(num_samples * sizeof(float));
float *dwt_side = malloc(num_samples * sizeof(float));
int16_t *quant_mid = malloc(num_samples * sizeof(int16_t));
int16_t *quant_side = malloc(num_samples * sizeof(int16_t));
int8_t *quant_mid = malloc(num_samples * sizeof(int8_t));
int8_t *quant_side = malloc(num_samples * sizeof(int8_t));
// Step 1: Deinterleave stereo
for (size_t i = 0; i < num_samples; i++) {
@@ -844,10 +825,10 @@ size_t tad32_encode_chunk(const float *pcm32_stereo, size_t num_samples,
accumulate_quantized(quant_side, dwt_levels, num_samples, side_quant_accumulators);
}
// Step 5: Encode with pure bitplanes (quant_bits + 1 bits per coefficient)
uint8_t *temp_buffer = malloc(num_samples * 4 * sizeof(int32_t));
size_t mid_size = encode_bitplanes(quant_mid, num_samples, temp_buffer, max_index);
size_t side_size = encode_bitplanes(quant_side, num_samples, temp_buffer + mid_size, max_index);
// Step 5: Encode with twobit-map significance map
uint8_t *temp_buffer = malloc(num_samples * 4); // Generous buffer for twobitmap + other values
size_t mid_size = encode_twobitmap(quant_mid, num_samples, temp_buffer);
size_t side_size = encode_twobitmap(quant_side, num_samples, temp_buffer + mid_size);
size_t uncompressed_size = mid_size + side_size;

View File

@@ -114,7 +114,7 @@ int main(int argc, char *argv[]) {
printf("Input: %s\n", input_file);
printf("Output: %s\n", output_file);
printf("Quant: %d\n", max_index);
printf("Encoding method: Pure bitplanes (%d bits per coefficient)\n", ((int)ceilf(log2f(max_index))) + 1);
printf("Encoding method: Twobit-map significance map (int8_t coefficients)\n");
printf("Zstd compression: %s\n", use_zstd ? "enabled" : "disabled");
}