Tweak mixing and fix TXTP with "commands" failing in some cases

This commit is contained in:
bnnm 2019-03-23 22:42:07 +01:00
parent 08e1666691
commit dd42e99823
8 changed files with 107 additions and 43 deletions

View File

@ -14,7 +14,11 @@ fileN
mode = (mode) # "segments" is the default if not set 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 ### 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* **Super Robot Taisen OG Saga - Masou Kishin III - Pride of Justice (Vita)**: *bgm_12.txtp*
``` ```
# select subsong 12 # 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 # single files loop normally by default
# if loop segment is defined it forces a full loop (0..num_samples) # if loop segment is defined it forces a full loop (0..num_samples)

View File

@ -53,7 +53,7 @@ typedef struct {
/* macros */ /* macros */
int max; int max;
uint32_t mask; uint32_t mask;
int overlap; char mode;
} txtp_mix_data; } txtp_mix_data;
#endif #endif
@ -352,9 +352,9 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
/* macro mixes */ /* macro mixes */
case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix.vol, mix.mask); break; case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix.vol, mix.mask); break;
case MACRO_TRACK: mixing_macro_track(vgmstream, 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_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: default:
break; break;
@ -712,15 +712,19 @@ static int add_filename(txtp_header * txtp, char *filename, int is_default) {
{ {
char *config; char *config;
/* find config start (filenames and config can contain multiple dots and #, if (is_default) {
* so this may be fooled by certain patterns of . and #) */ config = filename; /* multiple commands without filename */
config = strchr(filename, '.'); /* first dot (may be a false positive) */ }
if (!config) /* extensionless */ else {
config = filename; /* find config start (filenames and config can contain multiple dots and #,
config = strchr(config, '#'); /* next should be config */ * so this may be fooled by certain patterns of . and #) */
if (!config) /* no config */ config = strchr(filename, '.'); /* first dot (may be a false positive) */
config = filename; //todo if no config just exit? 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_start = 0;
range_end = 1; 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); nm = get_double(config, &mix.vol);
config += nm; config += nm;
if (nm == 0) continue; if (nm == 0) continue;
nm = get_mask(config, &mix.mask); 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); 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}; txtp_mix_data mix = {0};
nm = get_int(config, &mix.max); 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); nm = get_mask(config, &mix.mask);
config += nm; config += nm;
mix.overlap = (strcmp(command,"@overlap") == 0); mix.mode = command[7]; /* pass letter */
add_mixing(&cfg, &mix, MACRO_LAYER); 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_data mix = {0};
txtp_mix_t type; txtp_mix_t type;
if (strcmp(command,"@crosstrack") == 0) if (strcmp(command,"@crosstrack") == 0) {
type = MACRO_CROSSTRACK; type = MACRO_CROSSTRACK;
else }
else {
type = MACRO_CROSSLAYER; type = MACRO_CROSSLAYER;
mix.mode = command[12]; /* pass letter */
}
nm = get_int(config, &mix.max); nm = get_int(config, &mix.max);
config += nm; config += nm;

View File

@ -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; mixing_data *data = vgmstream->mixing_data;
int current, ch, selected_channels; int current, ch, output_channels, selected_channels;
if (!data) if (!data)
return; return;
@ -708,9 +708,12 @@ void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, int overla
mask = ~mask; mask = ~mask;
} }
/* save before adding fake channels */
output_channels = data->output_channels;
/* count possibly set channels */ /* count possibly set channels */
selected_channels = 0; selected_channels = 0;
for (ch = 0; ch < data->output_channels; ch++) { for (ch = 0; ch < output_channels; ch++) {
selected_channels += (mask >> ch) & 1; 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 ... */ /* add all layers in this order: ch0: 0, 0+N, 0+N*2 ... / ch1: 1, 1+N ... */
current = 0; current = 0;
for (ch = 0; ch < data->output_channels; ch++) { for (ch = 0; ch < output_channels; ch++) {
double volume = 1.0; double volume = 1.0;
if (!((mask >> ch) & 1)) if (!((mask >> ch) & 1))
continue; continue;
/* adjust volume (layer = lower, for layered bgm, overlap = same, for layered vocals) */ /* mode 'v': same volume for all layers (for layered vocals) */
if (!overlap) { /* 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 /* 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) */ * 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; 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? */ if (current < selected_channels % (channel_mixes * max)) /* may be simplified? */
channel_mixes += 1; 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 */ mixing_push_add(vgmstream, current, max + ch, volume); /* ch adjusted considering upmixed channels */
current++; current++;
@ -811,7 +828,7 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
mixing_push_killmix(vgmstream, 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; mixing_data *data = vgmstream->mixing_data;
int current, ch, layer, layer_ch, layer_num, loop, output_channels; int current, ch, layer, layer_ch, layer_num, loop, output_channels;
int32_t change_pos, change_time; 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) if (vgmstream->config_loop_count < layer_num)
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: * (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]-- * - 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]-- * - 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]-- * - 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++) { for (loop = 1; loop < layer_num; loop++) {
double volume1 = 1 / sqrt(loop + 0); double volume1 = 1.0;
double volume2 = 1 / sqrt(loop + 1); double volume2 = 1.0;
int loop_pre = vgmstream->loop_start_sample; int loop_pre = vgmstream->loop_start_sample;
int loop_samples = vgmstream->loop_end_sample - vgmstream->loop_start_sample; int loop_samples = vgmstream->loop_end_sample - vgmstream->loop_start_sample;
change_pos = loop_pre + loop_samples * loop; change_pos = loop_pre + loop_samples * loop;
change_time = 10.0 * vgmstream->sample_rate; /* in secs */ change_time = 10.0 * vgmstream->sample_rate; /* in secs */
if (mode == 'e') {
volume1 = 1 / sqrt(loop + 0);
volume2 = 1 / sqrt(loop + 1);
}
ch = 0; ch = 0;
for (layer = 0; layer < layer_num; layer++) { for (layer = 0; layer < layer_num; layer++) {
char type; 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) */ if (layer > loop) { /* not playing yet (volume is implicitly 0.0 from first fade in) */
continue; continue;
} else if (layer == loop) { /* fades in for the first time */ } else if (layer == loop) { /* fades in for the first time */
@ -863,6 +899,8 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max) {
type = ')'; 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++) { 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); 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; 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 */ /* create or alter internal buffer */
mixbuf_re = realloc(data->mixbuf, max_sample_count*data->mixing_channels*sizeof(float)); mixbuf_re = realloc(data->mixbuf, max_sample_count*data->mixing_channels*sizeof(float));
if (!mixbuf_re) goto fail; if (!mixbuf_re) goto fail;
@ -900,12 +948,6 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) {
data->mixbuf = mixbuf_re; data->mixbuf = mixbuf_re;
data->mixing_on = 1; 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 /* since data exists on its own memory and pointer is already set
* there is no need to propagate to start_vgmstream */ * there is no need to propagate to start_vgmstream */

View File

@ -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_volume(VGMSTREAM* vgmstream, double volume, uint32_t mask);
void mixing_macro_track(VGMSTREAM* vgmstream, 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_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_ */ #endif /* _MIXING_H_ */

View File

@ -1,6 +1,8 @@
#include "vgmstream.h" #include "vgmstream.h"
#include "plugins.h" #include "plugins.h"
#ifdef VGMSTREAM_MIXING
#include "mixing.h" #include "mixing.h"
#endif
#define VGMSTREAM_TAGS_LINE_MAX 2048 #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) { void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) {
mixing_setup(vgmstream, max_sample_count); mixing_setup(vgmstream, max_sample_count);
mixing_info(vgmstream, input_channels, output_channels); 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=downmix_tables#tbl_mono
// https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations // 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; return;
} }
#endif

View File

@ -83,6 +83,7 @@ void vgmstream_tags_close(VGMSTREAM_TAGS* tags);
/* Enables mixing effects, with max outbuf samples as a hint. Once active, plugin /* 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. * 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. */ * 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); void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels);

View File

@ -2323,6 +2323,8 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
mixing_info(vgmstream, NULL, &output_channels); mixing_info(vgmstream, NULL, &output_channels);
if (output_channels != vgmstream->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); snprintf(temp,TEMPSIZE, "output channels: %d\n", output_channels);
concatn(length,desc,temp); concatn(length,desc,temp);
} }

View File

@ -1202,7 +1202,7 @@ int winamp_InfoBox(const in_char *fn, HWND hwnd) {
apply_config(infostream, &infoconfig); apply_config(infostream, &infoconfig);
#ifdef VGMSTREAM_MIXING #ifdef VGMSTREAM_MIXING
vgmstream_mixing_autodownmix(infostream, settings.downmix_channels); vgmstream_mixing_autodownmix(infostream, settings.downmix_channels);
//vgmstream_mixing_enable(infostream, SAMPLE_BUFFER_SIZE, NULL, NULL); vgmstream_mixing_enable(infostream, 0, NULL, NULL);
#endif #endif
describe_vgmstream(infostream,description,description_size); describe_vgmstream(infostream,description,description_size);