mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-31 04:13:47 +01:00
Add pad end + don't emmit garbage samples past file end
This commit is contained in:
parent
aeb866fcf7
commit
57053ee348
@ -21,6 +21,10 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
|
|||||||
use_internal_buffer = 1;
|
use_internal_buffer = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data->current_segment >= data->segment_count) {
|
||||||
|
VGM_LOG("SEGMENT: wrong current segment\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (samples_written < sample_count) {
|
while (samples_written < sample_count) {
|
||||||
int samples_to_do;
|
int samples_to_do;
|
||||||
@ -75,6 +79,10 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
|
|||||||
/* detect segment change and restart */
|
/* detect segment change and restart */
|
||||||
if (samples_to_do == 0) {
|
if (samples_to_do == 0) {
|
||||||
data->current_segment++;
|
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]);
|
reset_vgmstream(data->segments[data->current_segment]);
|
||||||
vgmstream->samples_into_block = 0;
|
vgmstream->samples_into_block = 0;
|
||||||
continue;
|
continue;
|
||||||
|
39
src/player.c
39
src/player.c
@ -106,12 +106,10 @@ static void setup_state_processing(VGMSTREAM* vgmstream) {
|
|||||||
ps->fade_start = ps->pad_begin_left + ps->body_left;
|
ps->fade_start = ps->pad_begin_left + ps->body_left;
|
||||||
ps->fade_left = ps->fade_duration;
|
ps->fade_left = ps->fade_duration;
|
||||||
|
|
||||||
/* samples from last part */
|
/* samples from last part (anything beyond this is empty, unless play forever is set) */
|
||||||
|
ps->pad_end_start = ps->fade_start + ps->fade_left;
|
||||||
ps->pad_end_left = pc->pad_end;
|
ps->pad_end_left = pc->pad_end;
|
||||||
|
|
||||||
/* todo if play forever: ignore some? */
|
|
||||||
|
|
||||||
|
|
||||||
/* final count */
|
/* final count */
|
||||||
ps->play_duration = ps->pad_begin_left + ps->body_left + ps->fade_left + ps->pad_end_left;
|
ps->play_duration = ps->pad_begin_left + ps->body_left + ps->fade_left + ps->pad_end_left;
|
||||||
ps->play_position = 0;
|
ps->play_position = 0;
|
||||||
@ -242,36 +240,45 @@ void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) {
|
|||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) {
|
int render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) {
|
||||||
play_state_t* ps = &vgmstream->pstate;
|
play_state_t* ps = &vgmstream->pstate;
|
||||||
play_config_t* pc = &vgmstream->config;
|
//play_config_t* pc = &vgmstream->config;
|
||||||
|
|
||||||
if (!ps->fade_left || pc->play_forever)
|
//if (!ps->fade_left || pc->play_forever)
|
||||||
return;
|
// return;
|
||||||
if (ps->play_position + samples_done < ps->fade_start)
|
//if (ps->play_position + samples_done < ps->fade_start)
|
||||||
return; /* not yet */
|
// return;
|
||||||
|
|
||||||
{
|
{
|
||||||
int s, ch, start, pos;
|
int s, ch, start, fade_pos;
|
||||||
int channels = ps->output_channels;
|
int channels = ps->output_channels;
|
||||||
|
int32_t to_do = ps->fade_left;
|
||||||
|
|
||||||
if (ps->play_position < ps->fade_start) {
|
if (ps->play_position < ps->fade_start) {
|
||||||
start = samples_done - (ps->play_position + samples_done - ps->fade_start);
|
start = samples_done - (ps->play_position + samples_done - ps->fade_start);
|
||||||
pos = 0;
|
fade_pos = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
start = 0;
|
start = 0;
|
||||||
pos = ps->play_position - ps->fade_start;
|
fade_pos = ps->play_position - ps->fade_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (to_do > samples_done - start)
|
||||||
|
to_do = samples_done - start;
|
||||||
|
|
||||||
//TODO: use delta fadedness to improve performance?
|
//TODO: use delta fadedness to improve performance?
|
||||||
for (s = start; s < samples_done; s++, pos++) {
|
for (s = start; s < start + to_do; s++, fade_pos++) {
|
||||||
double fadedness = (double)(ps->fade_duration - pos) / ps->fade_duration;
|
double fadedness = (double)(ps->fade_duration - fade_pos) / ps->fade_duration;
|
||||||
for (ch = 0; ch < channels; ch++) {
|
for (ch = 0; ch < channels; ch++) {
|
||||||
buf[s*channels + ch] = (sample_t)buf[s*channels + ch] * fadedness;
|
buf[s*channels + ch] = (sample_t)buf[s*channels + ch] * fadedness;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vgmstream->pstate.fade_left -= (samples_done - start);
|
ps->fade_left -= to_do;
|
||||||
|
|
||||||
|
/* next samples after fade end would be pad end/silence, so we can just memset */
|
||||||
|
memset(buf + (start + to_do) * channels, 0, (samples_done - to_do - start) * sizeof(sample_t) * channels);
|
||||||
|
|
||||||
|
return samples_done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
src/vgmstream.c
106
src/vgmstream.c
@ -1077,6 +1077,17 @@ int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double
|
|||||||
|
|
||||||
static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
|
static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
|
||||||
|
|
||||||
|
/* current_sample goes between loop points (if looped) or up to max samples,
|
||||||
|
* must detect beyond that decoders would encounter garbage data */
|
||||||
|
|
||||||
|
if (vgmstream->current_sample > vgmstream->num_samples) {
|
||||||
|
int channels = vgmstream->channels;
|
||||||
|
int to_do = sample_count;
|
||||||
|
int done = 0;
|
||||||
|
memset(buf + done * channels, 0, to_do * sizeof(sample_t) * channels);
|
||||||
|
return sample_count;
|
||||||
|
}
|
||||||
|
|
||||||
switch (vgmstream->layout_type) {
|
switch (vgmstream->layout_type) {
|
||||||
case layout_interleave:
|
case layout_interleave:
|
||||||
render_vgmstream_interleave(buf, sample_count, vgmstream);
|
render_vgmstream_interleave(buf, sample_count, vgmstream);
|
||||||
@ -1136,6 +1147,14 @@ static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstre
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (vgmstream->current_sample > vgmstream->num_samples) {
|
||||||
|
int channels = vgmstream->channels;
|
||||||
|
int to_do = (vgmstream->current_sample - vgmstream->num_samples);
|
||||||
|
int done = sample_count - to_do;
|
||||||
|
memset(buf + done * channels, 0, to_do * sizeof(sample_t) * channels);
|
||||||
|
return sample_count;
|
||||||
|
}
|
||||||
|
|
||||||
return sample_count;
|
return sample_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1167,66 +1186,89 @@ static int render_pad_begin(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_
|
|||||||
return to_do;
|
return to_do;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int render_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do) {
|
||||||
|
int channels = vgmstream->pstate.output_channels;
|
||||||
|
|
||||||
|
/* since anything beyond pad end is silence no need to check ranges */
|
||||||
|
|
||||||
|
memset(buf, 0, samples_to_do * sizeof(sample_t) * channels);
|
||||||
|
return samples_to_do;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Decode data into sample buffer. Controls the "external" part of the decoding,
|
/* Decode data into sample buffer. Controls the "external" part of the decoding,
|
||||||
* while layout/decode control the "internal" part. */
|
* while layout/decode control the "internal" part. */
|
||||||
void render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
|
int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
|
||||||
int done;
|
play_state_t* ps = &vgmstream->pstate;
|
||||||
|
int samples_to_do = sample_count;
|
||||||
int samples_done = 0;
|
int samples_done = 0;
|
||||||
|
int done;
|
||||||
sample_t* tmpbuf = buf;
|
sample_t* tmpbuf = buf;
|
||||||
|
|
||||||
|
|
||||||
/* simple mode with no settings */
|
/* simple mode with no settings (just skip everything below) */
|
||||||
if (!vgmstream->config_enabled) {
|
if (!vgmstream->config_enabled) {
|
||||||
render_layout(buf, sample_count, vgmstream);
|
render_layout(buf, samples_to_do, vgmstream);
|
||||||
mix_vgmstream(buf, sample_count, vgmstream);
|
mix_vgmstream(buf, samples_to_do, vgmstream);
|
||||||
return;
|
return samples_to_do;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* trim may go first since it doesn't need output */
|
/* trim may go first since it doesn't need output nor changes totals */
|
||||||
if (vgmstream->pstate.trim_begin_left) {
|
if (ps->trim_begin_left) {
|
||||||
render_trim(vgmstream);
|
render_trim(vgmstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* adds empty samples to buf */
|
/* adds empty samples to buf */
|
||||||
if (vgmstream->pstate.pad_begin_left) {
|
if (ps->pad_begin_left) {
|
||||||
done = render_pad_begin(vgmstream, tmpbuf, sample_count);
|
done = render_pad_begin(vgmstream, tmpbuf, samples_to_do);
|
||||||
samples_done += done;
|
samples_done += done;
|
||||||
sample_count -= done;
|
samples_to_do -= done;
|
||||||
tmpbuf += done * vgmstream->pstate.output_channels;
|
tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end padding (done before to avoid decoding if possible) */
|
||||||
|
if (!vgmstream->config.play_forever /*&& ps->pad_end_left*/
|
||||||
|
&& ps->play_position + samples_done >= ps->pad_end_start) {
|
||||||
|
done = render_pad_end(vgmstream, tmpbuf, samples_to_do);
|
||||||
|
samples_done += done;
|
||||||
|
samples_to_do -= done;
|
||||||
|
tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* main decode */
|
/* main decode */
|
||||||
{
|
{ //if (samples_to_do) /* 0 ok, less likely */
|
||||||
done = render_layout(tmpbuf, sample_count, vgmstream);
|
done = render_layout(tmpbuf, samples_to_do, vgmstream);
|
||||||
samples_done += done;
|
|
||||||
mix_vgmstream(tmpbuf, done, vgmstream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* simple fadeout */
|
mix_vgmstream(tmpbuf, done, vgmstream);
|
||||||
if (vgmstream->pstate.fade_left && !vgmstream->config.play_forever) {
|
|
||||||
render_fade(vgmstream, tmpbuf, samples_done);
|
samples_done += done;
|
||||||
|
|
||||||
|
/* simple fadeout */
|
||||||
|
if (!vgmstream->config.play_forever && ps->fade_left
|
||||||
|
&& ps->play_position + done >= ps->fade_start) {
|
||||||
|
render_fade(vgmstream, tmpbuf, done);
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpbuf += done * vgmstream->pstate.output_channels;
|
||||||
}
|
}
|
||||||
/* silence samples in buf */
|
|
||||||
//else if (vgmstream->pstate.pad_end_left) {
|
|
||||||
// done = pad_end_vgmstream(vgmstream, buf, sample_count);
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
vgmstream->pstate.play_position += samples_done;
|
vgmstream->pstate.play_position += samples_done;
|
||||||
|
|
||||||
if (vgmstream->pstate.play_position > vgmstream->pstate.play_duration) {
|
/* signal end */
|
||||||
//int channels = vgmstream->pstate.output_channels;
|
if (!vgmstream->config.play_forever
|
||||||
|
&& ps->play_position > ps->play_duration) {
|
||||||
|
int excess = ps->play_position - ps->play_duration;
|
||||||
|
if (excess > sample_count)
|
||||||
|
excess = sample_count;
|
||||||
|
|
||||||
//todo silence if position > end and not loop forever?
|
samples_done = (sample_count - excess);
|
||||||
//if (!vgmstream->config.play_forever) {
|
|
||||||
// memset(...);
|
|
||||||
//}
|
|
||||||
vgmstream->pstate.play_position = vgmstream->pstate.play_duration;
|
|
||||||
|
|
||||||
//memset(buf, 0, samples_done * sizeof(sample_t) * channels);
|
ps->play_position = ps->play_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return samples_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -831,6 +831,7 @@ typedef struct {
|
|||||||
int32_t fade_duration;
|
int32_t fade_duration;
|
||||||
int32_t fade_start;
|
int32_t fade_start;
|
||||||
int32_t fade_left;
|
int32_t fade_left;
|
||||||
|
int32_t pad_end_start;
|
||||||
int32_t pad_end_left;
|
int32_t pad_end_left;
|
||||||
|
|
||||||
int32_t play_duration; /* total samples that the stream lasts (after applying all config) */
|
int32_t play_duration; /* total samples that the stream lasts (after applying all config) */
|
||||||
@ -1104,8 +1105,8 @@ void close_vgmstream(VGMSTREAM* vgmstream);
|
|||||||
/* calculate the number of samples to be played based on looping parameters */
|
/* calculate the number of samples to be played based on looping parameters */
|
||||||
int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream);
|
int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream);
|
||||||
|
|
||||||
/* Decode data into sample buffer */
|
/* Decode data into sample buffer. Returns < sample_count on stream end */
|
||||||
void render_vgmstream(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
|
int render_vgmstream(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
|
||||||
|
|
||||||
/* Write a description of the stream into array pointed by desc, which must be length bytes long.
|
/* Write a description of the stream into array pointed by desc, which must be length bytes long.
|
||||||
* Will always be null-terminated if length > 0 */
|
* Will always be null-terminated if length > 0 */
|
||||||
@ -1170,6 +1171,6 @@ void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t ou
|
|||||||
void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size);
|
void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size);
|
||||||
|
|
||||||
|
|
||||||
void render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done);
|
int render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done);
|
||||||
void setup_state_vgmstream(VGMSTREAM* vgmstream);
|
void setup_state_vgmstream(VGMSTREAM* vgmstream);
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user