Handle config in segments/layouts

This commit is contained in:
bnnm 2020-07-26 11:19:52 +02:00
parent e2e1a3b20c
commit a017033ba8
7 changed files with 358 additions and 187 deletions

View File

@ -1,9 +1,8 @@
#include "layout.h"
#include "../vgmstream.h"
#include "../mixing.h"
#include "../plugins.h"
/* NOTE: if loop settings change the layered vgmstreams must be notified (preferably using vgmstream_force_loop) */
#define VGMSTREAM_MAX_LAYERS 255
#define VGMSTREAM_LAYER_SAMPLE_BUFFER 8192
@ -13,22 +12,53 @@
* with custom codecs and different number of channels, creating a single super-vgmstream.
* Usually combined with custom streamfiles to handle data interleaved in weird ways. */
void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) {
int samples_written = 0;
int samples_written = 0, loop_samples_skip = 0;
layered_layout_data* data = vgmstream->layout_data;
while (samples_written < sample_count) {
int samples_to_do = VGMSTREAM_LAYER_SAMPLE_BUFFER;
int samples_to_do;
int samples_this_block = VGMSTREAM_LAYER_SAMPLE_BUFFER;
int layer, ch = 0;
if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_written;
if (data->external_looping) {
/* normally each layer handles its own looping internally, except when using config
* were each layer is treated as a solid part, so loop is applied externally */
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
for (layer = 0; layer < data->layer_count; layer++) {
reset_vgmstream(data->layers[layer]);
//todo per-layer seeking instead of layout looping
}
loop_samples_skip = vgmstream->loop_start_sample;
vgmstream->current_sample = 0;
vgmstream->samples_into_block = 0;
continue;
}
samples_to_do = get_vgmstream_samples_to_do(vgmstream->num_samples, samples_this_block, vgmstream);
if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_written;
/* looping: discard until actual start */
if (loop_samples_skip > 0) {
if (samples_to_do > loop_samples_skip)
samples_to_do = loop_samples_skip;
}
}
else {
samples_to_do = samples_this_block;
if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_written;
}
/* decode all layers */
for (layer = 0; layer < data->layer_count; layer++) {
int s, layer_ch, layer_channels;
/* each layer will handle its own looping/mixing internally */
/* layers may have its own number of channels */
mixing_info(data->layers[layer], NULL, &layer_channels);
@ -37,6 +67,10 @@ void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM*
samples_to_do,
data->layers[layer]);
if (loop_samples_skip > 0) {
continue;
}
/* mix layer samples to main samples */
for (layer_ch = 0; layer_ch < layer_channels; layer_ch++) {
for (s = 0; s < samples_to_do; s++) {
@ -49,11 +83,24 @@ void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM*
}
}
samples_written += samples_to_do;
/* needed for info (ex. for mixing) */
vgmstream->current_sample = data->layers[0]->current_sample;
vgmstream->loop_count = data->layers[0]->loop_count;
//vgmstream->samples_into_block = 0; /* handled in each layer */
if (loop_samples_skip > 0) {
loop_samples_skip -= samples_to_do;
vgmstream->samples_into_block += samples_to_do;
continue;
}
if (data->external_looping) {
samples_written += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
}
else {
samples_written += samples_to_do;
vgmstream->current_sample = data->layers[0]->current_sample;
vgmstream->samples_into_block = 0; /* handled in each layer */
vgmstream->loop_count = data->layers[0]->loop_count;
}
}
}
@ -88,12 +135,12 @@ int setup_layout_layered(layered_layout_data* data) {
int layer_input_channels, layer_output_channels;
if (data->layers[i] == NULL) {
VGM_LOG("layered: no vgmstream in %i\n", i);
VGM_LOG("LAYERED: no vgmstream in %i\n", i);
goto fail;
}
if (data->layers[i]->num_samples <= 0) {
VGM_LOG("layered: no samples in %i\n", i);
VGM_LOG("LAYERED: no samples in %i\n", i);
goto fail;
}
@ -107,21 +154,25 @@ int setup_layout_layered(layered_layout_data* data) {
if (i > 0) {
/* a bit weird, but no matter */
if (data->layers[i]->sample_rate != data->layers[i-1]->sample_rate) {
VGM_LOG("layered: layer %i has different sample rate\n", i);
VGM_LOG("LAYERED: layer %i has different sample rate\n", i);
}
/* also weird */
if (data->layers[i]->coding_type != data->layers[i-1]->coding_type) {
VGM_LOG("layered: layer %i has different coding type\n", i);
VGM_LOG("LAYERED: layer %i has different coding type\n", i);
}
}
/* loops and other values could be mismatched but hopefully not */
/* loops and other values could be mismatched, but should be handled on allocate */
/* init mixing */
mixing_setup(data->layers[i], VGMSTREAM_LAYER_SAMPLE_BUFFER);
setup_vgmstream(data->layers[i]); /* final setup in case the VGMSTREAM was created manually */
/* allow config if set for fine-tuned parts (usually TXTP only) */
data->layers[i]->config_enabled = data->layers[i]->config.config_set;
mixing_setup(data->layers[i], VGMSTREAM_LAYER_SAMPLE_BUFFER); /* init mixing */
/* final setup in case the VGMSTREAM was created manually */
setup_vgmstream(data->layers[i]);
}
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
@ -168,33 +219,66 @@ void reset_layout_layered(layered_layout_data *data) {
}
/* helper for easier creation of layers */
VGMSTREAM *allocate_layered_vgmstream(layered_layout_data* data) {
VGMSTREAM *vgmstream = NULL;
int i, channels, loop_flag, num_samples;
VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data) {
VGMSTREAM* vgmstream = NULL;
int i, channels, loop_flag, sample_rate, external_looping;
int32_t num_samples, loop_start, loop_end;
int delta = 1024;
/* get data */
channels = data->output_channels;
loop_flag = 1;
num_samples = 0;
loop_flag = 1;
loop_start = data->layers[0]->loop_start_sample;
loop_end = data->layers[0]->loop_end_sample;
external_looping = 0;
sample_rate = 0;
for (i = 0; i < data->layer_count; i++) {
if (loop_flag && !data->layers[i]->loop_flag)
int32_t layer_samples = vgmstream_get_samples(data->layers[i]);
int layer_loop = data->layers[i]->loop_flag;
int32_t layer_loop_start = data->layers[i]->loop_start_sample;
int32_t layer_loop_end = data->layers[i]->loop_end_sample;
int layer_rate = data->layers[i]->sample_rate;
/* internal has own config (and maybe looping), looping now must be done on layout level
* (instead of on each layer, that is faster) */
if (data->layers[i]->config_enabled) {
loop_flag = 0;
if (num_samples < data->layers[i]->num_samples)
num_samples = data->layers[i]->num_samples;
layer_loop = 0;
external_looping = 1;
}
/* all layers should share loop pointsto consider looping enabled,
* but allow some leeway (ex. Dragalia Lost bgm+vocals ~12 samples) */
if (!layer_loop
|| !(loop_start >= layer_loop_start - delta && loop_start <= layer_loop_start + delta)
|| !(loop_end >= layer_loop_end - delta && loop_start <= layer_loop_end + delta)) {
loop_flag = 0;
loop_start = 0;
loop_end = 0;
}
if (num_samples < layer_samples) /* max */
num_samples = layer_samples;
if (sample_rate < layer_rate)
sample_rate = layer_rate;
}
data->external_looping = external_looping;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = data->layers[0]->meta_type;
vgmstream->sample_rate = data->layers[0]->sample_rate;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = data->layers[0]->loop_start_sample;
vgmstream->loop_end_sample = data->layers[0]->loop_end_sample;
vgmstream->coding_type = data->layers[0]->coding_type;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->meta_type = data->layers[0]->meta_type; /* info */
vgmstream->coding_type = data->layers[0]->coding_type; /* info */
vgmstream->layout_type = layout_layered;
vgmstream->layout_data = data;

View File

@ -1,6 +1,7 @@
#include "layout.h"
#include "../vgmstream.h"
#include "../mixing.h"
#include "../plugins.h"
#define VGMSTREAM_MAX_SEGMENTS 1024
#define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192
@ -15,7 +16,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
int use_internal_buffer = 0;
/* normally uses outbuf directly (faster) but could need internal buffer if downmixing */
/* normally uses outbuf directly (faster?) but could need internal buffer if downmixing */
if (vgmstream->channels != data->input_channels) {
use_internal_buffer = 1;
}
@ -23,7 +24,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
while (samples_written < sample_count) {
int samples_to_do;
int samples_this_segment = data->segments[data->current_segment]->num_samples;
int samples_this_segment = vgmstream_get_samples(data->segments[data->current_segment]);
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
int segment, loop_segment, total_samples;
@ -32,7 +33,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
loop_segment = 0;
total_samples = 0;
while (total_samples < vgmstream->num_samples) {
int32_t segment_samples = data->segments[loop_segment]->num_samples;
int32_t segment_samples = vgmstream_get_samples(data->segments[loop_segment]);
if (vgmstream->loop_current_sample >= total_samples &&
vgmstream->loop_current_sample < total_samples + segment_samples) {
@ -44,7 +45,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
}
if (loop_segment == data->segment_count) {
VGM_LOG("segmented_layout: can't find loop segment\n");
VGM_LOG("SEGMENTED: can't find loop segment\n");
loop_segment = 0;
}
@ -65,7 +66,7 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
if (samples_to_do > VGMSTREAM_SEGMENT_SAMPLE_BUFFER /*&& use_internal_buffer*/) /* always for fade/etc mixes */
samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER;
/* segment looping: discard until actual start */
/* looping: discard until actual start */
if (loop_samples_skip > 0) {
if (samples_to_do > loop_samples_skip)
samples_to_do = loop_samples_skip;
@ -136,22 +137,27 @@ int setup_layout_segmented(segmented_layout_data* data) {
for (i = 0; i < data->segment_count; i++) {
int segment_input_channels, segment_output_channels;
/* allow config if set for fine-tuned parts (usually TXTP only) */
data->segments[i]->config_enabled = data->segments[i]->config.config_set;
if (data->segments[i] == NULL) {
VGM_LOG("segmented: no vgmstream in segment %i\n", i);
VGM_LOG("SEGMENTED: no vgmstream in segment %i\n", i);
goto fail;
}
if (data->segments[i]->num_samples <= 0) {
VGM_LOG("segmented: no samples in segment %i\n", i);
VGM_LOG("SEGMENTED: no samples in segment %i\n", i);
goto fail;
}
/* disable so that looping is controlled by render_vgmstream_segmented */
if (data->segments[i]->loop_flag != 0) {
VGM_LOG("segmented: segment %i is looped\n", i);
data->segments[i]->loop_flag = 0;
VGM_LOG("SEGMENTED: segment %i is looped\n", i);
/* config allows internal loops */
if (!data->segments[i]->config_enabled) {
data->segments[i]->loop_flag = 0;
}
}
/* different segments may have different input channels, though output should be
@ -167,13 +173,13 @@ int setup_layout_segmented(segmented_layout_data* data) {
mixing_info(data->segments[i-1], NULL, &prev_output_channels);
if (segment_output_channels != prev_output_channels) {
VGM_LOG("segmented: segment %i has wrong channels %i vs prev channels %i\n", i, segment_output_channels, prev_output_channels);
VGM_LOG("SEGMENTED: segment %i has wrong channels %i vs prev channels %i\n", i, segment_output_channels, prev_output_channels);
goto fail;
}
/* a bit weird, but no matter */
if (data->segments[i]->sample_rate != data->segments[i-1]->sample_rate) {
VGM_LOG("segmented: segment %i has different sample rate\n", i);
VGM_LOG("SEGMENTED: segment %i has different sample rate\n", i);
}
/* perfectly acceptable */
@ -181,10 +187,11 @@ int setup_layout_segmented(segmented_layout_data* data) {
// goto fail;
}
/* init mixing */
mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER);
setup_vgmstream(data->segments[i]); /* final setup in case the VGMSTREAM was created manually */
mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER); /* init mixing */
/* final setup in case the VGMSTREAM was created manually */
setup_vgmstream(data->segments[i]);
}
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
@ -242,21 +249,27 @@ void reset_layout_segmented(segmented_layout_data* data) {
}
/* helper for easier creation of segments */
VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment) {
VGMSTREAM *vgmstream = NULL;
VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment) {
VGMSTREAM* vgmstream = NULL;
int channel_layout;
int i, num_samples, loop_start, loop_end;
int i, sample_rate;
int32_t num_samples, loop_start, loop_end;
/* save data */
channel_layout = data->segments[0]->channel_layout;
num_samples = 0;
loop_start = 0;
loop_end = 0;
sample_rate = 0;
for (i = 0; i < data->segment_count; i++) {
/* needs get_samples since element may use play settings */
int32_t segment_samples = vgmstream_get_samples(data->segments[i]);
int segment_rate = data->segments[i]->sample_rate;
if (loop_flag && i == loop_start_segment)
loop_start = num_samples;
num_samples += data->segments[i]->num_samples;
num_samples += segment_samples;
if (loop_flag && i == loop_end_segment)
loop_end = num_samples;
@ -264,6 +277,9 @@ VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_fl
/* inherit first segment's layout but only if all segments' layout match */
if (channel_layout != 0 && channel_layout != data->segments[i]->channel_layout)
channel_layout = 0;
if (sample_rate < segment_rate)
sample_rate = segment_rate;
}
/* respect loop_flag even when no loop_end found as it's possible file loops are set outside */

View File

@ -2,6 +2,7 @@
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../mixing.h"
#include "../plugins.h"
#define TXTP_LINE_MAX 1024
@ -506,6 +507,10 @@ static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int*
}
static void copy_config(play_config_t* dst, play_config_t* src) {
if (!src->config_set)
return;
dst->config_set = 1;
copy_flag(&dst->play_forever, &src->play_forever);
copy_flag(&dst->ignore_fade, &src->ignore_fade);
copy_flag(&dst->force_loop, &src->force_loop);
@ -521,12 +526,24 @@ static void copy_config(play_config_t* dst, play_config_t* src) {
copy_time(&dst->body_time_set, &dst->body_time, &dst->body_time_s, &src->body_time_set, &src->body_time, &src->body_time_s);
}
#if 0
static void init_config(VGMSTREAM* vgmstream) {
play_config_t* cfg = &vgmstream->config;
//todo only on segmented/layered?
if (cfg->play_forever
cfg->loop_count_set || cfg->fade_time_set || cfg->fade_delay_set ||
cfg->pad_begin_set || cfg->pad_end_set || cfg->trim_begin_set || cfg->trim_end_set ||
cfg->body_time_set) {
VGM_LOG("setup!\n");
}
}
#endif
static void apply_settings(VGMSTREAM* vgmstream, txtp_entry* current) {
/* default play config */
copy_config(&vgmstream->config, &current->config);
/* other settings */
/* base settings */
if (current->sample_rate > 0) {
vgmstream->sample_rate = current->sample_rate;
}
@ -638,6 +655,13 @@ static void apply_settings(VGMSTREAM* vgmstream, txtp_entry* current) {
}
}
}
/* default play config (last after sample rate mods/mixing/etc) */
copy_config(&vgmstream->config, &current->config);
setup_state_vgmstream(vgmstream);
/* config is enabled in layouts or externally (for compatibility, since we don't know yet if this
* VGMSTREAM will part of a layout, or is enabled externally to not mess up plugins's calcs) */
}
@ -947,9 +971,7 @@ void add_mixing(txtp_entry* entry, txtp_mix_data* mix, txtp_mix_t command) {
entry->mixing_count++;
}
static void add_settings(txtp_entry* current, txtp_entry* base, const char* filename) {
play_config_t* src_cfg = &base->config;
play_config_t* dst_cfg = &current->config;
static void add_settings(txtp_entry* current, txtp_entry* entry, const char* filename) {
/* don't memcopy to allow list additions and ignore values not set, as current can be "default" settings */
//*current = *cfg;
@ -959,50 +981,50 @@ static void add_settings(txtp_entry* current, txtp_entry* base, const char* file
/* play config */
copy_config(dst_cfg, src_cfg);
copy_config(&current->config, &entry->config);
/* file settings */
if (base->subsong)
current->subsong = base->subsong;
if (entry->subsong)
current->subsong = entry->subsong;
if (base->sample_rate > 0)
current->sample_rate = base->sample_rate;
if (entry->sample_rate > 0)
current->sample_rate = entry->sample_rate;
if (base->channel_mask)
current->channel_mask = base->channel_mask;
if (entry->channel_mask)
current->channel_mask = entry->channel_mask;
if (base->loop_install_set) {
current->loop_install_set = base->loop_install_set;
current->loop_end_max = base->loop_end_max;
current->loop_start_sample = base->loop_start_sample;
current->loop_start_second = base->loop_start_second;
current->loop_end_sample = base->loop_end_sample;
current->loop_end_second = base->loop_end_second;
if (entry->loop_install_set) {
current->loop_install_set = entry->loop_install_set;
current->loop_end_max = entry->loop_end_max;
current->loop_start_sample = entry->loop_start_sample;
current->loop_start_second = entry->loop_start_second;
current->loop_end_sample = entry->loop_end_sample;
current->loop_end_second = entry->loop_end_second;
}
if (base->trim_set) {
current->trim_set = base->trim_set;
current->trim_second = base->trim_second;
current->trim_sample = base->trim_sample;
if (entry->trim_set) {
current->trim_set = entry->trim_set;
current->trim_second = entry->trim_second;
current->trim_sample = entry->trim_sample;
}
if (base->mixing_count > 0) {
if (entry->mixing_count > 0) {
int i;
for (i = 0; i < base->mixing_count; i++) {
current->mixing[current->mixing_count] = base->mixing[i];
for (i = 0; i < entry->mixing_count; i++) {
current->mixing[current->mixing_count] = entry->mixing[i];
current->mixing_count++;
}
}
}
static void parse_params(txtp_entry* cfg, char* params) {
static void parse_params(txtp_entry* entry, char* params) {
/* parse params: #(commands) */
int n, nc, nm, mc;
char command[TXTP_LINE_MAX] = {0};
play_config_t* tcfg = &cfg->config;
play_config_t* tcfg = &entry->config;
cfg->range_start = 0;
cfg->range_end = 1;
entry->range_start = 0;
entry->range_end = 1;
while (params != NULL) {
/* position in next #(command) */
@ -1024,8 +1046,8 @@ static void parse_params(txtp_entry* cfg, char* params) {
if (strcmp(command,"c") == 0) {
/* channel mask: file.ext#c1,2 = play channels 1,2 and mutes rest */
params += get_mask(params, &cfg->channel_mask);
//;VGM_LOG("TXTP: channel_mask ");{int i; for (i=0;i<16;i++)VGM_LOG("%i ",(cfg->channel_mask>>i)&1);}VGM_LOG("\n");
params += get_mask(params, &entry->channel_mask);
//;VGM_LOG("TXTP: channel_mask ");{int i; for (i=0;i<16;i++)VGM_LOG("%i ",(entry->channel_mask>>i)&1);}VGM_LOG("\n");
}
else if (strcmp(command,"m") == 0) {
/* channel mixing: file.ext#m(sub-command),(sub-command),etc */
@ -1044,7 +1066,7 @@ static void parse_params(txtp_entry* cfg, char* params) {
if (sscanf(params, " %d - %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) {
//;VGM_LOG("TXTP: mix %i-%i\n", mix.ch_dst, mix.ch_src);
add_mixing(cfg, &mix, MIX_SWAP); /* N-M: swaps M with N */
add_mixing(entry, &mix, MIX_SWAP); /* N-M: swaps M with N */
params += n;
continue;
}
@ -1052,14 +1074,14 @@ static void parse_params(txtp_entry* cfg, char* params) {
if ((sscanf(params, " %d + %d * %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0) ||
(sscanf(params, " %d + %d x %lf%n", &mix.ch_dst, &mix.ch_src, &mix.vol, &n) == 3 && n != 0)) {
//;VGM_LOG("TXTP: mix %i+%i*%f\n", mix.ch_dst, mix.ch_src, mix.vol);
add_mixing(cfg, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */
add_mixing(entry, &mix, MIX_ADD_VOLUME); /* N+M*V: mixes M*volume to N */
params += n;
continue;
}
if (sscanf(params, " %d + %d%n", &mix.ch_dst, &mix.ch_src, &n) == 2 && n != 0) {
//;VGM_LOG("TXTP: mix %i+%i\n", mix.ch_dst, mix.ch_src);
add_mixing(cfg, &mix, MIX_ADD); /* N+M: mixes M to N */
add_mixing(entry, &mix, MIX_ADD); /* N+M: mixes M to N */
params += n;
continue;
}
@ -1067,35 +1089,35 @@ static void parse_params(txtp_entry* cfg, char* params) {
if ((sscanf(params, " %d * %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0) ||
(sscanf(params, " %d x %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
//;VGM_LOG("TXTP: mix %i*%f\n", mix.ch_dst, mix.vol);
add_mixing(cfg, &mix, MIX_VOLUME); /* N*V: changes volume of N */
add_mixing(entry, &mix, MIX_VOLUME); /* N*V: changes volume of N */
params += n;
continue;
}
if ((sscanf(params, " %d = %lf%n", &mix.ch_dst, &mix.vol, &n) == 2 && n != 0)) {
//;VGM_LOG("TXTP: mix %i=%f\n", mix.ch_dst, mix.vol);
add_mixing(cfg, &mix, MIX_LIMIT); /* N=V: limits volume of N */
add_mixing(entry, &mix, MIX_LIMIT); /* N=V: limits volume of N */
params += n;
continue;
}
if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'D') {
//;VGM_LOG("TXTP: mix %iD\n", mix.ch_dst);
add_mixing(cfg, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */
add_mixing(entry, &mix, MIX_KILLMIX); /* ND: downmix N and all following channels */
params += n;
continue;
}
if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'd') {
//;VGM_LOG("TXTP: mix %id\n", mix.ch_dst);
add_mixing(cfg, &mix, MIX_DOWNMIX);/* Nd: downmix N only */
add_mixing(entry, &mix, MIX_DOWNMIX);/* Nd: downmix N only */
params += n;
continue;
}
if (sscanf(params, " %d%c%n", &mix.ch_dst, &cmd, &n) == 2 && n != 0 && cmd == 'u') {
//;VGM_LOG("TXTP: mix %iu\n", mix.ch_dst);
add_mixing(cfg, &mix, MIX_UPMIX); /* Nu: upmix N */
add_mixing(entry, &mix, MIX_UPMIX); /* Nu: upmix N */
params += n;
continue;
}
@ -1104,7 +1126,7 @@ static void parse_params(txtp_entry* cfg, char* params) {
//;VGM_LOG("TXTP: fade %d^%f~%f=%c@%f~%f+%f~%f\n",
// mix.ch_dst, mix.vol_start, mix.vol_end, mix.shape,
// mix.time_pre, mix.time_start, mix.time_end, mix.time_post);
add_mixing(cfg, &mix, MIX_FADE); /* N^V1~V2@T1~T2+T3~T4: fades volumes between positions */
add_mixing(entry, &mix, MIX_FADE); /* N^V1~V2@T1~T2+T3~T4: fades volumes between positions */
params += n;
continue;
}
@ -1119,15 +1141,15 @@ static void parse_params(txtp_entry* cfg, char* params) {
//todo also advance params?
if (sscanf(params, " %d ~ %d", &subsong_start, &subsong_end) == 2) {
if (subsong_start > 0 && subsong_end > 0) {
cfg->range_start = subsong_start-1;
cfg->range_end = subsong_end;
entry->range_start = subsong_start-1;
entry->range_end = subsong_end;
}
//;VGM_LOG("TXTP: subsong range %i~%i\n", range_start, range_end);
}
else if (sscanf(params, " %d", &subsong_start) == 1) {
if (subsong_start > 0) {
cfg->range_start = subsong_start-1;
cfg->range_end = subsong_start;
entry->range_start = subsong_start-1;
entry->range_end = subsong_start;
}
//;VGM_LOG("TXTP: subsong single %i-%i\n", range_start, range_end);
}
@ -1136,86 +1158,91 @@ static void parse_params(txtp_entry* cfg, char* params) {
}
}
/* play settings */
/* play config */
else if (strcmp(command,"i") == 0) {
params += get_bool(params, &tcfg->ignore_loop);
//;VGM_LOG("TXTP: ignore_loop=%i\n", tcfg->ignore_loop);
tcfg->config_set = 1;
}
else if (strcmp(command,"e") == 0) {
params += get_bool(params, &tcfg->force_loop);
//;VGM_LOG("TXTP: force_loop=%i\n", tcfg->force_loop);
tcfg->config_set = 1;
}
else if (strcmp(command,"E") == 0) {
params += get_bool(params, &tcfg->really_force_loop);
tcfg->config_set = 1;
}
else if (strcmp(command,"F") == 0) {
params += get_bool(params, &tcfg->ignore_fade);
//;VGM_LOG("TXTP: ignore_fade=%i\n", tcfg->ignore_fade);
tcfg->config_set = 1;
}
else if (strcmp(command,"L") == 0) {
params += get_bool(params, &tcfg->play_forever);
//;VGM_LOG("TXTP: play_forever=%i\n", tcfg->play_forever);
tcfg->config_set = 1;
}
else if (strcmp(command,"l") == 0) {
params += get_double(params, &tcfg->loop_count, &tcfg->loop_count_set);
if (tcfg->loop_count < 0)
tcfg->loop_count_set = 0;
//;VGM_LOG("TXTP: loop_count=%f\n", tcfg->loop_count);
tcfg->config_set = 1;
}
else if (strcmp(command,"f") == 0) {
params += get_double(params, &tcfg->fade_time, &tcfg->fade_time_set);
if (tcfg->fade_time < 0)
tcfg->fade_time_set = 0;
//;VGM_LOG("TXTP: fade_time=%f\n", tcfg->fade_time);
tcfg->config_set = 1;
}
else if (strcmp(command,"d") == 0) {
params += get_double(params, &tcfg->fade_delay, &tcfg->fade_delay_set);
if (tcfg->fade_delay < 0)
tcfg->fade_delay_set = 0;
//;VGM_LOG("TXTP: fade_delay %f\n", tcfg->fade_delay);
tcfg->config_set = 1;
}
else if (strcmp(command,"p") == 0) {
params += get_time_f(params, &tcfg->pad_begin_s, &tcfg->pad_begin, &tcfg->pad_begin_set);
tcfg->config_set = 1;
}
else if (strcmp(command,"P") == 0) {
params += get_time_f(params, &tcfg->pad_end_s, &tcfg->pad_end, &tcfg->pad_end_set);
tcfg->config_set = 1;
}
else if (strcmp(command,"r") == 0) {
params += get_time_f(params, &tcfg->trim_begin_s, &tcfg->trim_begin, &tcfg->trim_begin_set);
tcfg->config_set = 1;
}
else if (strcmp(command,"R") == 0) {
params += get_time_f(params, &tcfg->trim_end_s, &tcfg->trim_end, &tcfg->trim_end_set);
tcfg->config_set = 1;
}
else if (strcmp(command,"b") == 0) {
params += get_time_f(params, &tcfg->body_time_s, &tcfg->body_time, &tcfg->body_time_set);
//;VGM_LOG("TXTP: body_time %i, %f, %i\n", tcfg->body_time_set, tcfg->body_time_s, tcfg->body_time);
tcfg->config_set = 1;
}
/* other settings */
else if (strcmp(command,"h") == 0) {
params += get_int(params, &cfg->sample_rate);
params += get_int(params, &entry->sample_rate);
//;VGM_LOG("TXTP: sample_rate %i\n", cfg->sample_rate);
}
else if (strcmp(command,"I") == 0) {
n = get_time(params, &cfg->loop_start_second, &cfg->loop_start_sample);
n = get_time(params, &entry->loop_start_second, &entry->loop_start_sample);
if (n > 0) { /* first value must exist */
params += n;
n = get_time(params, &cfg->loop_end_second, &cfg->loop_end_sample);
n = get_time(params, &entry->loop_end_second, &entry->loop_end_sample);
if (n == 0) { /* second value is optional */
cfg->loop_end_max = 1;
entry->loop_end_max = 1;
}
params += n;
cfg->loop_install_set = 1;
entry->loop_install_set = 1;
}
//;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", cfg->loop_install, cfg->loop_end_max,
// cfg->loop_start_sample, cfg->loop_end_sample, cfg->loop_start_second, cfg->loop_end_second);
//;VGM_LOG("TXTP: loop_install %i (max=%i): %i %i / %f %f\n", entry->loop_install, entry->loop_end_max,
// entry->loop_start_sample, entry->loop_end_sample, entry->loop_start_second, entry->loop_end_second);
}
else if (strcmp(command,"t") == 0) {
cfg->trim_set = get_time(params, &cfg->trim_second, &cfg->trim_sample);
//;VGM_LOG("TXTP: trim %i - %f / %i\n", cfg->trim_set, cfg->trim_second, cfg->trim_sample);
entry->trim_set = get_time(params, &entry->trim_second, &entry->trim_sample);
//;VGM_LOG("TXTP: trim %i - %f / %i\n", entry->trim_set, entry->trim_second, entry->trim_sample);
}
//todo cleanup
@ -1231,7 +1258,7 @@ static void parse_params(txtp_entry* cfg, char* params) {
nm = get_mask(params, &mix.mask);
params += nm;
add_mixing(cfg, &mix, MACRO_VOLUME);
add_mixing(entry, &mix, MACRO_VOLUME);
}
else if (strcmp(command,"@track") == 0 ||
strcmp(command,"C") == 0 ) {
@ -1241,7 +1268,7 @@ static void parse_params(txtp_entry* cfg, char* params) {
params += nm;
if (nm == 0) continue;
add_mixing(cfg, &mix, MACRO_TRACK);
add_mixing(entry, &mix, MACRO_TRACK);
}
else if (strcmp(command,"@layer-v") == 0 ||
strcmp(command,"@layer-b") == 0 ||
@ -1256,7 +1283,7 @@ static void parse_params(txtp_entry* cfg, char* params) {
params += nm;
mix.mode = command[7]; /* pass letter */
add_mixing(cfg, &mix, MACRO_LAYER);
add_mixing(entry, &mix, MACRO_LAYER);
}
else if (strcmp(command,"@crosslayer-v") == 0 ||
strcmp(command,"@crosslayer-b") == 0 ||
@ -1276,7 +1303,7 @@ static void parse_params(txtp_entry* cfg, char* params) {
params += nm;
if (nm == 0) continue;
add_mixing(cfg, &mix, type);
add_mixing(entry, &mix, type);
}
else if (strcmp(command,"@downmix") == 0) {
txtp_mix_data mix = {0};
@ -1286,7 +1313,7 @@ static void parse_params(txtp_entry* cfg, char* params) {
//params += nm;
//if (nm == 0) continue;
add_mixing(cfg, &mix, MACRO_DOWNMIX);
add_mixing(entry, &mix, MACRO_DOWNMIX);
}
else if (params[nc] == ' ') {
//;VGM_LOG("TXTP: comment\n");

View File

@ -807,8 +807,11 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
/* set loops to hear all track changes */
track_num = output_channels / max;
if (vgmstream->config.loop_count < track_num)
if (vgmstream->config.loop_count < track_num) {
vgmstream->config.loop_count = track_num;
vgmstream->config.loop_count_set = 1;
vgmstream->config.config_set = 1;
}
ch = 0;
for (track = 0; track < track_num; track++) {
@ -868,8 +871,11 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) {
/* set loops to hear all track changes */
layer_num = output_channels / 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_set = 1;
vgmstream->config.config_set = 1;
}
/* mode 'v': constant volume
* mode 'e': sets fades to successively lower/equalize volume per loop for each layer

View File

@ -6,15 +6,46 @@
int vgmstream_get_play_forever(VGMSTREAM* vgmstream) {
return vgmstream->config.play_forever;
}
int32_t vgmstream_get_samples(VGMSTREAM* vgmstream) {
if (!vgmstream->config_set)
if (!vgmstream->config_enabled || !vgmstream->config.config_set)
return vgmstream->num_samples;
return vgmstream->pstate.play_duration;
}
/*****************************************************************************/
static void setup_state_vgmstream(VGMSTREAM* vgmstream) {
static void setup_state_modifiers(VGMSTREAM* vgmstream) {
play_config_t* pc = &vgmstream->config;
/* apply final config */
if (pc->really_force_loop) {
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
}
if (pc->force_loop && !vgmstream->loop_flag) {
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
}
if (pc->ignore_loop) {
vgmstream_force_loop(vgmstream, 0, 0,0);
}
if (!vgmstream->loop_flag) {
pc->play_forever = 0;
}
if (pc->play_forever) {
pc->ignore_fade = 0;
}
/* loop N times, but also play stream end instead of fading out */
if (pc->ignore_fade) {
vgmstream_set_loop_target(vgmstream, (int)pc->loop_count);
pc->fade_time = 0;
pc->fade_delay = 0;
}
}
static void setup_state_processing(VGMSTREAM* vgmstream) {
play_state_t* ps = &vgmstream->pstate;
play_config_t* pc = &vgmstream->config;
double sample_rate = vgmstream->sample_rate;
@ -44,13 +75,17 @@ static void setup_state_vgmstream(VGMSTREAM* vgmstream) {
ps->body_left += pc->body_time; /* whether it loops or not */
}
else if (vgmstream->loop_flag) {
double loop_count = 1.0;
if (pc->loop_count_set) /* may set 0.0 on purpose I guess */
loop_count = pc->loop_count;
ps->body_left += vgmstream->loop_start_sample;
if (pc->ignore_fade) {
ps->body_left += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)pc->loop_count;
ps->body_left += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)loop_count;
ps->body_left += (vgmstream->num_samples - vgmstream->loop_end_sample);
}
else {
ps->body_left += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * pc->loop_count;
ps->body_left += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count;
}
}
else {
@ -99,15 +134,13 @@ static void setup_state_vgmstream(VGMSTREAM* vgmstream) {
ps->output_channels = vgmstream->channels;
}
static void load_player_config(VGMSTREAM* vgmstream, play_config_t* def, vgmstream_cfg_t* vcfg) {
def->play_forever = vcfg->play_forever;
def->ignore_loop = vcfg->ignore_loop;
def->force_loop = vcfg->force_loop;
def->really_force_loop = vcfg->really_force_loop;
def->ignore_fade = vcfg->ignore_fade;
def->loop_count = vcfg->loop_times; //todo loop times
def->fade_delay = vcfg->fade_delay;
def->fade_time = vcfg->fade_period; //todo loop period
void setup_state_vgmstream(VGMSTREAM* vgmstream) {
if (!vgmstream->config.config_set)
return;
setup_state_modifiers(vgmstream);
setup_state_processing(vgmstream);
setup_vgmstream(vgmstream); /* save current config for reset */
}
@ -119,7 +152,9 @@ static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int*
*dst_time_s = *src_time_s;
}
static void load_internal_config(VGMSTREAM* vgmstream, play_config_t* def, play_config_t* tcfg) {
//todo reuse in txtp?
static void load_default_config(play_config_t* def, play_config_t* tcfg) {
/* loop limit: txtp #L > txtp #l > player #L > player #l */
if (tcfg->play_forever) {
def->play_forever = 1;
@ -128,6 +163,7 @@ static void load_internal_config(VGMSTREAM* vgmstream, play_config_t* def, play_
if (tcfg->loop_count_set) {
def->ignore_loop = 0;
def->loop_count = tcfg->loop_count;
def->loop_count_set = 1;
if (!tcfg->play_forever)
def->play_forever = 0;
}
@ -167,6 +203,20 @@ static void load_internal_config(VGMSTREAM* vgmstream, play_config_t* def, play_
copy_time(&def->body_time_set, &def->body_time, &def->body_time_s, &tcfg->body_time_set, &tcfg->body_time, &tcfg->body_time_s);
}
static void load_player_config(play_config_t* def, vgmstream_cfg_t* vcfg) {
def->play_forever = vcfg->play_forever;
def->ignore_loop = vcfg->ignore_loop;
def->force_loop = vcfg->force_loop;
def->really_force_loop = vcfg->really_force_loop;
def->ignore_fade = vcfg->ignore_fade;
def->loop_count = vcfg->loop_times; //todo loop times
def->loop_count_set = 1;
def->fade_delay = vcfg->fade_delay;
def->fade_delay_set = 1;
def->fade_time = vcfg->fade_period; //todo loop period
def->fade_time_set = 1;
}
void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) {
play_config_t defs = {0};
@ -174,48 +224,20 @@ void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) {
play_config_t* tcfg = &vgmstream->config;
load_player_config(vgmstream, def, vcfg);
load_player_config(def, vcfg);
def->config_set = 1;
if (!vcfg->disable_config_override)
load_internal_config(vgmstream, def, tcfg);
load_default_config(def, tcfg);
/* apply final config */
if (def->really_force_loop) {
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
}
if (def->force_loop && !vgmstream->loop_flag) {
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
}
if (def->ignore_loop) {
vgmstream_force_loop(vgmstream, 0, 0,0);
}
/* remove non-compatible options */
if (!vcfg->allow_play_forever)
def->play_forever = 0;
if (!vgmstream->loop_flag) {
def->play_forever = 0;
}
if (def->play_forever) {
def->ignore_fade = 0;
}
/* loop N times, but also play stream end instead of fading out */
if (def->ignore_fade) {
vgmstream_set_loop_target(vgmstream, (int)def->loop_count);
def->fade_time = 0;
def->fade_delay = 0;
}
/* copy final config back */
*tcfg = *def;
vgmstream->config_set = 1;
vgmstream->config_enabled = def->config_set;
setup_state_vgmstream(vgmstream);
setup_vgmstream(vgmstream); /* save current config for reset */
}
/*****************************************************************************/

View File

@ -986,15 +986,19 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa
/* this requires a bit more messing with the VGMSTREAM than I'm comfortable with... */
if (loop_flag && !vgmstream->loop_flag && !vgmstream->loop_ch) {
vgmstream->loop_ch = calloc(vgmstream->channels,sizeof(VGMSTREAMCHANNEL));
vgmstream->loop_ch = calloc(vgmstream->channels, sizeof(VGMSTREAMCHANNEL));
if (!vgmstream->loop_ch) loop_flag = 0; /* ??? */
}
#if 0
/* allow in case loop_flag is re-enabled later */
else if (!loop_flag && vgmstream->loop_flag) {
free(vgmstream->loop_ch); /* not important though */
free(vgmstream->loop_ch);
vgmstream->loop_ch = NULL;
}
#endif
vgmstream->loop_flag = loop_flag;
if (loop_flag) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
@ -1009,9 +1013,12 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa
/* propagate changes to layouts that need them */
if (vgmstream->layout_type == layout_layered) {
int i;
layered_layout_data *data = vgmstream->layout_data;
layered_layout_data* data = vgmstream->layout_data;
/* layered loops using the internal VGMSTREAMs */
for (i = 0; i < data->layer_count; i++) {
vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample);
if (!data->layers[i]->config_enabled) /* only in simple mode */
vgmstream_force_loop(data->layers[i], loop_flag, loop_start_sample, loop_end_sample);
/* layer's force_loop also calls setup_vgmstream, no need to do it here */
}
}
@ -1166,9 +1173,11 @@ static int render_pad_begin(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_
void render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
int done;
int samples_done = 0;
sample_t* tmpbuf = buf;
/* no settings */
if (!vgmstream->config_set) {
/* simple mode with no settings */
if (!vgmstream->config_enabled) {
render_layout(buf, sample_count, vgmstream);
mix_vgmstream(buf, sample_count, vgmstream);
return;
@ -1182,22 +1191,22 @@ void render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream)
/* adds empty samples to buf */
if (vgmstream->pstate.pad_begin_left) {
done = render_pad_begin(vgmstream, buf, sample_count);
done = render_pad_begin(vgmstream, tmpbuf, sample_count);
samples_done += done;
sample_count -= done;
buf += done * vgmstream->pstate.output_channels;
tmpbuf += done * vgmstream->pstate.output_channels;
}
/* main decode */
{
done = render_layout(buf, sample_count, vgmstream);
done = render_layout(tmpbuf, sample_count, vgmstream);
samples_done += done;
mix_vgmstream(buf, done, vgmstream);
mix_vgmstream(tmpbuf, done, vgmstream);
}
/* simple fadeout */
if (vgmstream->pstate.fade_left && !vgmstream->config.play_forever) {
render_fade(vgmstream, buf, samples_done);
render_fade(vgmstream, tmpbuf, samples_done);
}
/* silence samples in buf */
//else if (vgmstream->pstate.pad_end_left) {
@ -1208,11 +1217,15 @@ void render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream)
vgmstream->pstate.play_position += samples_done;
if (vgmstream->pstate.play_position > vgmstream->pstate.play_duration) {
//int channels = vgmstream->pstate.output_channels;
//todo silence if position > end and not loop forever?
//if (!vgmstream->config.play_forever) {
// memset(...);
//}
vgmstream->pstate.play_position = vgmstream->pstate.play_duration;
//memset(buf, 0, samples_done * sizeof(sample_t) * channels);
}
}
@ -2448,6 +2461,7 @@ int vgmstream_do_loop(VGMSTREAM* vgmstream) {
vgmstream->current_block_samples = vgmstream->loop_block_samples;
vgmstream->current_block_offset = vgmstream->loop_block_offset;
vgmstream->next_block_offset = vgmstream->loop_next_block_offset;
//vgmstream->pstate = vgmstream->lstate; /* play state is applied over loops */
return 1; /* looped */
}
@ -2463,6 +2477,8 @@ int vgmstream_do_loop(VGMSTREAM* vgmstream) {
vgmstream->loop_block_samples = vgmstream->current_block_samples;
vgmstream->loop_block_offset = vgmstream->current_block_offset;
vgmstream->loop_next_block_offset = vgmstream->next_block_offset;
//vgmstream->lstate = vgmstream->pstate; /* play state is applied over loops */
vgmstream->hit_loop = 1;
}
@ -2628,7 +2644,7 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) {
concatn(length,desc,temp);
}
if (vgmstream->config_set) {
if (vgmstream->config_enabled) {
double time_mm, time_ss, seconds;
int32_t samples = vgmstream->pstate.play_duration;

View File

@ -780,6 +780,8 @@ typedef enum {
} mapping_t;
typedef struct {
int config_set; /* some of the mods below are set */
/* modifiers */
int play_forever;
int ignore_loop;
@ -964,15 +966,11 @@ typedef struct {
/* play config/state */
int config_set; /* config can be used */
int config_enabled; /* config can be used */
play_config_t config; /* player config (applied over decoding) */
play_state_t pstate; /* player state (applied over decoding) */
int loop_count; /* counter of complete loops (1=looped once) */
int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */
/* config must be set/allowed by the player, otherwise vgmstream behaves like a simple lib decoder
* (could always apply some config like begin trim/padding + modify get_vgmstream_samples, but
* external caller may read loops/samples manually and wouldn't expect this changed output),
* also segment/layer layouts may behave differently wheter set or not */
} VGMSTREAM;
@ -994,6 +992,7 @@ typedef struct {
sample_t* buffer;
int input_channels; /* internal buffer channels */
int output_channels; /* resulting channels (after mixing, if applied) */
int external_looping; /* don't loop using per-layer loops, but layout's own looping */
} layered_layout_data;
@ -1172,4 +1171,5 @@ void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_
void render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done);
void setup_state_vgmstream(VGMSTREAM* vgmstream);
#endif