diff --git a/src/decode.c b/src/decode.c index a5603187..f252831d 100644 --- a/src/decode.c +++ b/src/decode.c @@ -1,6 +1,6 @@ #include "vgmstream.h" #include "decode.h" -//#include "layout/layout.h" +#include "layout/layout.h" #include "coding/coding.h" #include "mixing.h" #include "plugins.h" @@ -1423,7 +1423,7 @@ int vgmstream_do_loop(VGMSTREAM* vgmstream) { } } - /* prepare certain codecs' internal state for looping */ + /* loop codecs */ seek_codec(vgmstream); /* restore! */ @@ -1436,6 +1436,18 @@ int vgmstream_do_loop(VGMSTREAM* vgmstream) { vgmstream->next_block_offset = vgmstream->loop_next_block_offset; //vgmstream->pstate = vgmstream->lstate; /* play state is applied over loops */ + /* loop layouts (after restore, in case layout needs state manipulations) */ + switch(vgmstream->layout_type) { + case layout_segmented: + loop_layout_segmented(vgmstream, vgmstream->loop_current_sample); + break; + case layout_layered: + loop_layout_layered(vgmstream, vgmstream->loop_current_sample); + break; + default: + break; + } + return 1; /* looped */ } diff --git a/src/layout/layered.c b/src/layout/layered.c index ac5e3b87..2d282e7f 100644 --- a/src/layout/layered.c +++ b/src/layout/layered.c @@ -9,54 +9,34 @@ /* Decodes samples for layered streams. - * Similar to interleave layout, but decodec samples are mixed from complete vgmstreams, each - * with custom codecs and different number of channels, creating a single super-vgmstream. + * Similar to flat layout, but decoded vgmstream are mixed into a final buffer, each vgmstream + * may have different codecs and 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, loop_samples_skip = 0; + int samples_written = 0; layered_layout_data* data = vgmstream->layout_data; + int samples_per_frame, samples_this_block; + samples_per_frame = VGMSTREAM_LAYER_SAMPLE_BUFFER; + samples_this_block = vgmstream->num_samples; /* do all samples if possible */ while (samples_written < sample_count) { int samples_to_do; - int samples_this_block = VGMSTREAM_LAYER_SAMPLE_BUFFER; - int layer, ch = 0; + int layer, ch; - 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; + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + /* handle looping (loop_layout has been called below) */ + continue; } + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; + /* decode all layers */ + ch = 0; for (layer = 0; layer < data->layer_count; layer++) { int s, layer_ch, layer_channels; @@ -68,10 +48,6 @@ 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++) { @@ -84,25 +60,40 @@ void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM* } } - if (loop_samples_skip > 0) { - loop_samples_skip -= samples_to_do; - vgmstream->samples_into_block += samples_to_do; - continue; - } + + samples_written += samples_to_do; + vgmstream->current_sample += samples_to_do; + vgmstream->samples_into_block += samples_to_do; + } +} +void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample) { + int layer; + layered_layout_data* data = vgmstream->layout_data; + + + for (layer = 0; layer < data->layer_count; layer++) { if (data->external_looping) { - samples_written += samples_to_do; - vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block += samples_to_do; + /* looping is applied over resulting decode, as each layer is its own "solid" block with + * config and needs 'external' seeking */ + seek_vgmstream(data->layers[layer], loop_sample); } 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; + /* looping is aplied as internal loops. normally each layer does it automatically, but + * just calls do_loop manually to behave a bit more controlled, and so that manual + * calls to do_loop work (used in seek_vgmstream) */ + if (data->layers[layer]->loop_flag) { /* mixing looping and non-looping layers is allowed */ + data->layers[layer]->current_sample = data->layers[layer]->loop_end_sample; /* forces do loop */ + vgmstream_do_loop(data->layers[layer]); /* guaranteed to work should loop_layout be called */ + } } } + + /* could always call seek_vgmstream, but it's not optimized to loop non-config vgmstreams ATM */ + + vgmstream->current_sample = loop_sample; + vgmstream->samples_into_block = loop_sample; } diff --git a/src/layout/layout.h b/src/layout/layout.h index 86cc3460..2b6405dc 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -59,6 +59,7 @@ segmented_layout_data* init_layout_segmented(int segment_count); int setup_layout_segmented(segmented_layout_data* data); void free_layout_segmented(segmented_layout_data* data); void reset_layout_segmented(segmented_layout_data* data); +void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample); VGMSTREAM *allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment); void render_vgmstream_layered(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); @@ -66,6 +67,7 @@ layered_layout_data* init_layout_layered(int layer_count); int setup_layout_layered(layered_layout_data* data); void free_layout_layered(layered_layout_data* data); void reset_layout_layered(layered_layout_data* data); +void loop_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample); VGMSTREAM *allocate_layered_vgmstream(layered_layout_data* data); #endif diff --git a/src/layout/segmented.c b/src/layout/segmented.c index 2e7fe3a8..38ab5150 100644 --- a/src/layout/segmented.c +++ b/src/layout/segmented.c @@ -12,11 +12,10 @@ * Chains together sequential vgmstreams, for data divided into separate sections or files * (like one part for intro and other for loop segments, which may even use different codecs). */ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { - int samples_written = 0, loop_samples_skip = 0; + int samples_written = 0, samples_this_block; segmented_layout_data* data = vgmstream->layout_data; int use_internal_buffer = 0; - /* normally uses outbuf directly (faster?) but could need internal buffer if downmixing */ if (vgmstream->channels != data->input_channels) { use_internal_buffer = 1; @@ -27,81 +26,49 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA return; } + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); + + //VGM_LOG("segment decode start: cur=%i, this=%i, into=%i\n", data->current_segment, samples_this_block, vgmstream->samples_into_block); while (samples_written < sample_count) { int samples_to_do; - 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; + /* handle looping (loop_layout has been called below, changes segments/state) */ + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); + continue; + } - /* handle looping by finding loop segment and loop_start inside that segment */ - loop_segment = 0; - total_samples = 0; - while (total_samples < vgmstream->num_samples) { - int32_t segment_samples = vgmstream_get_samples(data->segments[loop_segment]); + /* detect segment change and restart (after loop, but before decode, to allow looping to kick in) */ + if (vgmstream->samples_into_block == samples_this_block) { + data->current_segment++; - if (vgmstream->loop_current_sample >= total_samples && - vgmstream->loop_current_sample < total_samples + segment_samples) { - loop_samples_skip = vgmstream->loop_current_sample - total_samples; - break; /* loop_start falls within loop_segment's samples */ - } - total_samples += segment_samples; - loop_segment++; + /* could happen on last segment trying to decode more samples */ + if (data->current_segment >= data->segment_count) { + VGM_LOG("SEGMENTED: wrong next segment\n"); + break; } - if (loop_segment == data->segment_count) { - VGM_LOG("SEGMENTED: can't find loop segment\n"); - loop_segment = 0; - } - - data->current_segment = loop_segment; - - /* loops can span multiple segments */ - for (segment = loop_segment; segment < data->segment_count; segment++) { - reset_vgmstream(data->segments[segment]); - } + /* in case of looping spanning multiple segments */ + reset_vgmstream(data->segments[data->current_segment]); + samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]); vgmstream->samples_into_block = 0; continue; } - samples_to_do = get_vgmstream_samples_to_do(samples_this_segment, sample_count, vgmstream); + + samples_to_do = get_vgmstream_samples_to_do(samples_this_block, sample_count, vgmstream); if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; if (samples_to_do > VGMSTREAM_SEGMENT_SAMPLE_BUFFER /*&& use_internal_buffer*/) /* always for fade/etc mixes */ samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER; - /* looping: discard until actual start */ - if (loop_samples_skip > 0) { - if (samples_to_do > loop_samples_skip) - samples_to_do = loop_samples_skip; - } - - /* detect segment change and restart */ - if (samples_to_do == 0) { - data->current_segment++; - /* could happen on last segment trying to decode more samples */ - if (data->current_segment >= data->segment_count) { - break; - } - reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; - continue; - } - render_vgmstream( use_internal_buffer ? - data->buffer : - &outbuf[samples_written * data->output_channels], + data->buffer : &outbuf[samples_written * data->output_channels], samples_to_do, data->segments[data->current_segment]); - if (loop_samples_skip > 0) { - loop_samples_skip -= samples_to_do; - vgmstream->samples_into_block += samples_to_do; - continue; - } - if (use_internal_buffer) { int s; for (s = 0; s < samples_to_do * data->output_channels; s++) { @@ -115,6 +82,33 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA } } +void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t loop_sample) { + int segment, total_samples; + segmented_layout_data* data = vgmstream->layout_data; + + segment = 0; + total_samples = 0; + while (total_samples < vgmstream->num_samples) { + int32_t segment_samples = vgmstream_get_samples(data->segments[segment]); + + /* find if loop falls within segment's samples */ + if (loop_sample >= total_samples && loop_sample < total_samples + segment_samples) { + int32_t loop_relative = loop_sample - total_samples; + + seek_vgmstream(data->segments[segment], loop_relative); + data->current_segment = segment; + vgmstream->samples_into_block = loop_relative; + break; + } + total_samples += segment_samples; + segment++; + } + + if (segment == data->segment_count) { + VGM_LOG("SEGMENTED: can't find loop segment\n"); + } +} + segmented_layout_data* init_layout_segmented(int segment_count) { segmented_layout_data* data = NULL; diff --git a/src/render.c b/src/render.c index 01314ffb..72a76130 100644 --- a/src/render.c +++ b/src/render.c @@ -513,14 +513,24 @@ void seek_vgmstream(VGMSTREAM* vgmstream, int32_t seek_sample) { int is_looped = vgmstream->loop_flag || vgmstream->loop_target > 0; /* loop target disabled loop flag during decode */ + /* will decode and loop until seek sample, but slower */ + //todo apply same loop logic as below, or pretend we have play_forever + settings? if (!vgmstream->config_enabled) { - //todo same but ignore play duration or play_position + //;VGM_LOG("SEEK: simple seek=%i, cur=%i\n", seek_sample, vgmstream->current_sample); + if (seek_sample < vgmstream->current_sample) { + decode_samples = seek_sample; + reset_vgmstream(vgmstream); + } + else { + decode_samples = seek_sample - vgmstream->current_sample; + } + + seek_force_decode(vgmstream, decode_samples); return; } - //todo optimize layout looping with seek_vgmstream //todo could improve performance bit if hit_loop wasn't lost when calling reset - //todo wrong seek with ignore fade + //todo wrong seek with ignore fade, also for layered layers (pass count to force loop + layers) /* seeking to requested sample normally means decoding and discarding up to that point (from