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;
}
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 */
if (!mixer_is_active(vgmstream->mixer))
return;
sbuf_t sbuf_tmp;
sbuf_t* sbuf = &sbuf_tmp;
sbuf_init_s16(sbuf, outbuf, sample_count, vgmstream->channels);
sbuf->filled = sbuf->samples;
//sbuf_t sbuf_tmp;
//sbuf_t* sbuf = &sbuf_tmp;
//sbuf_init_s16(sbuf, outbuf, sample_count, vgmstream->channels);
//sbuf->filled = sbuf->samples;
int32_t current_pos = get_current_pos(vgmstream, sbuf->filled);

View File

@ -3,10 +3,11 @@
#include "../vgmstream.h"
#include "../util/log.h"
#include "sbuf.h"
/* Applies mixing commands to the sample buffer. Mixing must be externally enabled and
* 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.
* 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
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;
if (!ps->trim_begin_left)
return;
if (!renderer->samples_to_do)
if (sbuf->samples <= 0)
return;
// simpler using external buf?
//sample_t* tmpbuf = vgmstream->tmpbuf;
//size_t tmpbuf_size = vgmstream->tmpbuf_size;
//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) {
int to_do = ps->trim_begin_left;
if (to_do > buf_samples)
to_do = buf_samples;
if (to_do > sbuf->samples)
to_do = sbuf->samples;
render_layout(tmpbuf, to_do, vgmstream);
render_layout(sbuf->buf, to_do, vgmstream);
/* no mixing */
ps->trim_begin_left -= to_do;
}
}
// 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;
if (!ps->pad_begin_left)
return;
//if (ps->play_position > ps->play_begin_duration) //implicit
// return;
int channels = ps->output_channels;
int buf_samples = renderer->samples_to_do;
int to_do = ps->pad_begin_left;
if (to_do > buf_samples)
to_do = buf_samples;
if (to_do > sbuf->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;
renderer->samples_done += to_do;
renderer->samples_to_do -= to_do;
renderer->tmpbuf += to_do * channels; /* as if mixed */
sbuf->filled += to_do;
}
// 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_state_t* ps = &vgmstream->pstate;
if (pc->play_forever || !ps->fade_left)
return;
if (ps->play_position + samples_done < ps->fade_start)
if (ps->play_position + sbuf->filled < ps->fade_start)
return;
int start, fade_pos;
int channels = ps->output_channels;
int32_t to_do = ps->fade_left;
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;
}
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;
}
if (to_do > samples_done - start)
to_do = samples_done - start;
if (to_do > sbuf->filled - start)
to_do = sbuf->filled - start;
//TODO: use delta fadedness to improve performance?
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);
}
}
sbuf_fadeout(sbuf, start, to_do, fade_pos, ps->fade_duration);
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
// 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)
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_state_t* ps = &vgmstream->pstate;
if (pc->play_forever)
return 0;
if (samples_done == 0)
return 0;
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;
return;
if (ps->play_position + sbuf->filled < ps->pad_end_start)
return;
int start;
int to_do;
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;
}
else {
skip = 0;
start = 0;
to_do = (ps->pad_end_start + ps->pad_end_duration) - ps->play_position;
}
if (to_do > samples_done - skip)
to_do = samples_done - skip;
if (to_do > sbuf->filled - start)
to_do = sbuf->filled - start;
sbuf_silence_s16(buf, to_do + skip, channels, skip);
return skip + to_do;
sbuf_silence_part(sbuf, start, 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.
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;
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) */
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)
excess = sample_count;
renderer->samples_done = (sample_count - excess);
sbuf->filled = (sample_count - excess);
ps->play_position = ps->play_duration;
}
@ -326,52 +298,51 @@ static void play_adjust_totals(VGMSTREAM* vgmstream, render_helper_t* renderer,
* \ end-fade |
*
* 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)
* samples to buf, we need to
* buf may fall anywhere in the middle of all that. Some ops add "fake" (non-decoded)
* samples to buf.
*/
int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
render_helper_t renderer = {0};
renderer.tmpbuf = buf;
renderer.samples_done = 0;
renderer.samples_to_do = sample_count;
sbuf_t sbuf = {0};
sbuf_init_s16(&sbuf, buf, sample_count, vgmstream->channels);
//sbuf_init16(&renderer.sbuf, buf, sample_count, vgmstream->channels);
/* simple mode with no settings (just skip everything below) */
/* simple mode with no play settings (just skip everything below) */
if (!vgmstream->config_enabled) {
render_layout(buf, renderer.samples_to_do, vgmstream);
mix_vgmstream(buf, renderer.samples_to_do, vgmstream);
return renderer.samples_to_do;
render_layout(buf, sample_count, vgmstream);
sbuf.filled = sample_count;
mix_vgmstream(&sbuf, vgmstream);
return sample_count;
}
/* adds empty samples to buf and moves it */
play_op_pad_begin(vgmstream, &renderer);
/* trim decoder output (may go anywhere before main render since it doesn't use render output, but easier first) */
play_op_trim(vgmstream, &sbuf);
/* trim decoder output (may go anywhere before main render since it doesn't use render output) */
play_op_trim(vgmstream, &renderer);
/* adds empty samples to buf and moves it */
play_op_pad_begin(vgmstream, &sbuf);
/* 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) */
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)
* (could be done before render to "consume" buf but doesn't matter much) */
play_op_pad_end(vgmstream, renderer.tmpbuf, done);
renderer.samples_done += done;
//renderer.samples_to_do -= done; //not useful at this point
//renderer.tmpbuf += done * vgmstream->pstate.output_channels;
play_op_pad_end(vgmstream, &sbuf);
play_adjust_totals(vgmstream, &renderer, sample_count);
return renderer.samples_done;
play_adjust_totals(vgmstream, &sbuf, sample_count);
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:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
@ -46,6 +81,16 @@ static inline int float_to_int(float val) {
#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: maybe use macro-style templating (but kinda ugly)
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) {
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) {
@ -146,3 +203,49 @@ bool sbuf_realloc(sample_t** dst, int samples, int channels) {
*dst = outbuf_re;
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;
/* 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) */
typedef struct {
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_get_filled_buf(sbuf_t* sbuf);
//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) */
@ -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_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);
void sbuf_fadeout(sbuf_t* sbuf, int start, int to_do, int fade_pos, int fade_duration);
#endif