Minor layout cleanup + doc

This commit is contained in:
bnnm 2018-08-25 20:46:54 +02:00
parent 3765831dbe
commit 3130eebf0c
6 changed files with 119 additions and 86 deletions

View File

@ -1,16 +1,20 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.h" #include "../vgmstream.h"
static void block_update(VGMSTREAM * vgmstream); 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) { 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); frame_size = get_vgmstream_frame_size(vgmstream);
int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
int samples_this_block; samples_this_block = 0;
/* get samples in the current block */
if (vgmstream->current_block_samples) { if (vgmstream->current_block_samples) {
samples_this_block = 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... */ } 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; samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame;
} }
/* decode all samples */
while (samples_written < sample_count) { while (samples_written < sample_count) {
int samples_to_do; int samples_to_do;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { 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) { if (vgmstream->current_block_samples) {
samples_this_block = 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... */ } 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; continue;
} }
/* probably block bug or EOF, next calcs would give wrong values and buffer segfaults */
if (samples_this_block < 0) { 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); 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)); 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) { 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"); VGM_LOG("layout_blocked: wrong block offset found\n");
memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample)); 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); 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_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) { 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); 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 */ /* move to next block when all samples are consumed */
if (vgmstream->samples_into_block==samples_this_block if (vgmstream->samples_into_block == samples_this_block
/*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ /*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ //todo
block_update(vgmstream); block_update(vgmstream);
/* for VBR these may change */ /* update since these may change each block */
frame_size = get_vgmstream_frame_size(vgmstream); frame_size = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
/* get samples in the current block */
if (vgmstream->current_block_samples) { if (vgmstream->current_block_samples) {
samples_this_block = 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... */ } 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) { static void block_update(VGMSTREAM * vgmstream) {
switch (vgmstream->layout_type) { switch (vgmstream->layout_type) {
case layout_blocked_ast: case layout_blocked_ast:

View File

@ -1,33 +1,39 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.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) { 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; samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_this_block = vgmstream->num_samples; /* do all samples if possible */
while (samples_written<sample_count) {
while (samples_written < sample_count) {
int samples_to_do; int samples_to_do;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
/* handle looping */
continue; continue;
} }
samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream); samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream);
if (samples_to_do > sample_count - samples_written)
if (samples_written+samples_to_do > sample_count) samples_to_do = sample_count - samples_written;
samples_to_do=sample_count-samples_written;
if (!samples_to_do) { if (samples_to_do == 0) {
memset(buffer + samples_written * vgmstream->channels, 0, sizeof(sample) * vgmstream->channels * (sample_count - samples_written)); VGM_LOG("layout_flat: wrong samples_to_do found\n");
return; 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); decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
samples_written += samples_to_do; samples_written += samples_to_do;
vgmstream->current_sample += samples_to_do; vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block+=samples_to_do; vgmstream->samples_into_block += samples_to_do;
} }
} }

View File

@ -1,29 +1,35 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.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) { void render_vgmstream_interleave(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 has_interleave_last = vgmstream->interleave_last_block_size && vgmstream->channels > 1;
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 = vgmstream->interleave_block_size / frame_size * samples_per_frame; samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame;
if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && if (has_interleave_last &&
vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block> vgmstream->num_samples) { 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); frame_size = get_vgmstream_shortframe_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream);
samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame;
} }
while (samples_written<sample_count) {
while (samples_written < sample_count) {
int samples_to_do; int samples_to_do;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
/* we assume that the loop is not back into a short block */ /* handle looping, restore standard interleave sizes */
if (vgmstream->interleave_last_block_size && vgmstream->channels > 1) { if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */
frame_size = get_vgmstream_frame_size(vgmstream); frame_size = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream); samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
samples_this_block = vgmstream->interleave_block_size / frame_size * samples_per_frame; 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); 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) //todo test if (samples to do == 0)
samples_to_do=sample_count-samples_written;
decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer); decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
samples_written += samples_to_do; samples_written += samples_to_do;
vgmstream->current_sample += 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; /* move to next interleaved block when all samples are consumed */
if (vgmstream->interleave_last_block_size && vgmstream->channels > 1 && if (vgmstream->samples_into_block == samples_this_block) {
vgmstream->current_sample + samples_this_block > vgmstream->num_samples) { 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); frame_size = get_vgmstream_shortframe_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream); samples_per_frame = get_vgmstream_samples_per_shortframe(vgmstream);
samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame; samples_this_block = vgmstream->interleave_last_block_size / frame_size * samples_per_frame;
for (chan=0;chan<vgmstream->channels;chan++)
vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*(vgmstream->channels-chan)+vgmstream->interleave_last_block_size*chan;
} else {
for (chan=0;chan<vgmstream->channels;chan++) for (ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->ch[chan].offset+=vgmstream->interleave_block_size*vgmstream->channels; 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;
} }
} }

View File

@ -1,37 +1,41 @@
#include "layout.h" #include "layout.h"
#include "../vgmstream.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_BUF_SIZE 512
#define LAYER_MAX_CHANNELS 6 /* at least 2, but let's be generous */ #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) { void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
sample interleave_buf[LAYER_BUF_SIZE*LAYER_MAX_CHANNELS]; int samples_written = 0;
int32_t samples_done = 0;
layered_layout_data *data = vgmstream->layout_data; 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; int layer, ch = 0;
if (samples_to_do > sample_count - samples_done) if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_done; samples_to_do = sample_count - samples_written;
for (layer = 0; layer < data->layer_count; layer++) { for (layer = 0; layer < data->layer_count; layer++) {
int s,l_ch; int s, layer_ch;
int layer_channels = data->layers[layer]->channels; 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]); 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++) { for (s = 0; s < samples_to_do; s++) {
size_t layer_sample = s*layer_channels + l_ch; size_t layer_sample = s*layer_channels + layer_ch;
size_t buffer_sample = (samples_done+s)*vgmstream->channels + ch; size_t buffer_sample = (samples_written+s)*vgmstream->channels + ch;
buffer[buffer_sample] = interleave_buf[layer_sample]; buffer[buffer_sample] = interleave_buf[layer_sample];
} }
@ -39,8 +43,9 @@ void render_vgmstream_layered(sample * buffer, int32_t sample_count, VGMSTREAM *
} }
} }
samples_written += samples_to_do;
samples_done += 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 */
} }
} }

View File

@ -2,35 +2,38 @@
#include "../vgmstream.h" #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) { 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; segmented_layout_data *data = vgmstream->layout_data;
//int samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
while (samples_written<sample_count) {
while (samples_written < sample_count) {
int samples_to_do; int samples_to_do;
int samples_this_block = data->segments[data->current_segment]->num_samples; int samples_this_block = data->segments[data->current_segment]->num_samples;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) { if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
/* handle looping, moving to loop segment */
//todo can only loop in a segment start //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) // (for arbitrary values find loop segment from loop_start_sample, and skip N samples until loop start)
data->current_segment = data->loop_segment; data->current_segment = data->loop_segment;
reset_vgmstream(data->segments[data->current_segment]); reset_vgmstream(data->segments[data->current_segment]);
vgmstream->samples_into_block = 0; vgmstream->samples_into_block = 0;
continue; continue;
} }
samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); /* decode samples */
samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream); //todo should use a buffer
if (samples_written+samples_to_do > sample_count) if (samples_to_do > sample_count - samples_written)
samples_to_do=sample_count-samples_written; samples_to_do = sample_count - samples_written;
/* detect segment change and restart */
if (samples_to_do == 0) { if (samples_to_do == 0) {
data->current_segment++; data->current_segment++;
reset_vgmstream(data->segments[data->current_segment]); reset_vgmstream(data->segments[data->current_segment]);
vgmstream->samples_into_block = 0; vgmstream->samples_into_block = 0;
continue; continue;
} }
@ -40,7 +43,7 @@ void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM
samples_written += samples_to_do; samples_written += samples_to_do;
vgmstream->current_sample += samples_to_do; vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block+=samples_to_do; vgmstream->samples_into_block += samples_to_do;
} }
} }

View File

@ -779,8 +779,8 @@ typedef struct {
/* layout/block state */ /* layout/block state */
size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */ 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 current_sample; /* number of samples we've passed (for loop detection) */
int32_t samples_into_block; /* number of samples into the current block */ 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) */ 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_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) */ size_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */