#include "ffmpeg_audio.h" #include #include #include #ifdef WIN32 #define fprintf fprintf_s #endif static void write_u16_le(FILE *f, uint16_t v) { fputc((int)(v & 0xff), f); fputc((int)((v >> 8) & 0xff), f); } static void write_u32_le(FILE *f, uint32_t v) { fputc((int)(v & 0xff), f); fputc((int)((v >> 8) & 0xff), f); fputc((int)((v >> 16) & 0xff), f); fputc((int)((v >> 24) & 0xff), f); } static int write_wav_header(FILE *f, int sample_rate, int channels, int bits_per_sample, uint32_t data_size) { const uint32_t byte_rate = (uint32_t)(sample_rate * channels * bits_per_sample / 8); const uint16_t block_align = (uint16_t)(channels * bits_per_sample / 8); fwrite("RIFF", 1, 4, f); write_u32_le(f, 36u + data_size); fwrite("WAVE", 1, 4, f); fwrite("fmt ", 1, 4, f); write_u32_le(f, 16); /* fmt chunk size */ write_u16_le(f, 1); /* PCM */ write_u16_le(f, (uint16_t)channels); write_u32_le(f, (uint32_t)sample_rate); write_u32_le(f, byte_rate); write_u16_le(f, block_align); write_u16_le(f, (uint16_t)bits_per_sample); fwrite("data", 1, 4, f); write_u32_le(f, data_size); return ferror(f) ? 0 : 1; } static int rewrite_wav_header(FILE *f, int sample_rate, int channels, int bits_per_sample, uint32_t data_size) { if (fseek(f, 0, SEEK_SET) != 0) { return 0; } return write_wav_header(f, sample_rate, channels, bits_per_sample, data_size); } static void print_if_present(const char *label, const char *value) { if (value && value[0]) { printf("%s: %s\n", label, value); } } int main(int argc, char **argv) { const char *infile; const char *outfile; fmpg_instance *fmpg = NULL; FILE *out = NULL; int sample_rate; int channels; int bits_per_sample; uint64_t total_written = 0; if (argc != 3) { fprintf(stderr, "usage: %s input-audio output.wav\n", argv[0]); return 1; } infile = argv[1]; outfile = argv[2]; fmpg = fmpg_init(); if (!fmpg) { fprintf(stderr, "fmpg_init failed\n"); return 1; } if (!fmpg_open_file(fmpg, infile)) { fprintf(stderr, "could not open input file: %s\n", infile); fmpg_free(fmpg); return 1; } sample_rate = fmpg_audio_sample_rate(fmpg); channels = fmpg_audio_channels(fmpg); bits_per_sample = fmpg_audio_bits_per_sample(fmpg); if (sample_rate <= 0 || channels <= 0 || bits_per_sample != 32) { fprintf(stderr, "unexpected audio format reported by decoder\n"); fmpg_free(fmpg); return 1; } out = fopen(outfile, "wb"); if (!out) { fprintf(stderr, "could not open output file: %s\n", outfile); fmpg_free(fmpg); return 1; } /* * The final WAV data size is only known after decoding. Write a temporary * header now and patch it after the decode loop. */ if (!write_wav_header(out, sample_rate, channels, bits_per_sample, 0)) { fprintf(stderr, "could not write WAV header\n"); fclose(out); fmpg_free(fmpg); return 1; } while (fmpg_decode_next(fmpg)) { const uint8_t *buf = fmpg_buffer(fmpg); const int size = fmpg_buffer_size(fmpg); if (buf && size > 0) { if (fwrite(buf, 1, (size_t)size, out) != (size_t)size) { fprintf(stderr, "write error\n"); fclose(out); fmpg_free(fmpg); return 1; } total_written += (uint64_t)size; } } if (total_written > 0xffffffffu) { fprintf(stderr, "warning: WAV data larger than 4GB; classic WAV header " "will overflow\n"); } if (!rewrite_wav_header(out, sample_rate, channels, bits_per_sample, (uint32_t)total_written)) { fprintf(stderr, "could not rewrite WAV header\n"); fclose(out); fmpg_free(fmpg); return 1; } fclose(out); printf("wrote: %s\n", outfile); printf("sample rate: %d\n", sample_rate); printf("channels: %d\n", channels); printf("sample bits: %d\n", bits_per_sample); printf("data bytes: %llu\n", (unsigned long long)total_written); printf("samples out: %lld\n", (long long)fmpg_sample_position(fmpg)); if (fmpg_duration_ms(fmpg) >= 0) { printf("duration ms: %lld\n", (long long)fmpg_duration_ms(fmpg)); } if (fmpg_duration_samples(fmpg) >= 0) { printf("duration smp:%lld\n", (long long)fmpg_duration_samples(fmpg)); } print_if_present("title", fmpg_file_title(fmpg)); print_if_present("artist", fmpg_file_author(fmpg)); print_if_present("album", fmpg_file_album(fmpg)); fmpg_free(fmpg); return 0; }