From 48a98368288abf4ef6836d8621796100ce3f08ff Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 22 Jul 2020 23:29:38 +0200 Subject: [PATCH] Add begin trim/padding to main render --- src/mixing.c | 20 ++++---- src/player.c | 124 ++++++++++++++++++++++++++++++++---------------- src/plugins.c | 4 ++ src/vgmstream.c | 100 ++++++++++++++++++++++++++++++-------- src/vgmstream.h | 39 +++++++-------- 5 files changed, 193 insertions(+), 94 deletions(-) diff --git a/src/mixing.c b/src/mixing.c index e4bd02c7..e7028d7c 100644 --- a/src/mixing.c +++ b/src/mixing.c @@ -1062,12 +1062,6 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { if (!data) goto fail; - /* a bit wonky but eh... */ - if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { - vgmstream->channel_layout = 0; - ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0; - } - /* special value to not actually enable anything (used to query values) */ if (max_sample_count <= 0) goto fail; @@ -1079,6 +1073,12 @@ void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) { data->mixbuf = mixbuf_re; data->mixing_on = 1; + /* a bit wonky but eh... */ + if (vgmstream->channel_layout && vgmstream->channels != data->output_channels) { + vgmstream->channel_layout = 0; + ((VGMSTREAM*)vgmstream->start_vgmstream)->channel_layout = 0; + } + /* since data exists on its own memory and pointer is already set * there is no need to propagate to start_vgmstream */ @@ -1089,7 +1089,7 @@ fail: return; } -void mixing_info(VGMSTREAM * vgmstream, int *out_input_channels, int *out_output_channels) { +void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_channels) { mixing_data *data = vgmstream->mixing_data; int input_channels, output_channels; @@ -1101,11 +1101,13 @@ void mixing_info(VGMSTREAM * vgmstream, int *out_input_channels, int *out_output else input_channels = vgmstream->channels; - if (out_input_channels) *out_input_channels = input_channels; - if (out_output_channels) *out_output_channels = output_channels; + if (p_input_channels) *p_input_channels = input_channels; + if (p_output_channels) *p_output_channels = output_channels; //;VGM_LOG("MIX: channels %i, in=%i, out=%i, mix=%i\n", vgmstream->channels, input_channels, output_channels, data->mixing_channels); return; fail: + if (p_input_channels) *p_input_channels = vgmstream->channels; + if (p_output_channels) *p_output_channels = vgmstream->channels; return; } diff --git a/src/player.c b/src/player.c index c4af77e6..76bceb91 100644 --- a/src/player.c +++ b/src/player.c @@ -1,4 +1,5 @@ #include "vgmstream.h" +#include "mixing.h" #include "plugins.h" @@ -16,62 +17,87 @@ int32_t vgmstream_get_samples(VGMSTREAM* vgmstream) { static void setup_state_vgmstream(VGMSTREAM* vgmstream) { play_state_t* ps = &vgmstream->pstate; play_config_t* pc = &vgmstream->config; + double sample_rate = vgmstream->sample_rate; + + /* time to samples */ + if (pc->pad_begin_s) + pc->pad_begin = pc->pad_begin_s * sample_rate; + if (pc->pad_end_s) + pc->pad_end = pc->pad_end_s * sample_rate; + if (pc->trim_begin_s) + pc->trim_begin = pc->trim_begin_s * sample_rate; + if (pc->trim_end_s) + pc->trim_end = pc->trim_end_s * sample_rate; + if (pc->target_time_s) + pc->target_time = pc->target_time_s * sample_rate; + //todo fade time also set to samples - ps->pad_begin_duration = pc->pad_begin; - ps->pad_begin_start = 0; - ps->pad_begin_end = ps->pad_begin_start + ps->pad_begin_duration; + /* samples before all decode */ + ps->pad_begin_left = pc->pad_begin; - ps->trim_begin_duration = pc->trim_begin; - ps->trim_begin_start = ps->pad_begin_end; - ps->trim_begin_end = ps->trim_begin_start + ps->trim_begin_duration; + /* removed samples from first decode */ + ps->trim_begin_left = pc->trim_begin; /* main samples part */ - ps->body_duration = 0; + ps->body_left = 0; if (pc->target_time) { - ps->body_duration += pc->target_time; /* wheter it loops or not */ + ps->body_left += pc->target_time; /* whether it loops or not */ } else if (vgmstream->loop_flag) { - ps->body_duration += vgmstream->loop_start_sample; + ps->body_left += vgmstream->loop_start_sample; if (pc->ignore_fade) { - ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)pc->loop_count; - ps->body_duration += (vgmstream->num_samples - vgmstream->loop_end_sample); + ps->body_left += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)pc->loop_count; + ps->body_left += (vgmstream->num_samples - vgmstream->loop_end_sample); } else { - ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * pc->loop_count; + ps->body_left += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * pc->loop_count; } } else { - ps->body_duration += vgmstream->num_samples; + ps->body_left += vgmstream->num_samples; } - /* no need to apply in real time */ + /* samples from some modify body */ + if (pc->trim_begin) + ps->body_left -= pc->trim_begin; if (pc->trim_end) - ps->body_duration -= pc->trim_end; + ps->body_left -= pc->trim_end; if (pc->fade_delay && vgmstream->loop_flag) - ps->body_duration += pc->fade_delay * vgmstream->sample_rate; - if (ps->body_duration < 0) /* ? */ - ps->body_duration = 0; - ps->body_start = ps->trim_begin_end; - ps->body_end = ps->body_start + ps->body_duration; + ps->body_left += pc->fade_delay * vgmstream->sample_rate; + /* samples from fade part */ if (pc->fade_time && vgmstream->loop_flag) ps->fade_duration = pc->fade_time * vgmstream->sample_rate; - ps->fade_start = ps->body_end; - ps->fade_end = ps->fade_start + ps->fade_duration; + ps->fade_start = ps->pad_begin_left + ps->body_left; + ps->fade_left = ps->fade_duration; + + /* samples from last part */ + ps->pad_end_left = pc->pad_end; + + /* todo if play forever: ignore some? */ - ps->pad_end_duration = pc->pad_end; - ps->pad_end_start = ps->fade_end; - ps->pad_end_end = ps->pad_end_start + ps->pad_end_duration; /* final count */ - ps->play_duration = ps->pad_end_end; + ps->play_duration = ps->pad_begin_left + ps->body_left + ps->fade_left + ps->pad_end_left; ps->play_position = 0; - /* other info */ - vgmstream_mixing_enable(vgmstream, 0, &ps->input_channels, &ps->output_channels); + /* values too big can overflow, just ignore */ + if (ps->pad_begin_left < 0) + ps->pad_begin_left = 0; + if (ps->body_left < 0) + ps->body_left = 0; + if (ps->fade_left < 0) + ps->fade_left = 0; + if (ps->pad_end_left < 0) + ps->pad_end_left = 0; + if (ps->play_duration < 0) + ps->play_duration = 0; - VGM_LOG("**%i, %i, %i\n", ps->body_duration, ps->fade_duration, ps->play_duration);//todo + + /* other info (updated once mixing is enabled) */ + ps->input_channels = vgmstream->channels; + ps->output_channels = vgmstream->channels; } static void load_player_config(VGMSTREAM* vgmstream, play_config_t* def, vgmstream_cfg_t* vcfg) { @@ -85,6 +111,15 @@ static void load_player_config(VGMSTREAM* vgmstream, play_config_t* def, vgmstre def->fade_time = vcfg->fade_period; //todo loop period } + +static void copy_time(int* dst_flag, int32_t* dst_time, double* dst_time_s, int* src_flag, int32_t* src_time, double* src_time_s) { + if (!*src_flag) + return; + *dst_flag = 1; + *dst_time = *src_time; + *dst_time_s = *src_time_s; +} + static void load_internal_config(VGMSTREAM* vgmstream, play_config_t* def, play_config_t* tcfg) { /* loop limit: txtp #L > txtp #l > player #L > player #l */ if (tcfg->play_forever) { @@ -125,6 +160,12 @@ static void load_internal_config(VGMSTREAM* vgmstream, play_config_t* def, play_ def->force_loop = 0; def->really_force_loop = 0; } + + copy_time(&def->pad_begin_set, &def->pad_begin, &def->pad_begin_s, &tcfg->pad_begin_set, &tcfg->pad_begin, &tcfg->pad_begin_s); + copy_time(&def->pad_end_set, &def->pad_end, &def->pad_end_s, &tcfg->pad_end_set, &tcfg->pad_end, &tcfg->pad_end_s); + copy_time(&def->trim_begin_set, &def->trim_begin, &def->trim_begin_s, &tcfg->trim_begin_set, &tcfg->trim_begin, &tcfg->trim_begin_s); + copy_time(&def->trim_end_set, &def->trim_end, &def->trim_end_s, &tcfg->trim_end_set, &tcfg->trim_end, &tcfg->trim_end_s); + copy_time(&def->target_time_set,&def->target_time, &def->target_time_s, &tcfg->target_time_set, &tcfg->target_time, &tcfg->target_time_s); } @@ -180,37 +221,36 @@ void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) { /*****************************************************************************/ -void fade_vgmstream(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { +void render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { play_state_t* ps = &vgmstream->pstate; play_config_t* pc = &vgmstream->config; - if (!ps->fade_duration || pc->play_forever) + if (!ps->fade_left || pc->play_forever) return; if (ps->play_position + samples_done < ps->fade_start) - return; - if (ps->play_position > ps->fade_end) - return; + return; /* not yet */ { - int s, ch; + int s, ch, start, pos; int channels = ps->output_channels; - int sample_start, fade_pos; if (ps->play_position < ps->fade_start) { - sample_start = samples_done - (ps->play_position + samples_done - ps->fade_start); - fade_pos = 0; + start = samples_done - (ps->play_position + samples_done - ps->fade_start); + pos = 0; } else { - sample_start = 0; - fade_pos = ps->play_position - ps->fade_start; + start = 0; + pos = ps->play_position - ps->fade_start; } //TODO: use delta fadedness to improve performance? - for (s = sample_start; s < samples_done; s++, fade_pos++) { - double fadedness = (double)(ps->fade_duration - fade_pos) / ps->fade_duration; + for (s = start; s < samples_done; s++, pos++) { + double fadedness = (double)(ps->fade_duration - pos) / ps->fade_duration; for (ch = 0; ch < channels; ch++) { buf[s*channels + ch] = (sample_t)buf[s*channels + ch] * fadedness; } } + + vgmstream->pstate.fade_left -= (samples_done - start); } } diff --git a/src/plugins.c b/src/plugins.c index 1e982bb6..406c5524 100644 --- a/src/plugins.c +++ b/src/plugins.c @@ -329,6 +329,10 @@ void vgmstream_tags_reset(VGMSTREAM_TAGS* tags, const char* target_filename) { void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int *input_channels, int *output_channels) { mixing_setup(vgmstream, max_sample_count); mixing_info(vgmstream, input_channels, output_channels); + + /* update internals */ + mixing_info(vgmstream, &vgmstream->pstate.input_channels, &vgmstream->pstate.output_channels); + setup_vgmstream(vgmstream); } void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) { diff --git a/src/vgmstream.c b/src/vgmstream.c index 1ffe7afc..3d7f670d 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1068,7 +1068,7 @@ int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double } } -void render_vgmstream_internal(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { +static int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { switch (vgmstream->layout_type) { case layout_interleave: @@ -1129,33 +1129,91 @@ void render_vgmstream_internal(sample_t* buf, int32_t sample_count, VGMSTREAM* v break; } - - mix_vgmstream(buf, sample_count, vgmstream); + return sample_count; } +static void render_trim(VGMSTREAM* vgmstream) { + /* big-ish buffer since the average trim would be a few seconds for >=2ch at 48000, and less calls = better */ + sample_t tmpbuf[0x40000]; + int max_samples = 0x40000 / vgmstream->pstate.input_channels; + + while (vgmstream->pstate.trim_begin_left) { + int to_do = vgmstream->pstate.trim_begin_left; + if (to_do > max_samples) + to_do = max_samples; + + render_layout(tmpbuf, to_do, vgmstream); + /* just consume samples so no need to apply mixing */ + vgmstream->pstate.trim_begin_left -= to_do; + } +} + +static int render_pad_begin(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do) { + int channels = vgmstream->pstate.output_channels; + int to_do = vgmstream->pstate.pad_begin_left; + if (to_do > samples_to_do) + to_do = samples_to_do; + + memset(buf, 0, to_do * sizeof(sample_t) * channels); + vgmstream->pstate.pad_begin_left -= to_do; + + return to_do; +} + + /* Decode data into sample buffer. Controls the "external" part of the decoding, - * while layouts control the "internal" part. */ + * while layout/decode control the "internal" part. */ void render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { + int done; + int samples_done = 0; - - render_vgmstream_internal(buf, sample_count, vgmstream); - - if (vgmstream->config_set) - fade_vgmstream(vgmstream, buf, sample_count); - - //todo silence if position > and not loop forever - - if (vgmstream->config_set) { - vgmstream->pstate.play_position += sample_count; - - if (vgmstream->pstate.play_position > vgmstream->pstate.play_duration) { - //if (!vgmstream->config.play_forever) { - // memset(...); - //} - vgmstream->pstate.play_position = vgmstream->pstate.play_duration; - } + /* no settings */ + if (!vgmstream->config_set) { + render_layout(buf, sample_count, vgmstream); + mix_vgmstream(buf, sample_count, vgmstream); + return; } + + /* trim may go first since it doesn't need output */ + if (vgmstream->pstate.trim_begin_left) { + render_trim(vgmstream); + } + + /* adds empty samples to buf */ + if (vgmstream->pstate.pad_begin_left) { + done = render_pad_begin(vgmstream, buf, sample_count); + samples_done += done; + sample_count -= done; + buf += done * vgmstream->pstate.output_channels; + } + + /* main decode */ + { + done = render_layout(buf, sample_count, vgmstream); + samples_done += done; + mix_vgmstream(buf, done, vgmstream); + } + + /* simple fadeout */ + if (vgmstream->pstate.fade_left && !vgmstream->config.play_forever) { + render_fade(vgmstream, buf, samples_done); + } + /* 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; + + if (vgmstream->pstate.play_position > vgmstream->pstate.play_duration) { + //todo silence if position > end and not loop forever? + //if (!vgmstream->config.play_forever) { + // memset(...); + //} + vgmstream->pstate.play_position = vgmstream->pstate.play_duration; + } } diff --git a/src/vgmstream.h b/src/vgmstream.h index 77a4861a..6d187f82 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -788,15 +788,23 @@ typedef struct { int ignore_fade; /* processing */ + double loop_count; int32_t pad_begin; int32_t trim_begin; int32_t target_time; - double loop_count; int32_t trim_end; double fade_delay; /* not in samples for backwards compatibility */ double fade_time; int32_t pad_end; + double pad_begin_s; + double trim_begin_s; + double target_time_s; + double trim_end_s; + //double fade_delay_s; + //double fade_time_s; + double pad_end_s; + /* internal flags */ int pad_begin_set; int trim_begin_set; @@ -807,6 +815,7 @@ typedef struct { int fade_time_set; int pad_end_set; + } play_config_t; @@ -814,25 +823,13 @@ typedef struct { int input_channels; int output_channels; - int32_t pad_begin_duration; - int32_t pad_begin_start; - int32_t pad_begin_end; - - int32_t trim_begin_duration; - int32_t trim_begin_start; - int32_t trim_begin_end; - - int32_t body_duration; - int32_t body_start; - int32_t body_end; - + int32_t pad_begin_left; + int32_t trim_begin_left; + int32_t body_left; int32_t fade_duration; int32_t fade_start; - int32_t fade_end; - - int32_t pad_end_duration; - int32_t pad_end_start; - int32_t pad_end_end; + int32_t fade_left; + int32_t pad_end_left; int32_t play_duration; /* total samples that the stream lasts (after applying all config) */ int32_t play_position; /* absolute sample where stream is */ @@ -1169,8 +1166,6 @@ void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t ou void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size); void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size); -//void pad_begin_vgmstream(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do); -//void trim_begin_vgmstream(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do); -//void pad_end_vgmstream(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do); -void fade_vgmstream(VGMSTREAM* vgmstream, sample_t* buf, int samples_done); + +void render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done); #endif