mirror of
https://github.com/zeldaret/oot.git
synced 2025-02-13 17:07:44 +00:00
ef329e633a
* [Audio 2/?] Extract audio samples to wav Co-authored-by: zelda2774 <69368340+zelda2774@users.noreply.github.com> * How * Hopefully fix warning I don't get locally * Pad default sample filenames, comment on the vadpcm frame encoder functions, other suggested changes * Small tweaks to above * Remove some obsolete code --------- Co-authored-by: zelda2774 <69368340+zelda2774@users.noreply.github.com>
209 lines
5.9 KiB
C
209 lines
5.9 KiB
C
/**
|
|
* SPDX-FileCopyrightText: Copyright (C) 2024 ZeldaRET
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "util.h"
|
|
|
|
#include "codec/codec.h"
|
|
#include "codec/uncompressed.h"
|
|
#include "codec/vadpcm.h"
|
|
|
|
#include "container/container.h"
|
|
#include "container/wav.h"
|
|
#include "container/aiff.h"
|
|
|
|
static const codec_spec codecs[] = {
|
|
{"pcm16", SAMPLE_TYPE_PCM16, 16, false, pcm16_enc_dec, pcm16_enc_dec},
|
|
{ "vadpcm", SAMPLE_TYPE_VADPCM, 9, true, vadpcm_dec, vadpcm_enc },
|
|
{ "vadpcm-half", SAMPLE_TYPE_VADPCM_HALF, 5, true, vadpcm_dec, vadpcm_enc },
|
|
// { "pcm8", SAMPLE_TYPE_PCM8, TODO, TODO, pcm8_dec, pcm8_enc },
|
|
};
|
|
|
|
static const container_spec containers[] = {
|
|
{".wav", wav_read, wav_write },
|
|
{ ".aiff", aiff_read, aiff_write},
|
|
{ ".aifc", aifc_read, aifc_write},
|
|
};
|
|
|
|
static bool
|
|
str_endswith(const char *str1, const char *str2)
|
|
{
|
|
size_t len1 = strlen(str1);
|
|
size_t len2 = strlen(str2);
|
|
|
|
if (len2 > len1)
|
|
return false;
|
|
return strequ(str1 + len1 - len2, str2);
|
|
}
|
|
|
|
NORETURN static void
|
|
help(const char *progname)
|
|
{
|
|
fprintf(stderr, "%s [--matching] out_codec_name in_path out_path\n", progname);
|
|
fprintf(stderr, "Supported codecs:\n");
|
|
fprintf(stderr, " pcm16\n");
|
|
fprintf(stderr, " vadpcm\n");
|
|
fprintf(stderr, " vadpcm-half\n");
|
|
fprintf(stderr, "Possible input/output formats with supported codecs, automatically selected by file extension:\n");
|
|
fprintf(stderr, " wav [pcm16]\n");
|
|
fprintf(stderr, " aiff [pcm16]\n");
|
|
fprintf(stderr, " aifc [vadpcm, vadpcm-half]\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
NORETURN static void
|
|
usage(const char *progname)
|
|
{
|
|
fprintf(stderr, "%s [--matching] out_codec_name in_path out_path\n", progname);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static const codec_spec *
|
|
codec_from_name(const char *name)
|
|
{
|
|
for (size_t i = 0; i < ARRAY_COUNT(codecs); i++) {
|
|
if (strequ(name, codecs[i].name))
|
|
return &codecs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const codec_spec *
|
|
codec_from_type(sample_data_type type)
|
|
{
|
|
for (size_t i = 0; i < ARRAY_COUNT(codecs); i++) {
|
|
if (type == codecs[i].type)
|
|
return &codecs[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static const container_spec *
|
|
container_from_name(const char *name)
|
|
{
|
|
for (size_t i = 0; i < ARRAY_COUNT(containers); i++) {
|
|
if (str_endswith(name, containers[i].fext))
|
|
return &containers[i];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
const char *progname = argv[0];
|
|
// Required arguments
|
|
const char *in_path = NULL;
|
|
const char *out_path = NULL;
|
|
const char *out_codec_name = NULL;
|
|
// Optional arguments
|
|
enc_dec_opts opts = {
|
|
.matching = false,
|
|
// VADPCM
|
|
.truncate = false,
|
|
.min_loop_length = 800,
|
|
.design.order = 2,
|
|
.design.bits = 2,
|
|
.design.refine_iters = 2,
|
|
.design.thresh = 10.0,
|
|
.design.frame_size = 16,
|
|
};
|
|
|
|
// parse args
|
|
|
|
#define arg_error(fmt, ...) \
|
|
do { \
|
|
fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
|
|
usage(progname); \
|
|
} while (0)
|
|
|
|
int argn = 0;
|
|
for (int i = 1; i < argc; i++) {
|
|
if (argv[i][0] == '-') {
|
|
// Optional args
|
|
|
|
if (strequ(argv[i], "--help")) {
|
|
help(progname);
|
|
} else if (strequ(argv[i], "--matching")) {
|
|
if (opts.matching)
|
|
arg_error("Received --matching option twice");
|
|
|
|
opts.matching = true;
|
|
continue;
|
|
}
|
|
arg_error("Unknown option \"%s\"", argv[i]);
|
|
} else {
|
|
// Required args
|
|
|
|
switch (argn) {
|
|
case 0:
|
|
out_codec_name = argv[i];
|
|
break;
|
|
case 1:
|
|
in_path = argv[i];
|
|
break;
|
|
case 2:
|
|
out_path = argv[i];
|
|
break;
|
|
default:
|
|
arg_error("Unknown positional argument \"%s\"", argv[i]);
|
|
break;
|
|
}
|
|
argn++;
|
|
}
|
|
}
|
|
if (argn != 3)
|
|
arg_error("Not enough positional arguments");
|
|
|
|
#undef arg_error
|
|
|
|
const container_spec *in_container = container_from_name(in_path);
|
|
if (in_container == NULL)
|
|
error("Unsupported input format");
|
|
|
|
const container_spec *out_container = container_from_name(out_path);
|
|
if (out_container == NULL)
|
|
error("Unsupported output format");
|
|
|
|
const codec_spec *out_codec = codec_from_name(out_codec_name);
|
|
if (out_codec == NULL)
|
|
error("Unrecognized output codec: \"%s\"", out_codec_name);
|
|
|
|
container_data ctnr;
|
|
|
|
// Read input from container
|
|
if (in_container->read(&ctnr, in_path, opts.matching))
|
|
error("Error reading input file");
|
|
|
|
// Determine input codec from input container automatically
|
|
const codec_spec *in_codec = codec_from_type(ctnr.data_type);
|
|
if (in_codec == NULL)
|
|
error("Unrecognized input codec: type=%d", ctnr.data_type);
|
|
|
|
// Decode to PCM16 (this does nothing if in_codec is PCM16)
|
|
if (in_codec->decode(&ctnr, in_codec, &opts))
|
|
error("Error in decoding");
|
|
|
|
// Encode to output (this does nothing if out_codec is PCM16)
|
|
if (out_codec->encode(&ctnr, out_codec, &opts))
|
|
error("Error in encoding");
|
|
|
|
// Write output to container
|
|
if (out_container->write(&ctnr, out_path, opts.matching))
|
|
error("Error reading output file");
|
|
|
|
container_destroy(&ctnr);
|
|
return EXIT_SUCCESS;
|
|
}
|