diff --git a/video_encoder/Makefile b/video_encoder/Makefile index dec6a62..3d5dfa9 100644 --- a/video_encoder/Makefile +++ b/video_encoder/Makefile @@ -35,9 +35,11 @@ tav: encoder_tav.c encoder_tad.c encoder_tav_opencv.cpp $(CXX) $(CXXFLAGS) $(OPENCV_CFLAGS) $(ZSTD_CFLAGS) -c encoder_tav_opencv.cpp -o encoder_tav_opencv.o $(CXX) -o encoder_tav encoder_tav.o encoder_tad.o encoder_tav_opencv.o $(LIBS) $(OPENCV_LIBS) -tav_decoder: decoder_tav.c - rm -f decoder_tav - $(CC) $(CFLAGS) $(ZSTD_CFLAGS) -o decoder_tav $< $(LIBS) +tav_decoder: decoder_tav.c decoder_tad.c decoder_tad.h + rm -f decoder_tav decoder_tav.o + $(CC) $(CFLAGS) $(ZSTD_CFLAGS) -DTAD_DECODER_LIB -c decoder_tad.c -o decoder_tad.o + $(CC) $(CFLAGS) $(ZSTD_CFLAGS) -c decoder_tav.c -o decoder_tav.o + $(CC) -o decoder_tav decoder_tav.o decoder_tad.o $(LIBS) tav_inspector: tav_inspector.c rm -f tav_inspector diff --git a/video_encoder/decoder_tad.c b/video_encoder/decoder_tad.c index b128f24..654d908 100644 --- a/video_encoder/decoder_tad.c +++ b/video_encoder/decoder_tad.c @@ -868,8 +868,9 @@ static int tad_decode_channel_ezbc(const uint8_t *input, size_t input_size, int8 // Chunk Decoding //============================================================================= -static int decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_stereo, - size_t *bytes_consumed, size_t *samples_decoded) { +// Public API: TAD32 chunk decoder (can be used by both standalone decoder and TAV decoder) +int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_stereo, + size_t *bytes_consumed, size_t *samples_decoded) { const uint8_t *read_ptr = input; // Read chunk header @@ -988,6 +989,7 @@ static int decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_ // Main Decoder //============================================================================= +#ifndef TAD_DECODER_LIB // Only compile main() when building standalone decoder static void print_usage(const char *prog_name) { printf("Usage: %s -i [options]\n", prog_name); printf("Options:\n"); @@ -1130,8 +1132,8 @@ int main(int argc, char *argv[]) { while (offset < input_size) { size_t bytes_consumed, samples_decoded; - int result = decode_chunk(input_data + offset, input_size - offset, - chunk_output, &bytes_consumed, &samples_decoded); + int result = tad32_decode_chunk(input_data + offset, input_size - offset, + chunk_output, &bytes_consumed, &samples_decoded); if (result != 0) { fprintf(stderr, "Error: Chunk decoding failed at offset %zu\n", offset); @@ -1183,3 +1185,4 @@ int main(int argc, char *argv[]) { return 0; } +#endif // TAD_DECODER_LIB diff --git a/video_encoder/decoder_tad.h b/video_encoder/decoder_tad.h new file mode 100644 index 0000000..80094c9 --- /dev/null +++ b/video_encoder/decoder_tad.h @@ -0,0 +1,39 @@ +#ifndef TAD32_DECODER_H +#define TAD32_DECODER_H + +#include +#include + +// TAD32 (Terrarum Advanced Audio - PCM32f version) Decoder +// DWT-based perceptual audio codec for TSVM +// Shared decoder library used by both decoder_tad (standalone) and decoder_tav (video decoder) + +// Constants (must match encoder) +#define TAD32_SAMPLE_RATE 32000 +#define TAD32_CHANNELS 2 // Stereo +#define TAD_DEFAULT_CHUNK_SIZE 31991 // Default chunk size for standalone TAD files + +/** + * Decode audio chunk with TAD32 codec + * + * @param input Input TAD32 chunk data + * @param input_size Size of input buffer + * @param pcmu8_stereo Output PCMu8 stereo samples (interleaved L,R) + * @param bytes_consumed [out] Number of bytes consumed from input + * @param samples_decoded [out] Number of samples decoded per channel + * @return 0 on success, -1 on error + * + * Input format: + * uint16 sample_count (samples per channel) + * uint8 max_index (maximum quantization index) + * uint32 payload_size (bytes in payload) + * * payload (encoded M/S data, Zstd-compressed with EZBC) + * + * Output format: + * PCMu8 stereo interleaved (8-bit unsigned PCM, L,R pairs) + * Range: [0, 255] where 128 = silence + */ +int tad32_decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_stereo, + size_t *bytes_consumed, size_t *samples_decoded); + +#endif // TAD32_DECODER_H diff --git a/video_encoder/decoder_tav.c b/video_encoder/decoder_tav.c index 7f30cde..d426c28 100644 --- a/video_encoder/decoder_tav.c +++ b/video_encoder/decoder_tav.c @@ -13,6 +13,7 @@ #include #include #include +#include "decoder_tad.h" // Shared TAD decoder library #define DECODER_VENDOR_STRING "Decoder-TAV 20251103 (ffv1+pcmu8)" @@ -502,6 +503,48 @@ static void expand_mu_law(float *left, float *right, size_t count) { } } +//============================================================================= +// De-emphasis Filter (TAD) +//============================================================================= + +static void calculate_deemphasis_coeffs(float *b0, float *b1, float *a1) { + // De-emphasis factor (must match encoder pre-emphasis alpha=0.5) + const float alpha = 0.5f; + + *b0 = 1.0f; + *b1 = 0.0f; // No feedforward delay + *a1 = -alpha; // NEGATIVE because equation has minus sign: y = x - a1*prev_y +} + +static void apply_deemphasis(float *left, float *right, size_t count) { + // Static state variables - persistent across chunks to prevent discontinuities + static float prev_x_l = 0.0f; + static float prev_y_l = 0.0f; + static float prev_x_r = 0.0f; + static float prev_y_r = 0.0f; + + float b0, b1, a1; + calculate_deemphasis_coeffs(&b0, &b1, &a1); + + // Left channel - use persistent state + for (size_t i = 0; i < count; i++) { + float x = left[i]; + float y = b0 * x + b1 * prev_x_l - a1 * prev_y_l; + left[i] = y; + prev_x_l = x; + prev_y_l = y; + } + + // Right channel - use persistent state + for (size_t i = 0; i < count; i++) { + float x = right[i]; + float y = b0 * x + b1 * prev_x_r - a1 * prev_y_r; + right[i] = y; + prev_x_r = x; + prev_y_r = y; + } +} + static void pcm32f_to_pcm8(const float *fleft, const float *fright, uint8_t *left, uint8_t *right, size_t count, float dither_error[2][2]) { const float b1 = 1.5f; // 1st feedback coefficient const float b2 = -0.75f; // 2nd feedback coefficient @@ -697,110 +740,9 @@ static void dequantize_dwt_coefficients(const int8_t *quantized, float *coeffs, } //============================================================================= -// Chunk Decoding -//============================================================================= - -static int decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_stereo, - size_t *bytes_consumed, size_t *samples_decoded) { - const uint8_t *read_ptr = input; - - // Read chunk header - uint16_t sample_count = *((const uint16_t*)read_ptr); - read_ptr += sizeof(uint16_t); - - uint8_t max_index = *read_ptr; - read_ptr += sizeof(uint8_t); - - uint32_t payload_size = *((const uint32_t*)read_ptr); - read_ptr += sizeof(uint32_t); - - // Calculate DWT levels from sample count - int dwt_levels = calculate_dwt_levels(sample_count); - if (dwt_levels < 0) { - fprintf(stderr, "Error: Invalid sample count %u\n", sample_count); - return -1; - } - - // Decompress if needed - const uint8_t *payload; - uint8_t *decompressed = NULL; - - // Estimate decompressed size (generous upper bound) - size_t decompressed_size = sample_count * 4 * sizeof(int8_t); - decompressed = malloc(decompressed_size); - - size_t actual_size = ZSTD_decompress(decompressed, decompressed_size, read_ptr, payload_size); - - if (ZSTD_isError(actual_size)) { - fprintf(stderr, "Error: Zstd decompression failed: %s\n", ZSTD_getErrorName(actual_size)); - free(decompressed); - return -1; - } - - read_ptr += payload_size; - *bytes_consumed = read_ptr - input; - *samples_decoded = sample_count; - - // Allocate working buffers - int8_t *quant_mid = malloc(sample_count * sizeof(int8_t)); - int8_t *quant_side = malloc(sample_count * sizeof(int8_t)); - float *dwt_mid = malloc(sample_count * sizeof(float)); - float *dwt_side = malloc(sample_count * sizeof(float)); - float *pcm32_left = malloc(sample_count * sizeof(float)); - float *pcm32_right = malloc(sample_count * sizeof(float)); - uint8_t *pcm8_left = malloc(sample_count * sizeof(uint8_t)); - uint8_t *pcm8_right = malloc(sample_count * sizeof(uint8_t)); - - // Separate Mid/Side - memcpy(quant_mid, decompressed, sample_count); - memcpy(quant_side, decompressed + sample_count, sample_count); - - // Debug: Check if we have non-zero coefficients -// static int debug_coeff_count = 0; -// if (debug_coeff_count < 3) { -// int nonzero_mid = 0, nonzero_side = 0; -// for (int i = 0; i < sample_count; i++) { -// if (quant_mid[i] != 0) nonzero_mid++; -// if (quant_side[i] != 0) nonzero_side++; -// } -// debug_coeff_count++; -// } - - // Dequantize with quantiser scaling and spectral interpolation - // Use quantiser_scale = 1.0f for baseline (must match encoder) - float quantiser_scale = 1.0f; - dequantize_dwt_coefficients(quant_mid, dwt_mid, sample_count, sample_count, dwt_levels, max_index, quantiser_scale); - dequantize_dwt_coefficients(quant_side, dwt_side, sample_count, sample_count, dwt_levels, max_index, quantiser_scale); - - // Inverse DWT - dwt_inverse_multilevel(dwt_mid, sample_count, dwt_levels); - dwt_inverse_multilevel(dwt_side, sample_count, dwt_levels); - - float err[2][2] = {{0,0},{0,0}}; - - // M/S to L/R correlation - ms_correlate(dwt_mid, dwt_side, pcm32_left, pcm32_right, sample_count); - - // expand dynamic range - expand_gamma(pcm32_left, pcm32_right, sample_count); - - // dither to 8-bit - pcm32f_to_pcm8(pcm32_left, pcm32_right, pcm8_left, pcm8_right, sample_count, err); - - // Interleave stereo output (PCMu8) - for (size_t i = 0; i < sample_count; i++) { - pcmu8_stereo[i * 2] = pcm8_left[i]; - pcmu8_stereo[i * 2 + 1] = pcm8_right[i]; - } - - // Cleanup - free(quant_mid); free(quant_side); free(dwt_mid); free(dwt_side); - free(pcm32_left); free(pcm32_right); free(pcm8_left); free(pcm8_right); - if (decompressed) free(decompressed); - - return 0; -} - +// Chunk Decoding (TAD Audio) +// NOTE: TAD decoding now uses shared tad32_decode_chunk() from decoder_tad.h +// This ensures decoder_tav and decoder_tad use identical decoding logic //============================================================================= // Significance Map Postprocessing (matches TSVM exactly) //============================================================================= @@ -2075,7 +2017,7 @@ static int extract_audio_to_wav(const char *input_file, const char *wav_file, in // Decode TAD uint8_t *pcmu8_output = malloc(sample_count_chunk * 2); size_t bytes_consumed, samples_decoded; - int decode_result = decode_chunk(tad_chunk, tad_chunk_size, + int decode_result = tad32_decode_chunk(tad_chunk, tad_chunk_size, pcmu8_output, &bytes_consumed, &samples_decoded); if (decode_result >= 0) { diff --git a/video_encoder/encoder_tav.c b/video_encoder/encoder_tav.c index 70df404..698048b 100644 --- a/video_encoder/encoder_tav.c +++ b/video_encoder/encoder_tav.c @@ -9935,7 +9935,7 @@ int main(int argc, char *argv[]) { }; int c, option_index = 0; - while ((c = getopt_long(argc, argv, "i:o:s:f:q:Q:a:w:c:d:b:S:vt?", long_options, &option_index)) != -1) { + while ((c = getopt_long(argc, argv, "i:o:s:f:q:Q:a:c:d:b:S:vt?", long_options, &option_index)) != -1) { switch (c) { case 'i': enc->input_file = strdup(optarg);