From 3130eebf0c52c4b75a46fa9e225fbaaa462619ed Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 25 Aug 2018 20:46:54 +0200 Subject: [PATCH] Minor layout cleanup + doc --- src/layout/blocked.c | 41 +++++++++++++------------ src/layout/flat.c | 28 ++++++++++------- src/layout/interleave.c | 68 +++++++++++++++++++++++++---------------- src/layout/layered.c | 39 ++++++++++++----------- src/layout/segmented.c | 25 ++++++++------- src/vgmstream.h | 4 +-- 6 files changed, 119 insertions(+), 86 deletions(-) diff --git a/src/layout/blocked.c b/src/layout/blocked.c index 728969e0..3c436420 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -1,16 +1,20 @@ #include "layout.h" #include "../vgmstream.h" + static void block_update(VGMSTREAM * vgmstream); +/* Decodes samples for blocked streams. + * Data is divided into headered blocks with a bunch of data. The layout calls external helper functions + * when a block is decoded, and those must parse the new block and move offsets accordingly. */ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; + int samples_written = 0; + int frame_size, samples_per_frame, samples_this_block; - int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - int samples_this_block; + frame_size = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_this_block = 0; - /* get samples in the current block */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -19,12 +23,13 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame; } - /* decode all samples */ + while (samples_written < sample_count) { int samples_to_do; + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { - /* on loop those values are changed */ + /* handle looping, readjust back to loop start values */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -35,26 +40,26 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * continue; } - /* probably block bug or EOF, next calcs would give wrong values and buffer segfaults */ if (samples_this_block < 0) { + /* probably block bug or EOF, next calcs would give wrong values/segfaults/infinite loop */ VGM_LOG("layout_blocked: wrong block samples at 0x%lx\n", vgmstream->current_block_offset); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); - break; /* probable infinite loop otherwise */ + break; } - /* probably block bug or EOF, block functions won't be able to read anything useful */ if (vgmstream->current_block_offset < 0 || vgmstream->current_block_offset == 0xFFFFFFFF) { + /* probably block bug or EOF, block functions won't be able to read anything useful/infinite loop */ VGM_LOG("layout_blocked: wrong block offset found\n"); memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); - break; /* probable infinite loop otherwise */ + break; } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - if (samples_written + samples_to_do > sample_count) + if (samples_to_do > sample_count - samples_written) samples_to_do = sample_count - samples_written; - /* samples_this_block = 0 is allowed (empty block, do nothing then move to next block) */ if (samples_to_do > 0) { + /* samples_this_block = 0 is allowed (empty block, do nothing then move to next block) */ decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); } @@ -64,15 +69,13 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * /* move to next block when all samples are consumed */ - if (vgmstream->samples_into_block==samples_this_block - /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ + if (vgmstream->samples_into_block == samples_this_block + /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ //todo block_update(vgmstream); - /* for VBR these may change */ + /* update since these may change each block */ frame_size = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - - /* get samples in the current block */ if (vgmstream->current_block_samples) { samples_this_block = vgmstream->current_block_samples; } else if (frame_size == 0) { /* assume 4 bit */ //TODO: get_vgmstream_frame_size() really should return bits... */ @@ -87,7 +90,7 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM * } } - +/* helper functions to parse new block */ static void block_update(VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { case layout_blocked_ast: diff --git a/src/layout/flat.c b/src/layout/flat.c index f38198aa..59f3ce03 100644 --- a/src/layout/flat.c +++ b/src/layout/flat.c @@ -1,33 +1,39 @@ #include "layout.h" #include "../vgmstream.h" + +/* Decodes samples for flat streams. + * Data forms a single stream, and the decoder may internally skip chunks and move offsets as needed. */ void render_vgmstream_flat(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; + int samples_written = 0; + int samples_per_frame, samples_this_block; - const int samples_this_block = vgmstream->num_samples; - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); + samples_this_block = vgmstream->num_samples; /* do all samples if possible */ - while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { + /* handle looping */ continue; } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; - if (!samples_to_do) { - memset(buffer + samples_written * vgmstream->channels, 0, sizeof(sample) * vgmstream->channels * (sample_count - samples_written)); - return; + if (samples_to_do == 0) { + VGM_LOG("layout_flat: wrong samples_to_do found\n"); + memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); + break; } decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; + vgmstream->samples_into_block += samples_to_do; } } diff --git a/src/layout/interleave.c b/src/layout/interleave.c index 6090d197..6eda0a92 100644 --- a/src/layout/interleave.c +++ b/src/layout/interleave.c @@ -1,29 +1,35 @@ #include "layout.h" #include "../vgmstream.h" + +/* Decodes samples for interleaved streams. + * Data has interleaved chunks per channel, and once one is decoded the layout moves offsets, + * skipping other chunks (essentially a simplified variety of blocked layout). + * Incompatible with decoders that move offsets. */ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; - - int frame_size = get_vgmstream_frame_size(vgmstream); - int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - int samples_this_block; + int samples_written = 0; + int frame_size, samples_per_frame, samples_this_block; + int has_interleave_last = vgmstream->interleave_last_block_size && vgmstream->channels > 1; + frame_size = get_vgmstream_frame_size(vgmstream); + samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && - vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block> vgmstream->num_samples) { + if (has_interleave_last && + vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block > vgmstream->num_samples) { + /* adjust values again if inside last interleave */ frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; } - while (samples_writtenloop_flag && vgmstream_do_loop(vgmstream)) { - /* we assume that the loop is not back into a short block */ - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1) { + /* handle looping, restore standard interleave sizes */ + if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */ frame_size = get_vgmstream_frame_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; @@ -32,33 +38,43 @@ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREA } samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); - /*printf("vgmstream_samples_to_do(samples_this_block=%d,samples_per_frame=%d,vgmstream) returns %d\n",samples_this_block,samples_per_frame,samples_to_do);*/ + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; + //todo test if (samples to do == 0) decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; + vgmstream->samples_into_block += samples_to_do; - if (vgmstream->samples_into_block==samples_this_block) { - int chan; - if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && - vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + + /* move to next interleaved block when all samples are consumed */ + if (vgmstream->samples_into_block == samples_this_block) { + int ch; + + if (has_interleave_last && + vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { + /* adjust values again if inside last interleave */ frame_size = get_vgmstream_shortframe_size(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); - samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; - for (chan=0;chanchannels;chan++) - vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*(vgmstream->channels-chan)+vgmstream->interleave_last_block_size*chan; - } else { - for (chan=0;chanchannels;chan++) - vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*vgmstream->channels; + for (ch = 0; ch < vgmstream->channels; ch++) { + off_t skip = vgmstream->interleave_block_size*(vgmstream->channels-ch) + + vgmstream->interleave_last_block_size*ch;; + vgmstream->ch[ch].offset += skip; + } } - vgmstream->samples_into_block=0; + else { + for (ch = 0; ch < vgmstream->channels; ch++) { + off_t skip = vgmstream->interleave_block_size*vgmstream->channels; + vgmstream->ch[ch].offset += skip; + } + } + + vgmstream->samples_into_block = 0; } } diff --git a/src/layout/layered.c b/src/layout/layered.c index 624aa670..2dcae87b 100644 --- a/src/layout/layered.c +++ b/src/layout/layered.c @@ -1,37 +1,41 @@ #include "layout.h" #include "../vgmstream.h" -/* TODO: there must be a reasonable way to respect the loop settings, as - the substreams are in their own little world. - Currently the VGMSTREAMs layers loop internally and the external/base VGMSTREAM - doesn't actually loop, and would ignore any altered values/loop_flag. */ +/* NOTE: if loop settings change the layered vgmstreams must be notified (preferably using vgmstream_force_loop) */ #define LAYER_BUF_SIZE 512 #define LAYER_MAX_CHANNELS 6 /* at least 2, but let's be generous */ - +/* 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. + * Usually combined with custom streamfiles to handle data interleaved in weird ways. */ void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; - int32_t samples_done = 0; + int samples_written = 0; layered_layout_data *data = vgmstream->layout_data; + sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; - while (samples_done < sample_count) { - int32_t samples_to_do = LAYER_BUF_SIZE; + + while (samples_written < sample_count) { + int samples_to_do = LAYER_BUF_SIZE; int layer, ch = 0; - if (samples_to_do > sample_count - samples_done) - samples_to_do = sample_count - samples_done; + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; for (layer = 0; layer < data->layer_count; layer++) { - int s,l_ch; + int s, layer_ch; int layer_channels = data->layers[layer]->channels; + /* each layer will handle its own looping internally */ + render_vgmstream(interleave_buf, samples_to_do, data->layers[layer]); - for (l_ch = 0; l_ch < layer_channels; l_ch++) { + /* mix layer samples to main samples */ + for (layer_ch = 0; layer_ch < layer_channels; layer_ch++) { for (s = 0; s < samples_to_do; s++) { - size_t layer_sample = s*layer_channels + l_ch; - size_t buffer_sample = (samples_done+s)*vgmstream->channels + ch; + size_t layer_sample = s*layer_channels + layer_ch; + size_t buffer_sample = (samples_written+s)*vgmstream->channels + ch; buffer[buffer_sample] = interleave_buf[layer_sample]; } @@ -39,8 +43,9 @@ void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * } } - - samples_done += samples_to_do; + samples_written += samples_to_do; + vgmstream->current_sample = data->layers[0]->current_sample; /* just in case it's used for info */ + //vgmstream->samples_into_block = 0; /* handled in each layer */ } } diff --git a/src/layout/segmented.c b/src/layout/segmented.c index 4f2b7073..d6303325 100644 --- a/src/layout/segmented.c +++ b/src/layout/segmented.c @@ -2,35 +2,38 @@ #include "../vgmstream.h" +/* Decodes samples for segmented streams. + * 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 * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { - int samples_written=0; + int samples_written = 0; segmented_layout_data *data = vgmstream->layout_data; - //int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); - while (samples_writtensegments[data->current_segment]->num_samples; + if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { + /* handle looping, moving to loop segment */ //todo can only loop in a segment start // (for arbitrary values find loop segment from loop_start_sample, and skip N samples until loop start) data->current_segment = data->loop_segment; - reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; continue; } - samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); - - if (samples_written+samples_to_do > sample_count) - samples_to_do=sample_count-samples_written; + /* decode samples */ + samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); //todo should use a buffer + if (samples_to_do > sample_count - samples_written) + samples_to_do = sample_count - samples_written; + /* detect segment change and restart */ if (samples_to_do == 0) { data->current_segment++; reset_vgmstream(data->segments[data->current_segment]); - vgmstream->samples_into_block = 0; continue; } @@ -40,7 +43,7 @@ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM samples_written += samples_to_do; vgmstream->current_sample += samples_to_do; - vgmstream->samples_into_block+=samples_to_do; + vgmstream->samples_into_block += samples_to_do; } } diff --git a/src/vgmstream.h b/src/vgmstream.h index 9a7fba31..2e5a0624 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -779,8 +779,8 @@ typedef struct { /* layout/block state */ size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ - int32_t current_sample; /* number of samples we've passed */ - int32_t samples_into_block; /* number of samples into the current block */ + int32_t current_sample; /* number of samples we've passed (for loop detection) */ + int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */ off_t current_block_offset; /* start of this block (offset of block header) */ size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */ size_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */