mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-31 12:23:44 +01:00
Improve bitrate calculations for complex .txtp
This commit is contained in:
parent
84200c4cb9
commit
157c50f2e1
120
src/vgmstream.c
120
src/vgmstream.c
@ -5,6 +5,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include "vgmstream.h"
|
#include "vgmstream.h"
|
||||||
#include "meta/meta.h"
|
#include "meta/meta.h"
|
||||||
#include "layout/layout.h"
|
#include "layout/layout.h"
|
||||||
@ -1330,6 +1331,33 @@ fail:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************/
|
||||||
|
/* BITRATE */
|
||||||
|
/*******************************************************************************/
|
||||||
|
#define BITRATE_FILES_MAX 128 /* arbitrary max, but +100 segments have been observed */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t hash[BITRATE_FILES_MAX]; /* already used streamfiles */
|
||||||
|
int subsong[BITRATE_FILES_MAX]; /* subsongs of those streamfiles (could be incorporated to the hash?) */
|
||||||
|
int count;
|
||||||
|
int count_max;
|
||||||
|
} bitrate_info_t;
|
||||||
|
|
||||||
|
static uint32_t hash_sf(STREAMFILE* sf) {
|
||||||
|
int i;
|
||||||
|
char path[PATH_LIMIT];
|
||||||
|
uint32_t hash = 2166136261;
|
||||||
|
|
||||||
|
get_streamfile_name(sf, path, sizeof(path));
|
||||||
|
|
||||||
|
/* our favorite garbo hash a.k.a FNV-1 32b */
|
||||||
|
for (i = 0; i < strlen(path); i++) {
|
||||||
|
char c = tolower(path[i]);
|
||||||
|
hash = (hash * 16777619) ^ (uint8_t)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
/* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */
|
/* average bitrate helper to get STREAMFILE for a channel, since some codecs may use their own */
|
||||||
static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* vgmstream, int channel) {
|
static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* vgmstream, int channel) {
|
||||||
|
|
||||||
@ -1368,18 +1396,18 @@ static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* v
|
|||||||
return vgmstream->ch[channel].streamfile;
|
return vgmstream->ch[channel].streamfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, int length_samples) {
|
static int get_vgmstream_file_bitrate_from_size(size_t size, int sample_rate, int32_t length_samples) {
|
||||||
if (sample_rate == 0 || length_samples == 0) return 0;
|
if (sample_rate == 0 || length_samples == 0) return 0;
|
||||||
if (length_samples < 100) return 0; /* ignore stupid bitrates caused by some segments */
|
if (length_samples < 100) return 0; /* ignore stupid bitrates caused by some segments */
|
||||||
return (int)((int64_t)size * 8 * sample_rate / length_samples);
|
return (int)((int64_t)size * 8 * sample_rate / length_samples);
|
||||||
}
|
}
|
||||||
static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* streamfile, int sample_rate, int length_samples) {
|
static int get_vgmstream_file_bitrate_from_streamfile(STREAMFILE* sf, int sample_rate, int32_t length_samples) {
|
||||||
if (streamfile == NULL) return 0;
|
if (sf == NULL) return 0;
|
||||||
return get_vgmstream_file_bitrate_from_size(get_streamfile_size(streamfile), sample_rate, length_samples);
|
return get_vgmstream_file_bitrate_from_size(get_streamfile_size(sf), sample_rate, length_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, STREAMFILE** streamfile_pointers, int *pointers_count, int pointers_max) {
|
static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t* br) {
|
||||||
int sub, ch;
|
int i, ch;
|
||||||
int bitrate = 0;
|
int bitrate = 0;
|
||||||
|
|
||||||
/* Recursively get bitrate and fill the list of streamfiles if needed (to filter),
|
/* Recursively get bitrate and fill the list of streamfiles if needed (to filter),
|
||||||
@ -1387,59 +1415,66 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, STREAMFILE** st
|
|||||||
*
|
*
|
||||||
* Because of how data, layers and segments can be combined it's possible to
|
* Because of how data, layers and segments can be combined it's possible to
|
||||||
* fool this in various ways; metas should report stream_size in complex cases
|
* fool this in various ways; metas should report stream_size in complex cases
|
||||||
* to get accurate bitrates (particularly for subsongs). */
|
* to get accurate bitrates (particularly for subsongs). An edge case is when
|
||||||
|
* segments use only a few samples from a full file (like Wwise transitions), bitrates
|
||||||
|
* become a bit high since its hard to detect only part of the file is needed. */
|
||||||
|
|
||||||
if (vgmstream->stream_size) {
|
if (vgmstream->layout_type == layout_segmented) {
|
||||||
bitrate = get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
|
|
||||||
}
|
|
||||||
else if (vgmstream->layout_type == layout_segmented) {
|
|
||||||
segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data;
|
segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data;
|
||||||
for (sub = 0; sub < data->segment_count; sub++) {
|
for (i = 0; i < data->segment_count; i++) {
|
||||||
bitrate += get_vgmstream_file_bitrate_main(data->segments[sub], streamfile_pointers, pointers_count, pointers_max);
|
bitrate += get_vgmstream_file_bitrate_main(data->segments[i], br);
|
||||||
}
|
}
|
||||||
bitrate = bitrate / data->segment_count;
|
|
||||||
}
|
}
|
||||||
else if (vgmstream->layout_type == layout_layered) {
|
else if (vgmstream->layout_type == layout_layered) {
|
||||||
layered_layout_data *data = vgmstream->layout_data;
|
layered_layout_data *data = vgmstream->layout_data;
|
||||||
for (sub = 0; sub < data->layer_count; sub++) {
|
for (i = 0; i < data->layer_count; i++) {
|
||||||
bitrate += get_vgmstream_file_bitrate_main(data->layers[sub], streamfile_pointers, pointers_count, pointers_max);
|
bitrate += get_vgmstream_file_bitrate_main(data->layers[i], br);
|
||||||
}
|
}
|
||||||
bitrate = bitrate / data->layer_count;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Add channel bitrate if streamfile hasn't been used before (comparing files
|
/* Add channel bitrate if streamfile hasn't been used before, so bitrate doesn't count repeats
|
||||||
* by absolute paths), so bitrate doesn't multiply when the same STREAMFILE is
|
* (like same STREAMFILE reopened per channel, also considering SFs may be wrapped). */
|
||||||
* reopened per channel, also skipping repeated pointers. */
|
|
||||||
char path_current[PATH_LIMIT];
|
|
||||||
char path_compare[PATH_LIMIT];
|
|
||||||
int is_unique = 1;
|
|
||||||
|
|
||||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||||
STREAMFILE* sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch);
|
uint32_t hash_cur;
|
||||||
if (!sf_cur) continue;
|
int subsong_cur;
|
||||||
get_streamfile_name(sf_cur, path_current, sizeof(path_current));
|
STREAMFILE* sf_cur;
|
||||||
|
int is_unique = 1; /* default to "no other SFs exist" */
|
||||||
|
|
||||||
for (sub = 0; sub < *pointers_count; sub++) {
|
/* compares paths (hashes for faster compares) + subsongs (same file + different subsong = "different" file) */
|
||||||
STREAMFILE* sf_cmp = streamfile_pointers[sub];
|
sf_cur = get_vgmstream_average_bitrate_channel_streamfile(vgmstream, ch);
|
||||||
if (!sf_cmp) continue;
|
if (!sf_cur) continue;
|
||||||
if (sf_cur == sf_cmp) {
|
|
||||||
is_unique = 0;
|
hash_cur = hash_sf(sf_cur);
|
||||||
break;
|
subsong_cur = vgmstream->stream_index;
|
||||||
}
|
|
||||||
get_streamfile_name(sf_cmp, path_compare, sizeof(path_compare));
|
for (i = 0; i < br->count; i++) {
|
||||||
if (strcmp(path_current, path_compare) == 0) {
|
uint32_t hash_cmp = br->hash[i];
|
||||||
|
int subsong_cmp = br->subsong[i];
|
||||||
|
|
||||||
|
if (hash_cur == hash_cmp && subsong_cur == subsong_cmp) {
|
||||||
is_unique = 0;
|
is_unique = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_unique) {
|
if (is_unique) {
|
||||||
if (*pointers_count >= pointers_max) goto fail;
|
if (br->count >= br->count_max) goto fail;
|
||||||
streamfile_pointers[*pointers_count] = sf_cur;
|
|
||||||
(*pointers_count)++;
|
|
||||||
|
|
||||||
|
br->hash[br->count] = hash_cur;
|
||||||
|
br->subsong[br->count] = subsong_cur;
|
||||||
|
|
||||||
|
br->count++;
|
||||||
|
|
||||||
|
if (vgmstream->stream_size) {
|
||||||
|
/* stream_size applies to both channels but should add once and detect repeats (for current subsong) */
|
||||||
|
bitrate += get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
|
||||||
|
}
|
||||||
|
else {
|
||||||
bitrate += get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
|
bitrate += get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1453,11 +1488,10 @@ fail:
|
|||||||
* it counts extra data like block headers and padding. While this can be surprising
|
* it counts extra data like block headers and padding. While this can be surprising
|
||||||
* sometimes (as it's often higher than common codec bitrates) it isn't wrong per se. */
|
* sometimes (as it's often higher than common codec bitrates) it isn't wrong per se. */
|
||||||
int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream) {
|
int get_vgmstream_average_bitrate(VGMSTREAM* vgmstream) {
|
||||||
const size_t pointers_max = 128; /* arbitrary max, but +100 segments have been observed */
|
bitrate_info_t br = {0};
|
||||||
STREAMFILE* streamfile_pointers[128]; /* list already used streamfiles */
|
br.count_max = BITRATE_FILES_MAX;
|
||||||
int pointers_count = 0;
|
|
||||||
|
|
||||||
return get_vgmstream_file_bitrate_main(vgmstream, streamfile_pointers, &pointers_count, pointers_max);
|
return get_vgmstream_file_bitrate_main(vgmstream, &br);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user