mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-09-22 20:17:43 +00:00
145 lines
4.4 KiB
C++
145 lines
4.4 KiB
C++
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
static void* testMp3Scratch();
|
|
#define MINIMP3_ONLY_MP3
|
|
#define MINIMP3_NO_SIMD
|
|
#define MINIMP3_SCRATCH ((mp3dec_scratch_t*)testMp3Scratch())
|
|
#define MINIMP3_IMPLEMENTATION
|
|
#include "../lib/minimp3/minimp3.h"
|
|
|
|
static mp3dec_scratch_t g_scratch;
|
|
static void* testMp3Scratch() { return &g_scratch; }
|
|
|
|
struct DecodeBuffer {
|
|
uint8_t input[16 * 1024];
|
|
mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
|
|
};
|
|
|
|
static uint32_t readLe32(const uint8_t* value) {
|
|
return (uint32_t)value[0] | ((uint32_t)value[1] << 8) |
|
|
((uint32_t)value[2] << 16) | ((uint32_t)value[3] << 24);
|
|
}
|
|
|
|
static long mp3DataEnd(FILE* file) {
|
|
assert(fseek(file, 0, SEEK_END) == 0);
|
|
long end = ftell(file);
|
|
assert(end >= 0);
|
|
uint8_t footer[128];
|
|
if (end >= 128 && fseek(file, end - 128, SEEK_SET) == 0 &&
|
|
fread(footer, 1, 3, file) == 3 && !memcmp(footer, "TAG", 3)) {
|
|
end -= 128;
|
|
}
|
|
if (end >= 32 && fseek(file, end - 32, SEEK_SET) == 0 &&
|
|
fread(footer, 1, 32, file) == 32 && !memcmp(footer, "APETAGEX", 8)) {
|
|
const uint32_t version = readLe32(footer + 8);
|
|
const uint32_t tag_size = readLe32(footer + 12);
|
|
const uint32_t flags = readLe32(footer + 20);
|
|
const uint64_t total_size = (uint64_t)tag_size + ((flags & 0x80000000u) ? 32u : 0u);
|
|
if ((version == 1000 || version == 2000) && tag_size >= 32 && total_size <= (uint64_t)end)
|
|
end -= (long)total_size;
|
|
}
|
|
rewind(file);
|
|
return end;
|
|
}
|
|
|
|
static bool decodeFile(const char* path) {
|
|
FILE* file = fopen(path, "rb");
|
|
if (!file) {
|
|
fprintf(stderr, "%s: could not open\n", path);
|
|
return false;
|
|
}
|
|
const long data_end = mp3DataEnd(file);
|
|
if (data_end <= 0) { fclose(file); return false; }
|
|
|
|
DecodeBuffer buffer;
|
|
mp3dec_t decoder;
|
|
mp3dec_init(&decoder);
|
|
size_t input_start = 0, input_count = 0;
|
|
uint64_t samples_total = 0;
|
|
int frames = 0, sample_rate = 0, channels = 0, bitrate_changes = 0;
|
|
int last_bitrate = 0;
|
|
bool eof = false, ok = true;
|
|
|
|
while (ok) {
|
|
if (!eof && input_count < 4096) {
|
|
if (input_start && input_start + input_count + 4096 > sizeof buffer.input) {
|
|
memmove(buffer.input, buffer.input + input_start, input_count);
|
|
input_start = 0;
|
|
}
|
|
size_t room = sizeof buffer.input - (input_start + input_count);
|
|
const long position = ftell(file);
|
|
const size_t remaining = position < data_end ? (size_t)(data_end - position) : 0;
|
|
if (room > remaining) room = remaining;
|
|
const size_t got = fread(buffer.input + input_start + input_count, 1, room, file);
|
|
if (got > 0) {
|
|
input_count += got;
|
|
eof = ftell(file) >= data_end;
|
|
}
|
|
else if (ferror(file)) ok = false;
|
|
else eof = true;
|
|
}
|
|
|
|
if (!ok || !input_count) break;
|
|
|
|
mp3dec_frame_info_t info = {};
|
|
const int samples = mp3dec_decode_frame(&decoder, buffer.input + input_start,
|
|
(int)input_count, buffer.pcm, &info);
|
|
if (info.frame_bytes < 0 || (size_t)info.frame_bytes > input_count) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
if (info.frame_bytes > 0) {
|
|
input_start += (size_t)info.frame_bytes;
|
|
input_count -= (size_t)info.frame_bytes;
|
|
if (!input_count) input_start = 0;
|
|
} else if (eof) {
|
|
break;
|
|
} else if (input_start + input_count == sizeof buffer.input) {
|
|
ok = false;
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
if (samples <= 0) continue;
|
|
if (info.layer != 3 || (info.channels != 1 && info.channels != 2) ||
|
|
info.hz < 8000 || info.hz > 48000) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
if (!sample_rate) {
|
|
sample_rate = info.hz;
|
|
channels = info.channels;
|
|
} else if (sample_rate != info.hz) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
if (last_bitrate && last_bitrate != info.bitrate_kbps) ++bitrate_changes;
|
|
last_bitrate = info.bitrate_kbps;
|
|
samples_total += (uint64_t)samples;
|
|
++frames;
|
|
}
|
|
|
|
fclose(file);
|
|
if (!ok || frames == 0 || samples_total == 0) {
|
|
fprintf(stderr, "%s: decode failed after %d frames\n", path, frames);
|
|
return false;
|
|
}
|
|
printf("%s: %d frames, %llu samples/channel, %d Hz, %d channel(s), %d bitrate changes\n",
|
|
path, frames, (unsigned long long)samples_total, sample_rate, channels,
|
|
bitrate_changes);
|
|
return true;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
if (argc < 2) {
|
|
fprintf(stderr, "usage: %s file.mp3 [file.mp3 ...]\n", argv[0]);
|
|
return 2;
|
|
}
|
|
for (int i = 1; i < argc; ++i) assert(decodeFile(argv[i]));
|
|
return 0;
|
|
}
|