cleanup: float stuff

This commit is contained in:
bnnm 2024-08-27 00:10:07 +02:00
parent 28c6d64693
commit 045e1c9107
5 changed files with 179 additions and 97 deletions

View File

@ -53,15 +53,15 @@ static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) {
return current_pos; return current_pos;
} }
void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream) { void mix_vgmstream(sbuf_t* sbuf, VGMSTREAM* vgmstream) {
/* no support or not need to apply */ /* no support or not need to apply */
if (!mixer_is_active(vgmstream->mixer)) if (!mixer_is_active(vgmstream->mixer))
return; return;
sbuf_t sbuf_tmp; //sbuf_t sbuf_tmp;
sbuf_t* sbuf = &sbuf_tmp; //sbuf_t* sbuf = &sbuf_tmp;
sbuf_init_s16(sbuf, outbuf, sample_count, vgmstream->channels); //sbuf_init_s16(sbuf, outbuf, sample_count, vgmstream->channels);
sbuf->filled = sbuf->samples; //sbuf->filled = sbuf->samples;
int32_t current_pos = get_current_pos(vgmstream, sbuf->filled); int32_t current_pos = get_current_pos(vgmstream, sbuf->filled);

View File

@ -3,10 +3,11 @@
#include "../vgmstream.h" #include "../vgmstream.h"
#include "../util/log.h" #include "../util/log.h"
#include "sbuf.h"
/* Applies mixing commands to the sample buffer. Mixing must be externally enabled and /* Applies mixing commands to the sample buffer. Mixing must be externally enabled and
* outbuf must big enough to hold output_channels*samples_to_do */ * outbuf must big enough to hold output_channels*samples_to_do */
void mix_vgmstream(sample_t *outbuf, int32_t sample_count, VGMSTREAM* vgmstream); void mix_vgmstream(sbuf_t* sbuf, VGMSTREAM* vgmstream);
/* Call to let vgmstream apply mixing, which must handle input/output_channels. /* Call to let vgmstream apply mixing, which must handle input/output_channels.
* Once mixing is active any new mixes are ignored (to avoid the possibility * Once mixing is active any new mixes are ignored (to avoid the possibility

View File

@ -166,80 +166,63 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
/*****************************************************************************/ /*****************************************************************************/
typedef struct {
//sbuf_t sbuf;
int16_t* tmpbuf;
int samples_to_do;
int samples_done;
} render_helper_t;
// consumes samples from decoder // consumes samples from decoder
static void play_op_trim(VGMSTREAM* vgmstream, render_helper_t* renderer) { static void play_op_trim(VGMSTREAM* vgmstream, sbuf_t* sbuf) {
play_state_t* ps = &vgmstream->pstate; play_state_t* ps = &vgmstream->pstate;
if (!ps->trim_begin_left) if (!ps->trim_begin_left)
return; return;
if (!renderer->samples_to_do) if (sbuf->samples <= 0)
return; return;
// simpler using external buf? // simpler using external buf?
//sample_t* tmpbuf = vgmstream->tmpbuf; //sample_t* tmpbuf = vgmstream->tmpbuf;
//size_t tmpbuf_size = vgmstream->tmpbuf_size; //size_t tmpbuf_size = vgmstream->tmpbuf_size;
//int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */ //int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */
sample_t* tmpbuf = renderer->tmpbuf;
int buf_samples = renderer->samples_to_do;
while (ps->trim_begin_left) { while (ps->trim_begin_left) {
int to_do = ps->trim_begin_left; int to_do = ps->trim_begin_left;
if (to_do > buf_samples) if (to_do > sbuf->samples)
to_do = buf_samples; to_do = sbuf->samples;
render_layout(tmpbuf, to_do, vgmstream); render_layout(sbuf->buf, to_do, vgmstream);
/* no mixing */ /* no mixing */
ps->trim_begin_left -= to_do; ps->trim_begin_left -= to_do;
} }
} }
// adds empty samples to buf // adds empty samples to buf
static void play_op_pad_begin(VGMSTREAM* vgmstream, render_helper_t* renderer) { static void play_op_pad_begin(VGMSTREAM* vgmstream, sbuf_t* sbuf) {
play_state_t* ps = &vgmstream->pstate; play_state_t* ps = &vgmstream->pstate;
if (!ps->pad_begin_left) if (!ps->pad_begin_left)
return; return;
//if (ps->play_position > ps->play_begin_duration) //implicit //if (ps->play_position > ps->play_begin_duration) //implicit
// return; // return;
int channels = ps->output_channels;
int buf_samples = renderer->samples_to_do;
int to_do = ps->pad_begin_left; int to_do = ps->pad_begin_left;
if (to_do > buf_samples) if (to_do > sbuf->samples)
to_do = buf_samples; to_do = sbuf->samples;
sbuf_silence_s16(renderer->tmpbuf, to_do, channels, 0); sbuf_silence_part(sbuf, 0, to_do);
ps->pad_begin_left -= to_do; ps->pad_begin_left -= to_do;
sbuf->filled += to_do;
renderer->samples_done += to_do;
renderer->samples_to_do -= to_do;
renderer->tmpbuf += to_do * channels; /* as if mixed */
} }
// fades (modifies volumes) part of buf // fades (modifies volumes) part of buf
static void play_op_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { static void play_op_fade(VGMSTREAM* vgmstream, sbuf_t* sbuf) {
play_config_t* pc = &vgmstream->config; play_config_t* pc = &vgmstream->config;
play_state_t* ps = &vgmstream->pstate; play_state_t* ps = &vgmstream->pstate;
if (pc->play_forever || !ps->fade_left) if (pc->play_forever || !ps->fade_left)
return; return;
if (ps->play_position + samples_done < ps->fade_start) if (ps->play_position + sbuf->filled < ps->fade_start)
return; return;
int start, fade_pos; int start, fade_pos;
int channels = ps->output_channels;
int32_t to_do = ps->fade_left; 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 = sbuf->filled - (ps->play_position + sbuf->filled - ps->fade_start);
fade_pos = 0; fade_pos = 0;
} }
else { else {
@ -247,62 +230,51 @@ static void play_op_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done)
fade_pos = ps->play_position - ps->fade_start; fade_pos = ps->play_position - ps->fade_start;
} }
if (to_do > samples_done - start) if (to_do > sbuf->filled - start)
to_do = samples_done - start; to_do = sbuf->filled - start;
//TODO: use delta fadedness to improve performance? sbuf_fadeout(sbuf, start, to_do, fade_pos, ps->fade_duration);
for (int s = start; s < start + to_do; s++, fade_pos++) {
double fadedness = (double)(ps->fade_duration - fade_pos) / ps->fade_duration;
for (int ch = 0; ch < channels; ch++) {
buf[s * channels + ch] = (sample_t)(buf[s*channels + ch] * fadedness);
}
}
ps->fade_left -= to_do; ps->fade_left -= to_do;
/* next samples after fade end would be pad end/silence */
sbuf_silence_s16(buf, samples_done, channels, (start + to_do));
} }
// adds null samples after decode // adds null samples after decode
// pad-end works like fades, where part of buf is samples and part is padding (blank) // pad-end works like fades, where part of buf is samples and part is padding (blank)
// (beyond pad end normally is silence, except with segmented layout) // (beyond pad end normally is silence, except with segmented layout)
static int play_op_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) { static void play_op_pad_end(VGMSTREAM* vgmstream, sbuf_t* sbuf) {
play_config_t* pc = &vgmstream->config; play_config_t* pc = &vgmstream->config;
play_state_t* ps = &vgmstream->pstate; play_state_t* ps = &vgmstream->pstate;
if (pc->play_forever) if (pc->play_forever)
return 0; return;
if (samples_done == 0) if (ps->play_position + sbuf->filled < ps->pad_end_start)
return 0; return;
if (ps->play_position + samples_done < ps->pad_end_start)
return 0;
int channels = vgmstream->pstate.output_channels;
int skip = 0;
int32_t to_do;
int start;
int to_do;
if (ps->play_position < ps->pad_end_start) { if (ps->play_position < ps->pad_end_start) {
skip = ps->pad_end_start - ps->play_position; start = ps->pad_end_start - ps->play_position;
to_do = ps->pad_end_duration; to_do = ps->pad_end_duration;
} }
else { else {
skip = 0; start = 0;
to_do = (ps->pad_end_start + ps->pad_end_duration) - ps->play_position; to_do = (ps->pad_end_start + ps->pad_end_duration) - ps->play_position;
} }
if (to_do > samples_done - skip) if (to_do > sbuf->filled - start)
to_do = samples_done - skip; to_do = sbuf->filled - start;
sbuf_silence_s16(buf, to_do + skip, channels, skip); sbuf_silence_part(sbuf, start, to_do);
return skip + to_do;
//TO-DO: this never adds samples and just silences them, since decoder always returns something
//sbuf->filled += ?
} }
// clamp final play_position + done samples. Probably doesn't matter, but just in case. // clamp final play_position + done samples. Probably doesn't matter, but just in case.
static void play_adjust_totals(VGMSTREAM* vgmstream, render_helper_t* renderer, int sample_count) { static void play_adjust_totals(VGMSTREAM* vgmstream, sbuf_t* sbuf, int sample_count) {
play_state_t* ps = &vgmstream->pstate; play_state_t* ps = &vgmstream->pstate;
ps->play_position += renderer->samples_done; ps->play_position += sbuf->filled;
/* usually only happens when mixing layers of different lengths (where decoder keeps calling render) */ /* usually only happens when mixing layers of different lengths (where decoder keeps calling render) */
if (!vgmstream->config.play_forever && ps->play_position > ps->play_duration) { if (!vgmstream->config.play_forever && ps->play_position > ps->play_duration) {
@ -310,7 +282,7 @@ static void play_adjust_totals(VGMSTREAM* vgmstream, render_helper_t* renderer,
if (excess > sample_count) if (excess > sample_count)
excess = sample_count; excess = sample_count;
renderer->samples_done = (sample_count - excess); sbuf->filled = (sample_count - excess);
ps->play_position = ps->play_duration; ps->play_position = ps->play_duration;
} }
@ -326,52 +298,51 @@ static void play_adjust_totals(VGMSTREAM* vgmstream, render_helper_t* renderer,
* \ end-fade | * \ end-fade |
* *
* Which part we are in depends on play_position. Because vgmstream render's * Which part we are in depends on play_position. Because vgmstream render's
* buf may fall anywhere in the middle of all that. Since some ops add "fake" (non-decoded) * buf may fall anywhere in the middle of all that. Some ops add "fake" (non-decoded)
* samples to buf, we need to * samples to buf.
*/ */
int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
render_helper_t renderer = {0}; sbuf_t sbuf = {0};
renderer.tmpbuf = buf; sbuf_init_s16(&sbuf, buf, sample_count, vgmstream->channels);
renderer.samples_done = 0;
renderer.samples_to_do = sample_count;
//sbuf_init16(&renderer.sbuf, buf, sample_count, vgmstream->channels); /* simple mode with no play settings (just skip everything below) */
/* simple mode with no settings (just skip everything below) */
if (!vgmstream->config_enabled) { if (!vgmstream->config_enabled) {
render_layout(buf, renderer.samples_to_do, vgmstream); render_layout(buf, sample_count, vgmstream);
mix_vgmstream(buf, renderer.samples_to_do, vgmstream); sbuf.filled = sample_count;
return renderer.samples_to_do;
mix_vgmstream(&sbuf, vgmstream);
return sample_count;
} }
/* adds empty samples to buf and moves it */ /* trim decoder output (may go anywhere before main render since it doesn't use render output, but easier first) */
play_op_pad_begin(vgmstream, &renderer); play_op_trim(vgmstream, &sbuf);
/* trim decoder output (may go anywhere before main render since it doesn't use render output) */ /* adds empty samples to buf and moves it */
play_op_trim(vgmstream, &renderer); play_op_pad_begin(vgmstream, &sbuf);
/* main decode */ /* main decode */
int done = render_layout(renderer.tmpbuf, renderer.samples_to_do, vgmstream); sbuf_t sbuf_tmp = sbuf;
sbuf_consume(&sbuf_tmp, sbuf_tmp.filled);
int done = render_layout(sbuf_tmp.buf, sbuf_tmp.samples, vgmstream);
sbuf.filled += done;
mix_vgmstream(renderer.tmpbuf, done, vgmstream); mix_vgmstream(&sbuf, vgmstream);
sbuf.channels = vgmstream->pstate.output_channels;
/* simple fadeout over decoded data (after mixing since usually results in less samples) */ /* simple fadeout over decoded data (after mixing since usually results in less samples) */
play_op_fade(vgmstream, renderer.tmpbuf, done); play_op_fade(vgmstream, &sbuf);
/* silence leftover buf samples (after fade as rarely may mix decoded buf + trim samples when no fade is set) /* silence leftover buf samples (after fade as rarely may mix decoded buf + trim samples when no fade is set)
* (could be done before render to "consume" buf but doesn't matter much) */ * (could be done before render to "consume" buf but doesn't matter much) */
play_op_pad_end(vgmstream, renderer.tmpbuf, done); play_op_pad_end(vgmstream, &sbuf);
renderer.samples_done += done;
//renderer.samples_to_do -= done; //not useful at this point
//renderer.tmpbuf += done * vgmstream->pstate.output_channels;
play_adjust_totals(vgmstream, &renderer, sample_count); play_adjust_totals(vgmstream, &sbuf, sample_count);
return renderer.samples_done; return sbuf.filled;
} }

View File

@ -22,6 +22,41 @@ void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels) {
} }
static int get_sample_size(sfmt_t fmt) {
switch(fmt) {
case SFMT_F32:
case SFMT_FLT:
return 0x04;
case SFMT_S16:
return 0x02;
default:
return 0;
}
}
void* sbuf_get_filled_buf(sbuf_t* sbuf) {
int sample_size = get_sample_size(sbuf->fmt);
uint8_t* buf = sbuf->buf;
buf += sbuf->filled * sbuf->channels * sample_size;
return buf;
}
void sbuf_consume(sbuf_t* sbuf, int count) {
int sample_size = get_sample_size(sbuf->fmt);
if (sample_size <= 0)
return;
if (count > sbuf->samples || count > sbuf->filled) //TODO?
return;
uint8_t* buf = sbuf->buf;
buf += count * sbuf->channels * sample_size;
sbuf->buf = buf;
sbuf->filled -= count;
sbuf->samples -= count;
}
/* when casting float to int, value is simply truncated: /* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1 * - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be: * alts for more accurate rounding could be:
@ -46,6 +81,16 @@ static inline int float_to_int(float val) {
#endif #endif
} }
static inline int double_to_int(double val) {
#if 1
return (int)val;
#elif defined(_MSC_VER)
return (int)val;
#else
return lrintf(val);
#endif
}
//TODO decide if using float 1.0 style or 32767 style (fuzzy PCM when doing that) //TODO decide if using float 1.0 style or 32767 style (fuzzy PCM when doing that)
//TODO: maybe use macro-style templating (but kinda ugly) //TODO: maybe use macro-style templating (but kinda ugly)
void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf) { void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf) {
@ -136,7 +181,19 @@ void sbuf_copy_layers(sample_t* dst, int dst_channels, sample_t* src, int src_ch
} }
void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled) { void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled) {
memset(dst + filled * channels, 0, (samples - filled) * channels * sizeof(sample_t)); memset(dst + filled * channels, 0, (samples - filled) * channels * sizeof(sample_t));
}
void sbuf_silence_part(sbuf_t* sbuf, int from, int count) {
int sample_size = get_sample_size(sbuf->fmt);
uint8_t* buf = sbuf->buf;
buf += from * sbuf->channels * sample_size;
memset(buf, 0, count * sbuf->channels * sample_size);
}
void sbuf_silence_rest(sbuf_t* sbuf) {
sbuf_silence_part(sbuf, sbuf->filled, sbuf->samples - sbuf->filled);
} }
bool sbuf_realloc(sample_t** dst, int samples, int channels) { bool sbuf_realloc(sample_t** dst, int samples, int channels) {
@ -146,3 +203,49 @@ bool sbuf_realloc(sample_t** dst, int samples, int channels) {
*dst = outbuf_re; *dst = outbuf_re;
return true; return true;
} }
void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration) {
//TODO: use interpolated fadedness to improve performance?
//TODO: use float fadedness?
int s = start * sbuf->channels;
int s_end = (start + to_do) * sbuf->channels;
switch(sbuf->fmt) {
case SFMT_S16: {
int16_t* buf = sbuf->buf;
while (s < s_end) {
double fadedness = (double)(fade_duration - fade_pos) / fade_duration;
fade_pos++;
for (int ch = 0; ch < sbuf->channels; ch++) {
buf[s] = double_to_int(buf[s] * fadedness);
s++;
}
}
break;
}
case SFMT_FLT:
case SFMT_F32: {
float* buf = sbuf->buf;
while (s < s_end) {
double fadedness = (double)(fade_duration - fade_pos) / fade_duration;
fade_pos++;
for (int ch = 0; ch < sbuf->channels; ch++) {
buf[s] = double_to_int(buf[s] * fadedness);
s++;
}
}
break;
}
default:
break;
}
/* next samples after fade end would be pad end/silence */
int count = sbuf->filled - (start + to_do);
sbuf_silence_part(sbuf, start + to_do, count);
}

View File

@ -15,7 +15,7 @@ typedef enum {
} sfmt_t; } sfmt_t;
/* simple buffer info for internal mixing /* simple buffer info to pass around, for internal mixing
* meant to held existing sound buffer pointers rather than alloc'ing directly (some ops will swap/move its internals) */ * meant to held existing sound buffer pointers rather than alloc'ing directly (some ops will swap/move its internals) */
typedef struct { typedef struct {
void* buf; /* current sample buffer */ void* buf; /* current sample buffer */
@ -30,9 +30,12 @@ void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels);
void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels); void sbuf_init_f32(sbuf_t* sbuf, float* buf, int samples, int channels);
void* sbuf_get_filled_buf(sbuf_t* sbuf);
//void sbuf_clamp(sbuf_t* sbuf, int samples); //void sbuf_clamp(sbuf_t* sbuf, int samples);
//void sbuf_consume(sbuf_t* sbuf, int samples); /* move buf by samples amount to simplify some code (will lose base buf pointer) */
void sbuf_consume(sbuf_t* sbuf, int count);
/* it's probably slightly faster to make those inline'd, but aren't called that often to matter (given big enough total samples) */ /* it's probably slightly faster to make those inline'd, but aren't called that often to matter (given big enough total samples) */
@ -46,7 +49,11 @@ void sbuf_copy_samples(sample_t* dst, int dst_channels, sample_t* src, int src_c
void sbuf_copy_layers(sample_t* dst, int dst_channels, sample_t* src, int src_channels, int samples_to_do, int samples_filled, int dst_ch_start); void sbuf_copy_layers(sample_t* dst, int dst_channels, sample_t* src, int src_channels, int samples_to_do, int samples_filled, int dst_ch_start);
void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled); void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled);
void sbuf_silence_rest(sbuf_t* sbuf);
void sbuf_silence_part(sbuf_t* sbuf, int from, int count);
bool sbuf_realloc(sample_t** dst, int samples, int channels); bool sbuf_realloc(sample_t** dst, int samples, int channels);
void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration);
#endif #endif