mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 06:50:20 +01:00
cleanup: float stuff
This commit is contained in:
parent
28c6d64693
commit
045e1c9107
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
105
src/base/sbuf.c
105
src/base/sbuf.c
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user