horror/thirdparty/miniaudio/tools/audioconverter/audioconverter.c

306 lines
9.3 KiB
C
Raw Permalink Normal View History

2024-06-10 17:48:14 +08:00
/*
USAGE: audioconverter [input file] [output file] [format] [channels] [rate]
EXAMPLES:
audioconverter my_file.flac my_file.wav
audioconverter my_file.flac my_file.wav f32 44100 linear --linear-order 8
*/
#define _CRT_SECURE_NO_WARNINGS /* For stb_vorbis' usage of fopen() instead of fopen_s(). */
#define STB_VORBIS_HEADER_ONLY
#include "../../extras/stb_vorbis.c" /* Enables Vorbis decoding. */
#define MA_NO_DEVICE_IO
#define MA_NO_THREADING
#define MINIAUDIO_IMPLEMENTATION
#include "../../miniaudio.h"
#include <stdio.h>
void print_usage()
{
printf("USAGE: audioconverter [input file] [output file] [format] [channels] [rate]\n");
printf(" [format] is optional and can be one of the following:\n");
printf(" u8 8-bit unsigned integer\n");
printf(" s16 16-bit signed integer\n");
printf(" s24 24-bit signed integer (tightly packed)\n");
printf(" s32 32-bit signed integer\n");
printf(" f32 32-bit floating point\n");
printf(" [channels] is optional and in the range of %d and %d\n", MA_MIN_CHANNELS, MA_MAX_CHANNELS);
printf(" [rate] is optional and in the range of %d and %d\n", ma_standard_sample_rate_min, ma_standard_sample_rate_max);
printf("\n");
printf("PARAMETERS:\n");
printf(" --linear-order [0..%d]\n", MA_MAX_FILTER_ORDER);
}
ma_result do_conversion(ma_decoder* pDecoder, ma_encoder* pEncoder)
{
ma_result result = MA_SUCCESS;
MA_ASSERT(pDecoder != NULL);
MA_ASSERT(pEncoder != NULL);
/*
All we do is read from the decoder and then write straight to the encoder. All of the necessary data conversion
will happen internally.
*/
for (;;) {
ma_uint8 pRawData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint64 framesReadThisIteration;
ma_uint64 framesToReadThisIteration;
framesToReadThisIteration = sizeof(pRawData) / ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
result = ma_decoder_read_pcm_frames(pDecoder, pRawData, framesToReadThisIteration, &framesReadThisIteration);
if (result != MA_SUCCESS) {
break; /* Reached the end, or an error occurred. */
}
/* At this point we have the raw data from the decoder. We now just need to write it to the encoder. */
ma_encoder_write_pcm_frames(pEncoder, pRawData, framesReadThisIteration, NULL);
/* Get out of the loop if we've reached the end. */
if (framesReadThisIteration < framesToReadThisIteration) {
break;
}
}
return result;
}
ma_bool32 is_number(const char* str)
{
if (str == NULL || str[0] == '\0') {
return MA_FALSE;
}
while (str[0] != '\0') {
if (str[0] < '0' || str[0] > '9') {
return MA_FALSE;
}
str += 1;
}
return MA_TRUE;
}
ma_bool32 try_parse_uint32_in_range(const char* str, ma_uint32* pValue, ma_uint32 lo, ma_uint32 hi)
{
ma_uint32 x;
if (!is_number(str)) {
return MA_FALSE; /* Not an integer. */
}
x = (ma_uint32)atoi(str);
if (x < lo || x > hi) {
return MA_FALSE; /* Out of range. */
}
if (pValue != NULL) {
*pValue = x;
}
return MA_TRUE;
}
ma_bool32 try_parse_format(const char* str, ma_format* pValue)
{
ma_format format;
/* */ if (strcmp(str, "u8") == 0) {
format = ma_format_u8;
} else if (strcmp(str, "s16") == 0) {
format = ma_format_s16;
} else if (strcmp(str, "s24") == 0) {
format = ma_format_s24;
} else if (strcmp(str, "s32") == 0) {
format = ma_format_s32;
} else if (strcmp(str, "f32") == 0) {
format = ma_format_f32;
} else {
return MA_FALSE; /* Not a format. */
}
if (pValue != NULL) {
*pValue = format;
}
return MA_TRUE;
}
ma_bool32 try_parse_channels(const char* str, ma_uint32* pValue)
{
return try_parse_uint32_in_range(str, pValue, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
}
ma_bool32 try_parse_sample_rate(const char* str, ma_uint32* pValue)
{
return try_parse_uint32_in_range(str, pValue, ma_standard_sample_rate_min, ma_standard_sample_rate_max);
}
ma_bool32 try_parse_resample_algorithm(const char* str, ma_resample_algorithm* pValue)
{
ma_resample_algorithm algorithm;
/* */ if (strcmp(str, "linear") == 0) {
algorithm = ma_resample_algorithm_linear;
} else {
return MA_FALSE; /* Not a valid algorithm */
}
if (pValue != NULL) {
*pValue = algorithm;
}
return MA_TRUE;
}
int main(int argc, char** argv)
{
ma_result result;
ma_decoder_config decoderConfig;
ma_decoder decoder;
ma_encoder_config encoderConfig;
ma_encoder encoder;
ma_encoding_format outputEncodingFormat;
ma_format format = ma_format_unknown;
ma_uint32 channels = 0;
ma_uint32 rate = 0;
ma_resample_algorithm resampleAlgorithm;
ma_uint32 linearOrder = 8;
int iarg;
const char* pOutputFilePath;
print_usage();
/* Print help if requested. */
if (argc == 2) {
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
print_usage();
return 0;
}
}
if (argc < 3) {
print_usage();
return -1;
}
resampleAlgorithm = ma_resample_algorithm_linear;
/*
The fourth and fifth arguments can be a format and/or rate specifier. It doesn't matter which order they are in as we can identify them by whether or
not it's a number. If it's a number we assume it's a sample rate, otherwise we assume it's a format specifier.
*/
for (iarg = 3; iarg < argc; iarg += 1) {
if (strcmp(argv[iarg], "--linear-order") == 0) {
iarg += 1;
if (iarg >= argc) {
break;
}
if (!try_parse_uint32_in_range(argv[iarg], &linearOrder, 0, 8)) {
printf("Expecting a number between 0 and %d for --linear-order.\n", MA_MAX_FILTER_ORDER);
return -1;
}
continue;
}
if (try_parse_resample_algorithm(argv[iarg], &resampleAlgorithm)) {
continue;
}
if (try_parse_format(argv[iarg], &format)) {
continue;
}
if (try_parse_channels(argv[iarg], &channels)) {
continue;
}
if (try_parse_sample_rate(argv[iarg], &rate)) {
continue;
}
/* Getting here means we have an unknown parameter. */
printf("Warning: Unknown parameter \"%s\"", argv[iarg]);
}
/* Initialize a decoder for the input file. */
decoderConfig = ma_decoder_config_init(format, channels, rate);
decoderConfig.resampling.algorithm = resampleAlgorithm;
decoderConfig.resampling.linear.lpfOrder = linearOrder;
result = ma_decoder_init_file(argv[1], &decoderConfig, &decoder);
if (result != MA_SUCCESS) {
printf("Failed to open input file. Check the file exists and the format is supported. Supported input formats:\n");
#if defined(dr_opus_h)
printf(" Opus\n");
#endif
#if defined(dr_mp3_h)
printf(" MP3\n");
#endif
#if defined(dr_flac_h)
printf(" FLAC\n");
#endif
#if defined(STB_VORBIS_INCLUDE_STB_VORBIS_H)
printf(" Vorbis\n");
#endif
#if defined(dr_wav_h)
printf(" WAV\n");
#endif
return (int)result;
}
pOutputFilePath = argv[2];
outputEncodingFormat = ma_encoding_format_wav; /* Wave by default in case we don't know the file extension. */
if (ma_path_extension_equal(pOutputFilePath, "wav")) {
outputEncodingFormat = ma_encoding_format_wav;
} else {
printf("Warning: Unknown file extension \"%s\". Encoding as WAV.\n", ma_path_extension(pOutputFilePath));
}
/* Initialize the encoder for the output file. */
encoderConfig = ma_encoder_config_init(outputEncodingFormat, decoder.outputFormat, decoder.outputChannels, decoder.outputSampleRate);
result = ma_encoder_init_file(pOutputFilePath, &encoderConfig, &encoder);
if (result != MA_SUCCESS) {
ma_decoder_uninit(&decoder);
printf("Failed to open output file. Check that the directory exists and that the file is not already opened by another process. %s", ma_result_description(result));
return -1;
}
/* We have our decoder and encoder ready, so now we can do the conversion. */
result = do_conversion(&decoder, &encoder);
/* Done. */
ma_encoder_uninit(&encoder);
ma_decoder_uninit(&decoder);
return (int)result;
}
/* stb_vorbis implementation must come after the implementation of miniaudio. */
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable:4100) /* unreferenced formal parameter */
#pragma warning(disable:4244) /* '=': conversion from '' to '', possible loss of data */
#pragma warning(disable:4245) /* '=': conversion from '' to '', signed/unsigned mismatch */
#pragma warning(disable:4456) /* declaration of '' hides previous local declaration */
#pragma warning(disable:4457) /* declaration of '' hides function parameter */
#pragma warning(disable:4701) /* potentially uninitialized local variable '' used */
#else
#endif
#undef STB_VORBIS_HEADER_ONLY
#include "../../extras/stb_vorbis.c"
#if defined(_MSC_VER) && !defined(__clang__)
#pragma warning(pop)
#else
#endif