text: /*
  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 neccessary 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
  