mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 06:50:20 +01:00
Tweak mixing and fix TXTP with "commands" failing in some cases
This commit is contained in:
parent
08e1666691
commit
dd42e99823
10
doc/TXTP.md
10
doc/TXTP.md
@ -14,7 +14,11 @@ fileN
|
||||
|
||||
mode = (mode) # "segments" is the default if not set
|
||||
```
|
||||
You can set commands to alter how files play (described later). Having a single file is ok too.
|
||||
You can set commands to alter how files play (described later). Having a single file is ok too, so are subdirs:
|
||||
```
|
||||
# set "subsong" command for single file inside subdir
|
||||
sounds/file#12
|
||||
```
|
||||
|
||||
|
||||
### Segments mode
|
||||
@ -83,9 +87,9 @@ You can set file commands by adding multiple `#(command)` after the name. `# (an
|
||||
**Super Robot Taisen OG Saga - Masou Kishin III - Pride of Justice (Vita)**: *bgm_12.txtp*
|
||||
```
|
||||
# select subsong 12
|
||||
bigfiles/bgm.sxd2#12 #relative paths are ok too for TXTP
|
||||
bgm.sxd2#12
|
||||
|
||||
#bigfiles/bgm.sxd2#s12 # "sN" is alt for subsong
|
||||
#bgm.sxd2#s12 # "sN" is alt for subsong
|
||||
|
||||
# single files loop normally by default
|
||||
# if loop segment is defined it forces a full loop (0..num_samples)
|
||||
|
@ -53,7 +53,7 @@ typedef struct {
|
||||
/* macros */
|
||||
int max;
|
||||
uint32_t mask;
|
||||
int overlap;
|
||||
char mode;
|
||||
} txtp_mix_data;
|
||||
#endif
|
||||
|
||||
@ -352,9 +352,9 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
|
||||
/* macro mixes */
|
||||
case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix.vol, mix.mask); break;
|
||||
case MACRO_TRACK: mixing_macro_track(vgmstream, mix.mask); break;
|
||||
case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.overlap); break;
|
||||
case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.mode); break;
|
||||
case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix.max); break;
|
||||
case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max); break;
|
||||
case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max, mix.mode); break;
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -712,15 +712,19 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
|
||||
{
|
||||
char *config;
|
||||
|
||||
/* find config start (filenames and config can contain multiple dots and #,
|
||||
* so this may be fooled by certain patterns of . and #) */
|
||||
config = strchr(filename, '.'); /* first dot (may be a false positive) */
|
||||
if (!config) /* extensionless */
|
||||
config = filename;
|
||||
config = strchr(config, '#'); /* next should be config */
|
||||
if (!config) /* no config */
|
||||
config = filename; //todo if no config just exit?
|
||||
|
||||
if (is_default) {
|
||||
config = filename; /* multiple commands without filename */
|
||||
}
|
||||
else {
|
||||
/* find config start (filenames and config can contain multiple dots and #,
|
||||
* so this may be fooled by certain patterns of . and #) */
|
||||
config = strchr(filename, '.'); /* first dot (may be a false positive) */
|
||||
if (!config) /* extensionless */
|
||||
config = filename;
|
||||
config = strchr(config, '#'); /* next should be config */
|
||||
if (!config) /* no config */
|
||||
config = filename; //todo if no config just exit?
|
||||
}
|
||||
|
||||
range_start = 0;
|
||||
range_end = 1;
|
||||
@ -936,6 +940,7 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
|
||||
|
||||
nm = get_double(config, &mix.vol);
|
||||
config += nm;
|
||||
|
||||
if (nm == 0) continue;
|
||||
|
||||
nm = get_mask(config, &mix.mask);
|
||||
@ -952,7 +957,9 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
|
||||
|
||||
add_mixing(&cfg, &mix, MACRO_TRACK);
|
||||
}
|
||||
else if (strcmp(command,"@layer") == 0 || strcmp(command,"@overlap") == 0) {
|
||||
else if (strcmp(command,"@layer-v") == 0 ||
|
||||
strcmp(command,"@layer-b") == 0 ||
|
||||
strcmp(command,"@layer-e") == 0) {
|
||||
txtp_mix_data mix = {0};
|
||||
|
||||
nm = get_int(config, &mix.max);
|
||||
@ -962,17 +969,22 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
|
||||
nm = get_mask(config, &mix.mask);
|
||||
config += nm;
|
||||
|
||||
mix.overlap = (strcmp(command,"@overlap") == 0);
|
||||
|
||||
mix.mode = command[7]; /* pass letter */
|
||||
add_mixing(&cfg, &mix, MACRO_LAYER);
|
||||
}
|
||||
else if (strcmp(command,"@crosslayer") == 0 || strcmp(command,"@crosstrack") == 0) {
|
||||
else if (strcmp(command,"@crosslayer-v") == 0 ||
|
||||
strcmp(command,"@crosslayer-b") == 0 ||
|
||||
strcmp(command,"@crosslayer-e") == 0 ||
|
||||
strcmp(command,"@crosstrack") == 0) {
|
||||
txtp_mix_data mix = {0};
|
||||
txtp_mix_t type;
|
||||
if (strcmp(command,"@crosstrack") == 0)
|
||||
if (strcmp(command,"@crosstrack") == 0) {
|
||||
type = MACRO_CROSSTRACK;
|
||||
else
|
||||
}
|
||||
else {
|
||||
type = MACRO_CROSSLAYER;
|
||||
mix.mode = command[12]; /* pass letter */
|
||||
}
|
||||
|
||||
nm = get_int(config, &mix.max);
|
||||
config += nm;
|
||||
|
76
src/mixing.c
76
src/mixing.c
@ -694,9 +694,9 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask) {
|
||||
}
|
||||
}
|
||||
|
||||
void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, int overlap) {
|
||||
void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode) {
|
||||
mixing_data *data = vgmstream->mixing_data;
|
||||
int current, ch, selected_channels;
|
||||
int current, ch, output_channels, selected_channels;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
@ -708,9 +708,12 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, int overla
|
||||
mask = ~mask;
|
||||
}
|
||||
|
||||
/* save before adding fake channels */
|
||||
output_channels = data->output_channels;
|
||||
|
||||
/* count possibly set channels */
|
||||
selected_channels = 0;
|
||||
for (ch = 0; ch < data->output_channels; ch++) {
|
||||
for (ch = 0; ch < output_channels; ch++) {
|
||||
selected_channels += (mask >> ch) & 1;
|
||||
}
|
||||
|
||||
@ -721,14 +724,27 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, int overla
|
||||
|
||||
/* add all layers in this order: ch0: 0, 0+N, 0+N*2 ... / ch1: 1, 1+N ... */
|
||||
current = 0;
|
||||
for (ch = 0; ch < data->output_channels; ch++) {
|
||||
for (ch = 0; ch < output_channels; ch++) {
|
||||
double volume = 1.0;
|
||||
|
||||
if (!((mask >> ch) & 1))
|
||||
continue;
|
||||
|
||||
/* adjust volume (layer = lower, for layered bgm, overlap = same, for layered vocals) */
|
||||
if (!overlap) {
|
||||
/* mode 'v': same volume for all layers (for layered vocals) */
|
||||
/* mode 'b': volume adjusted depending on layers (for layered bgm) */
|
||||
/* mode 'e': volume adjusted equally for all layers (for generic downmixing) */
|
||||
if (mode == 'b' && ch < max) {
|
||||
/* reduce a bit main channels (see below) */
|
||||
int channel_mixes = selected_channels / max;
|
||||
if (current < selected_channels % (channel_mixes * max)) /* may be simplified? */
|
||||
channel_mixes += 1;
|
||||
channel_mixes -= 1; /* better formula? */
|
||||
if (channel_mixes <= 0) /* ??? */
|
||||
channel_mixes = 1;
|
||||
|
||||
volume = 1 / sqrt(channel_mixes);
|
||||
}
|
||||
if ((mode == 'b' && ch >= max) || (mode == 'e')) {
|
||||
/* find how many will be mixed in current channel (earlier channels receive more
|
||||
* mixes than later ones, ex: selected 8ch + max 3ch: ch0=0+3+6, ch1=1+4+7, ch2=2+5) */
|
||||
int channel_mixes = selected_channels / max;
|
||||
@ -737,8 +753,9 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, int overla
|
||||
if (current < selected_channels % (channel_mixes * max)) /* may be simplified? */
|
||||
channel_mixes += 1;
|
||||
|
||||
volume = 1 / sqrt(channel_mixes); /* "power" add, good results most of the time */
|
||||
volume = 1 / sqrt(channel_mixes); /* "power" add */
|
||||
}
|
||||
//;VGM_LOG("MIX: layer ch=%i, cur=%i, v=%f\n", ch, current, volume);
|
||||
|
||||
mixing_push_add(vgmstream, current, max + ch, volume); /* ch adjusted considering upmixed channels */
|
||||
current++;
|
||||
@ -811,7 +828,7 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
|
||||
mixing_push_killmix(vgmstream, max);
|
||||
}
|
||||
|
||||
void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max) {
|
||||
void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) {
|
||||
mixing_data *data = vgmstream->mixing_data;
|
||||
int current, ch, layer, layer_ch, layer_num, loop, output_channels;
|
||||
int32_t change_pos, change_time;
|
||||
@ -835,25 +852,44 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max) {
|
||||
if (vgmstream->config_loop_count < layer_num)
|
||||
vgmstream->config_loop_count = layer_num;
|
||||
|
||||
/* must set fades to successively lower volume per loop for each layer
|
||||
/* mode 'v': constant volume
|
||||
* mode 'e': sets fades to successively lower/equalize volume per loop for each layer
|
||||
* (to keep final volume constant-ish), ex. 3 layers/loops, 2 max:
|
||||
* - layer0 (ch0+1): loop0 --[1.0]--, loop1 )=1.0..0.7, loop2 )=0.7..0.5, loop3 --[0.5/end]--
|
||||
* - layer1 (ch2+3): loop0 --[0.0]--, loop1 (=0.0..0.7, loop2 )=0.7..0.5, loop3 --[0.5/end]--
|
||||
* - layer2 (ch4+5): loop0 --[0.0]--, loop1 ---[0.0]--, loop2 (=0.0..0.5, loop3 --[0.5/end]--
|
||||
* mode 'b': similar but 1st layer (main) has higher/delayed volume:
|
||||
* - layer0 (ch0+1): loop0 --[1.0]--, loop1 )=1.0..1.0, loop2 )=1.0..0.7, loop3 --[0.7/end]--
|
||||
*/
|
||||
for (loop = 1; loop < layer_num; loop++) {
|
||||
double volume1 = 1 / sqrt(loop + 0);
|
||||
double volume2 = 1 / sqrt(loop + 1);
|
||||
double volume1 = 1.0;
|
||||
double volume2 = 1.0;
|
||||
|
||||
int loop_pre = vgmstream->loop_start_sample;
|
||||
int loop_samples = vgmstream->loop_end_sample - vgmstream->loop_start_sample;
|
||||
change_pos = loop_pre + loop_samples * loop;
|
||||
change_time = 10.0 * vgmstream->sample_rate; /* in secs */
|
||||
|
||||
if (mode == 'e') {
|
||||
volume1 = 1 / sqrt(loop + 0);
|
||||
volume2 = 1 / sqrt(loop + 1);
|
||||
}
|
||||
|
||||
ch = 0;
|
||||
for (layer = 0; layer < layer_num; layer++) {
|
||||
char type;
|
||||
|
||||
if (mode == 'b') {
|
||||
if (layer == 0) {
|
||||
volume1 = 1 / sqrt(loop - 1 <= 0 ? 1 : loop - 1);
|
||||
volume2 = 1 / sqrt(loop + 0);
|
||||
}
|
||||
else {
|
||||
volume1 = 1 / sqrt(loop + 0);
|
||||
volume2 = 1 / sqrt(loop + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (layer > loop) { /* not playing yet (volume is implicitly 0.0 from first fade in) */
|
||||
continue;
|
||||
} else if (layer == loop) { /* fades in for the first time */
|
||||
@ -863,6 +899,8 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max) {
|
||||
type = ')';
|
||||
}
|
||||
|
||||
//;VGM_LOG("MIX: loop=%i, layer %i, vol1=%f, vol2=%f\n", loop, layer, volume1, volume2);
|
||||
|
||||
for (layer_ch = 0; layer_ch < max; layer_ch++) {
|
||||
mixing_push_fade(vgmstream, ch + layer_ch, volume1, volume2, type, -1, change_pos, change_pos + change_time, -1);
|
||||
}
|
||||
@ -893,6 +931,16 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) {
|
||||
|
||||
if (!data) goto fail;
|
||||
|
||||
/* a bit wonky but eh... */
|
||||
if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) {
|
||||
vgmstream->channel_layout = 0;
|
||||
((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0;
|
||||
}
|
||||
|
||||
/* special value to not actually enable anything (used to query values) */
|
||||
if (max_sample_count <= 0)
|
||||
goto fail;
|
||||
|
||||
/* create or alter internal buffer */
|
||||
mixbuf_re = realloc(data->mixbuf, max_sample_count*data->mixing_channels*sizeof(float));
|
||||
if (!mixbuf_re) goto fail;
|
||||
@ -900,12 +948,6 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) {
|
||||
data->mixbuf = mixbuf_re;
|
||||
data->mixing_on = 1;
|
||||
|
||||
/* a bit wonky but eh... */
|
||||
if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) {
|
||||
vgmstream->channel_layout = 0;
|
||||
((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0;
|
||||
}
|
||||
|
||||
/* since data exists on its own memory and pointer is already set
|
||||
* there is no need to propagate to start_vgmstream */
|
||||
|
||||
|
@ -33,9 +33,9 @@ void mixing_push_fade(VGMSTREAM* vgmstream, int ch_dst, double vol_start, double
|
||||
|
||||
void mixing_macro_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask);
|
||||
void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask);
|
||||
void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, int overlap);
|
||||
void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode);
|
||||
void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max);
|
||||
void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max);
|
||||
void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode);
|
||||
|
||||
|
||||
#endif /* _MIXING_H_ */
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "vgmstream.h"
|
||||
#include "plugins.h"
|
||||
#ifdef VGMSTREAM_MIXING
|
||||
#include "mixing.h"
|
||||
#endif
|
||||
|
||||
#define VGMSTREAM_TAGS_LINE_MAX 2048
|
||||
|
||||
@ -225,7 +227,7 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef VGMSTREAM_MIXING
|
||||
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) {
|
||||
mixing_setup(vgmstream, max_sample_count);
|
||||
mixing_info(vgmstream, input_channels, output_channels);
|
||||
@ -240,7 +242,8 @@ void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) {
|
||||
// https://www.audiokinetic.com/library/edge/?source=Help&id=downmix_tables#tbl_mono
|
||||
// https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations
|
||||
|
||||
mixing_macro_layer(vgmstream, max_channels, 0, 0);
|
||||
mixing_macro_layer(vgmstream, max_channels, 0, 'e');
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
@ -83,6 +83,7 @@ void vgmstream_tags_close(VGMSTREAM_TAGS* tags);
|
||||
|
||||
/* Enables mixing effects, with max outbuf samples as a hint. Once active, plugin
|
||||
* must use returned input_channels to create outbuf and output_channels to output audio.
|
||||
* max_sample_count may be 0 if you only need to query values and not actually enable it.
|
||||
* Needs to be enabled last after adding effects. */
|
||||
void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels);
|
||||
|
||||
|
@ -2323,6 +2323,8 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
mixing_info(vgmstream, NULL, &output_channels);
|
||||
|
||||
if (output_channels != vgmstream->channels) {
|
||||
snprintf(temp,TEMPSIZE, "input channels: %d\n", vgmstream->channels); /* repeated but mainly for plugins */
|
||||
concatn(length,desc,temp);
|
||||
snprintf(temp,TEMPSIZE, "output channels: %d\n", output_channels);
|
||||
concatn(length,desc,temp);
|
||||
}
|
||||
|
@ -1202,7 +1202,7 @@ int winamp_InfoBox(const in_char *fn, HWND hwnd) {
|
||||
apply_config(infostream, &infoconfig);
|
||||
#ifdef VGMSTREAM_MIXING
|
||||
vgmstream_mixing_autodownmix(infostream, settings.downmix_channels);
|
||||
//vgmstream_mixing_enable(infostream, SAMPLE_BUFFER_SIZE, NULL, NULL);
|
||||
vgmstream_mixing_enable(infostream, 0, NULL, NULL);
|
||||
#endif
|
||||
|
||||
describe_vgmstream(infostream,description,description_size);
|
||||
|
Loading…
Reference in New Issue
Block a user