From a017033ba8c6a8c4effabb56ba8f1201b3aae7e3 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 26 Jul 2020 11:19:52 +0200 Subject: [PATCH] Handle config in segments/layouts --- src/layout/layered.c | 148 ++++++++++++++++++++++++++++-------- src/layout/segmented.c | 56 +++++++++----- src/meta/txtp.c | 167 ++++++++++++++++++++++++----------------- src/mixing.c | 10 ++- src/player.c | 114 ++++++++++++++++------------ src/vgmstream.c | 40 +++++++--- src/vgmstream.h | 10 +-- 7 files changed, 358 insertions(+), 187 deletions(-) diff --git a/src/layout/layered.c b/src/layout/layered.c index 2a59970b..5180a4fa 100644 --- a/src/layout/layered.c +++ b/src/layout/layered.c @@ -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; diff --git a/src/layout/segmented.c b/src/layout/segmented.c index 763a2d6b..792b11fd 100644 --- a/src/layout/segmented.c +++ b/src/layout/segmented.c @@ -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 */ diff --git a/src/meta/txtp.c b/src/meta/txtp.c index c84f1414..d4f6e07d 100644 --- a/src/meta/txtp.c +++ b/src/meta/txtp.c @@ -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, ¤t->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, ¤t->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 = ¤t->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(¤t->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"); diff --git a/src/mixing.c b/src/mixing.c index e7028d7c..df6d6c3e 100644 --- a/src/mixing.c +++ b/src/mixing.c @@ -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 diff --git a/src/player.c b/src/player.c index 9cf322e1..97c9e211 100644 --- a/src/player.c +++ b/src/player.c @@ -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 */ } /*****************************************************************************/ diff --git a/src/vgmstream.c b/src/vgmstream.c index 3d7f670d..213e4c36 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -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; diff --git a/src/vgmstream.h b/src/vgmstream.h index 42abf732..84fba01a 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -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