mirror of
https://github.com/curioustorvald/tsvm.git
synced 2026-03-07 11:51:49 +09:00
TAD: auto filename selection
This commit is contained in:
@@ -23,15 +23,15 @@ static const float TAD32_COEFF_SCALARS[] = {64.0f, 45.255f, 32.0f, 22.627f, 16.0
|
||||
// These weights are multiplied by quantiser_scale during dequantization
|
||||
static const float BASE_QUANTISER_WEIGHTS[] = {
|
||||
1.0f, // LL (L9) - finest preservation
|
||||
1.111f, // H (L9)
|
||||
1.222f, // H (L8)
|
||||
1.333f, // H (L7)
|
||||
1.444f, // H (L6)
|
||||
1.556f, // H (L5)
|
||||
1.667f, // H (L4)
|
||||
1.778f, // H (L3)
|
||||
1.889f, // H (L2)
|
||||
2.0f // H (L1) - coarsest quantization
|
||||
1.0f, // H (L9)
|
||||
1.0f, // H (L8)
|
||||
1.0f, // H (L7)
|
||||
1.0f, // H (L6)
|
||||
1.1f, // H (L5)
|
||||
1.2f, // H (L4)
|
||||
1.3f, // H (L3)
|
||||
1.4f, // H (L2)
|
||||
1.5f // H (L1) - coarsest quantization
|
||||
};
|
||||
|
||||
#define TAD_DEFAULT_CHUNK_SIZE 32768
|
||||
@@ -52,6 +52,37 @@ static inline float FCLAMP(float x, float min, float max) {
|
||||
return x < min ? min : (x > max ? max : x);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// WAV Header Writing
|
||||
//=============================================================================
|
||||
|
||||
static void write_wav_header(FILE *output, uint32_t data_size, uint16_t channels, uint32_t sample_rate, uint16_t bits_per_sample) {
|
||||
uint32_t byte_rate = sample_rate * channels * bits_per_sample / 8;
|
||||
uint16_t block_align = channels * bits_per_sample / 8;
|
||||
uint32_t chunk_size = 36 + data_size;
|
||||
|
||||
// RIFF header
|
||||
fwrite("RIFF", 1, 4, output);
|
||||
fwrite(&chunk_size, 4, 1, output);
|
||||
fwrite("WAVE", 1, 4, output);
|
||||
|
||||
// fmt chunk
|
||||
fwrite("fmt ", 1, 4, output);
|
||||
uint32_t fmt_size = 16;
|
||||
fwrite(&fmt_size, 4, 1, output);
|
||||
uint16_t audio_format = 1; // PCM
|
||||
fwrite(&audio_format, 2, 1, output);
|
||||
fwrite(&channels, 2, 1, output);
|
||||
fwrite(&sample_rate, 4, 1, output);
|
||||
fwrite(&byte_rate, 4, 1, output);
|
||||
fwrite(&block_align, 2, 1, output);
|
||||
fwrite(&bits_per_sample, 2, 1, output);
|
||||
|
||||
// data chunk header
|
||||
fwrite("data", 1, 4, output);
|
||||
fwrite(&data_size, 4, 1, output);
|
||||
}
|
||||
|
||||
// Calculate DWT levels from chunk size (must be power of 2, >= 1024)
|
||||
static int calculate_dwt_levels(int chunk_size) {
|
||||
/*if (chunk_size < TAD_MIN_CHUNK_SIZE) {
|
||||
@@ -287,9 +318,9 @@ static void expand_gamma(float *left, float *right, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
// decode(y) = sign(y) * |y|^(1/γ) where γ=0.5
|
||||
float x = left[i]; float a = fabsf(x);
|
||||
left[i] = signum(x) * a * a;
|
||||
left[i] = signum(x) * powf(a, 1.4142f);
|
||||
float y = right[i]; float b = fabsf(y);
|
||||
right[i] = signum(y) * b * b;
|
||||
right[i] = signum(y) * powf(b, 1.4142f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,24 +545,34 @@ static int decode_chunk(const uint8_t *input, size_t input_size, uint8_t *pcmu8_
|
||||
//=============================================================================
|
||||
|
||||
static void print_usage(const char *prog_name) {
|
||||
printf("Usage: %s -i <input> -o <output> [options]\n", prog_name);
|
||||
printf("Usage: %s -i <input> [options]\n", prog_name);
|
||||
printf("Options:\n");
|
||||
printf(" -i <file> Input TAD file\n");
|
||||
printf(" -o <file> Output PCMu8 file (raw 8-bit unsigned stereo @ 32kHz)\n");
|
||||
printf(" -o <file> Output file (optional, auto-generated from input)\n");
|
||||
printf(" Default: input_qNN.wav (or .pcm with --raw-pcm)\n");
|
||||
printf(" --raw-pcm Output raw PCMu8 instead of WAV file\n");
|
||||
printf(" -v Verbose output\n");
|
||||
printf(" -h, --help Show this help\n");
|
||||
printf("\nVersion: %s\n", DECODER_VENDOR_STRING);
|
||||
printf("Output format: PCMu8 (unsigned 8-bit) stereo @ 32000 Hz\n");
|
||||
printf("To convert to WAV: ffmpeg -f u8 -ar 32000 -ac 2 -i output.raw output.wav\n");
|
||||
printf("Default output: WAV file (8-bit unsigned PCM, stereo @ 32000 Hz)\n");
|
||||
printf("With --raw-pcm: PCMu8 raw file (8-bit unsigned stereo @ 32000 Hz)\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
char *input_file = NULL;
|
||||
char *output_file = NULL;
|
||||
int verbose = 0;
|
||||
int raw_pcm = 0;
|
||||
|
||||
static struct option long_options[] = {
|
||||
{"raw-pcm", no_argument, 0, 'r'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "i:o:vh")) != -1) {
|
||||
int option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, "i:o:vh", long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
input_file = optarg;
|
||||
@@ -539,6 +580,9 @@ int main(int argc, char *argv[]) {
|
||||
case 'o':
|
||||
output_file = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
raw_pcm = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
@@ -551,12 +595,52 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_file || !output_file) {
|
||||
fprintf(stderr, "Error: Input and output files are required\n");
|
||||
if (!input_file) {
|
||||
fprintf(stderr, "Error: Input file is required\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Generate output filename if not provided
|
||||
if (!output_file) {
|
||||
size_t input_len = strlen(input_file);
|
||||
output_file = malloc(input_len + 32); // Extra space for extension
|
||||
|
||||
// Find the last directory separator
|
||||
const char *basename_start = strrchr(input_file, '/');
|
||||
if (!basename_start) basename_start = strrchr(input_file, '\\');
|
||||
basename_start = basename_start ? basename_start + 1 : input_file;
|
||||
|
||||
// Copy directory part
|
||||
size_t dir_len = basename_start - input_file;
|
||||
strncpy(output_file, input_file, dir_len);
|
||||
|
||||
// Find the .tad extension
|
||||
const char *ext = strrchr(basename_start, '.');
|
||||
if (ext && strcmp(ext, ".tad") == 0) {
|
||||
// Copy basename without .tad
|
||||
size_t name_len = ext - basename_start;
|
||||
strncpy(output_file + dir_len, basename_start, name_len);
|
||||
output_file[dir_len + name_len] = '\0';
|
||||
|
||||
// Replace last dot with underscore (for .qNN pattern)
|
||||
char *last_dot = strrchr(output_file, '.');
|
||||
if (last_dot && last_dot > output_file + dir_len) {
|
||||
*last_dot = '_';
|
||||
}
|
||||
} else {
|
||||
// No .tad extension, copy entire basename
|
||||
strcpy(output_file + dir_len, basename_start);
|
||||
}
|
||||
|
||||
// Append appropriate extension
|
||||
strcat(output_file, raw_pcm ? ".pcm" : ".wav");
|
||||
|
||||
if (verbose) {
|
||||
printf("Auto-generated output path: %s\n", output_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("%s\n", DECODER_VENDOR_STRING);
|
||||
printf("Input: %s\n", input_file);
|
||||
@@ -588,6 +672,11 @@ int main(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Write placeholder WAV header if not in raw PCM mode
|
||||
if (!raw_pcm) {
|
||||
write_wav_header(output, 0, TAD_CHANNELS, TAD_SAMPLE_RATE, 8);
|
||||
}
|
||||
|
||||
// Decode chunks
|
||||
size_t offset = 0;
|
||||
size_t chunk_count = 0;
|
||||
@@ -629,13 +718,24 @@ int main(int argc, char *argv[]) {
|
||||
total_samples / (double)TAD_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
// Update WAV header with correct size if not in raw PCM mode
|
||||
if (!raw_pcm) {
|
||||
uint32_t data_size = total_samples * TAD_CHANNELS;
|
||||
fseek(output, 0, SEEK_SET);
|
||||
write_wav_header(output, data_size, TAD_CHANNELS, TAD_SAMPLE_RATE, 8);
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
free(input_data);
|
||||
free(chunk_output);
|
||||
fclose(output);
|
||||
|
||||
printf("Output written to: %s\n", output_file);
|
||||
printf("Format: PCMu8 stereo @ %d Hz\n", TAD_SAMPLE_RATE);
|
||||
if (raw_pcm) {
|
||||
printf("Format: PCMu8 stereo @ %d Hz (raw PCM)\n", TAD_SAMPLE_RATE);
|
||||
} else {
|
||||
printf("Format: WAV file (8-bit unsigned PCM, stereo @ %d Hz)\n", TAD_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -23,15 +23,15 @@ static const float TAD32_COEFF_SCALARS[] = {64.0f, 45.255f, 32.0f, 22.627f, 16.0
|
||||
// These weights are multiplied by quantiser_scale during quantization
|
||||
static const float BASE_QUANTISER_WEIGHTS[] = {
|
||||
1.0f, // LL (L9) - finest preservation
|
||||
1.111f, // H (L9)
|
||||
1.222f, // H (L8)
|
||||
1.333f, // H (L7)
|
||||
1.444f, // H (L6)
|
||||
1.556f, // H (L5)
|
||||
1.667f, // H (L4)
|
||||
1.778f, // H (L3)
|
||||
1.889f, // H (L2)
|
||||
2.0f // H (L1) - coarsest quantization
|
||||
1.0f, // H (L9)
|
||||
1.0f, // H (L8)
|
||||
1.0f, // H (L7)
|
||||
1.0f, // H (L6)
|
||||
1.1f, // H (L5)
|
||||
1.2f, // H (L4)
|
||||
1.3f, // H (L3)
|
||||
1.4f, // H (L2)
|
||||
1.5f // H (L1) - coarsest quantization
|
||||
};
|
||||
|
||||
// Forward declarations for internal functions
|
||||
@@ -223,9 +223,9 @@ static void compress_gamma(float *left, float *right, size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
// encode(x) = sign(x) * |x|^γ where γ=0.5
|
||||
float x = left[i];
|
||||
left[i] = signum(x) * sqrtf(fabsf(x));
|
||||
left[i] = signum(x) * powf(fabsf(x), 1.0f / 1.4142f);
|
||||
float y = right[i];
|
||||
right[i] = signum(y) * sqrtf(fabsf(y));
|
||||
right[i] = signum(y) * powf(fabsf(y), 1.0f / 1.4142f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ static void generate_random_filename(char *filename) {
|
||||
//=============================================================================
|
||||
|
||||
static void print_usage(const char *prog_name) {
|
||||
printf("Usage: %s -i <input> -o <output> [options]\n", prog_name);
|
||||
printf("Usage: %s -i <input> [options]\n", prog_name);
|
||||
printf("Options:\n");
|
||||
printf(" -i <file> Input audio file (any format supported by FFmpeg)\n");
|
||||
printf(" -o <file> Output TAD32 file\n");
|
||||
printf(" -o <file> Output TAD32 file (optional, auto-generated as input.qN.tad)\n");
|
||||
printf(" -q <bits> Quantization bits (default: 7, range: 4-8)\n");
|
||||
printf(" Higher = more precision, larger files\n");
|
||||
printf(" -s <scale> Quantiser scaling factor (default: 1.0, range: 0.5-4.0)\n");
|
||||
@@ -119,12 +119,47 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_file || !output_file) {
|
||||
fprintf(stderr, "Error: Input and output files are required\n");
|
||||
if (!input_file) {
|
||||
fprintf(stderr, "Error: Input file is required\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Generate output filename if not provided
|
||||
if (!output_file) {
|
||||
// Allocate space for output filename
|
||||
size_t input_len = strlen(input_file);
|
||||
output_file = malloc(input_len + 32); // Extra space for .qNN.tad
|
||||
|
||||
// Find the last directory separator
|
||||
const char *basename_start = strrchr(input_file, '/');
|
||||
if (!basename_start) basename_start = strrchr(input_file, '\\');
|
||||
basename_start = basename_start ? basename_start + 1 : input_file;
|
||||
|
||||
// Copy directory part
|
||||
size_t dir_len = basename_start - input_file;
|
||||
strncpy(output_file, input_file, dir_len);
|
||||
|
||||
// Find the extension (last dot after basename)
|
||||
const char *ext = strrchr(basename_start, '.');
|
||||
if (ext && ext > basename_start) {
|
||||
// Copy basename without extension
|
||||
size_t name_len = ext - basename_start;
|
||||
strncpy(output_file + dir_len, basename_start, name_len);
|
||||
output_file[dir_len + name_len] = '\0';
|
||||
} else {
|
||||
// No extension, copy entire basename
|
||||
strcpy(output_file + dir_len, basename_start);
|
||||
}
|
||||
|
||||
// Append .qNN.tad
|
||||
sprintf(output_file + strlen(output_file), ".q%d.tad", max_index);
|
||||
|
||||
if (verbose) {
|
||||
printf("Auto-generated output path: %s\n", output_file);
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
printf("%s\n", ENCODER_VENDOR_STRING);
|
||||
printf("Input: %s\n", input_file);
|
||||
|
||||
Reference in New Issue
Block a user