Merge pull request #1589 from bnnm/api-misc9

- Fix .nfx extension
- Fix extensionless files in foobar
- Fix edge case with looping + non-looping layers
- cleanup
This commit is contained in:
bnnm 2024-09-02 00:56:42 +02:00 committed by GitHub
commit 8205c522c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 417 additions and 283 deletions

View File

@ -286,19 +286,20 @@ like foobar or Winamp don't react well to that, they may be renamed to these
- `.stm` to `.lstm` (Rockstar STM)
- `.wav` to `.lwav` (standard WAV, various formats)
- `.wma` to `.lwma` (standard WMA)
- `.(any)` to `.vgmstream` (FFmpeg formats or TXTH)
- `.(unknown)` to `.vgmstream` (TXTH formats / extracted bigfiles without extension)
Command line tools don't have this restriction and will accept the original
filename.
filename. Note that vgmstream also accepts certain extension-less files as-is too.
The main advantage of renaming here is that vgmstream may use the file's internal
loop info, or apply subtle fixes, but is also limited in some ways (like ignoring
standard tags). `.vgmstream` is a catch-all extension that may work as a last resort
to make a file playable.
The main reason of renaming is forcing the player to use vgmstream instead of its
internal decoder. vgmstream then may use the file's loop info, or apply small
fixes, but is also limited in some ways such as regular tagged files (like `.ogg`)
won't show tags when played through vgmstream (since video game `.ogg` rarely
have anything worth showing).
Some plugins have options that allow "*common extensions*" to be played, making any
renaming unnecessary. You may need to adjust plugin priority in player's options
first. Note that vgmstream also accepts certain extension-less files as-is too.
first, but the same issues apply (will lose tags).
Similarly, vgmstream has a curated list of known extensions, that plugins may take
into account and ignore unknowns. Through *TXTH* you can make unknown files playable,

View File

@ -49,10 +49,12 @@ static void prepare_mixing(libvgmstream_priv_t* priv, libvgmstream_options_t* op
vgmstream_mixing_stereo_only(priv->vgmstream, opt->stereo_track - 1);
}
if (priv->cfg.force_pcm16)
if (priv->cfg.force_pcm16) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_S16);
else if (priv->cfg.force_float)
}
else if (priv->cfg.force_float) {
mixing_macro_output_sample_format(priv->vgmstream, SFMT_FLT);
}
vgmstream_mixing_enable(priv->vgmstream, INTERNAL_BUF_SAMPLES, NULL /*&input_channels*/, NULL /*&output_channels*/);
}

View File

@ -7,26 +7,22 @@
#include <math.h>
#include <limits.h>
//TODO simplify
/**
* Mixer modifies decoded sample buffer before final output. This is implemented
* mostly with simplicity in mind rather than performance. Process:
* with simplicity in mind rather than performance. Process:
* - detect if mixing applies at current moment or exit (mini performance optimization)
* - copy/upgrade buf to float mixbuf if needed
* - do mixing ops
* - copy/downgrade mixbuf to original buf if needed
*
* Mixing may add or remove channels. input_channels is the buf's original channels,
* and output_channels the resulting buf's channels. buf and mixbuf must be
* as big as max channels (mixing_channels).
*
* Mixing ops are added by a meta (ex. TXTP) or plugin through the API. Non-sensical
* mixes are ignored (to avoid rechecking every time).
*
* Currently, mixing must be manually enabled before starting to decode, because plugins
* need to setup bigger bufs when upmixing. (to be changed)
* Mixing ops are added by a meta (ex. TXTP) or plugins through API. Non-sensical config
* is ignored on add (to avoid rechecking every time).
*
* segmented/layered layouts handle mixing on their own.
* Mixing may add or remove channels or change sample format. external buf and internal mixbuf
* are expected to be as big as needed. Currently, mixing must be manually enabled before starting
* to decode, because plugins need to setup appropriate bufs. (to be changed)
*
* segmented/layered layouts handle mixing vgmstream sample bufs on their own.
*/
mixer_t* mixer_init(int channels) {

View File

@ -8,29 +8,7 @@
#include "mixer_priv.h"
#include "sbuf.h"
//TODO simplify
/**
* Mixer modifies decoded sample buffer before final output. This is implemented
* mostly with simplicity in mind rather than performance. Process:
* - detect if mixing applies at current moment or exit (mini performance optimization)
* - copy/upgrade buf to float mixbuf if needed
* - do mixing ops
* - copy/downgrade mixbuf to original buf if needed
*
* Mixing may add or remove channels. input_channels is the buf's original channels,
* and output_channels the resulting buf's channels. buf and mixbuf must be
* as big as max channels (mixing_channels).
*
* Mixing ops are added by a meta (ex. TXTP) or plugin through the API. Non-sensical
* mixes are ignored (to avoid rechecking every time).
*
* Currently, mixing must be manually enabled before starting to decode, because plugins
* need to setup bigger bufs when upmixing. (to be changed)
*
* segmented/layered layouts handle mixing on their own.
*/
/* ******************************************************************* */
/* Wrapper/helpers for vgmstream's "mixer", which does main sample buffer transformations */
static int32_t get_current_pos(VGMSTREAM* vgmstream, int32_t sample_count) {
int32_t current_pos;
@ -166,6 +144,7 @@ void mixing_info(VGMSTREAM* vgmstream, int* p_input_channels, int* p_output_chan
sfmt_t mixing_get_input_sample_type(VGMSTREAM* vgmstream) {
// TODO: check vgmstream
// TODO: on layered/segments, detect biggest value and use that (ex. if one of the layers uses flt > flt)
return SFMT_S16;
}

View File

@ -12,7 +12,6 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
const char** extension_list;
size_t extension_list_len;
const char* extension;
int i;
bool is_extension = cfg && cfg->is_extension;
bool reject_extensionless = cfg && cfg->reject_extensionless;
@ -29,19 +28,19 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
/* some metas accept extensionless files, but make sure it's not a path (unlikely but...) */
if (strlen(extension) <= 0) {
int len = strlen(filename); /* foobar passes an extension as so len may be still 0 */
if (len <= 0 && is_extension)
return 0;
if (len <= 0 && !is_extension)
return false;
if (len > 1 && (filename[len - 1] == '/' || filename[len - 1] == '\\'))
return 0;
return false;
return !reject_extensionless;
}
/* try in default list */
if (!skip_standard) {
extension_list = vgmstream_get_formats(&extension_list_len);
for (i = 0; i < extension_list_len; i++) {
for (int i = 0; i < extension_list_len; i++) {
if (strcasecmp(extension, extension_list[i]) == 0) {
return 1;
return true;
}
}
}
@ -49,29 +48,29 @@ int vgmstream_ctx_is_valid(const char* filename, vgmstream_ctx_valid_cfg *cfg) {
/* try in common extensions */
if (accept_common) {
extension_list = vgmstream_get_common_formats(&extension_list_len);
for (i = 0; i < extension_list_len; i++) {
for (int i = 0; i < extension_list_len; i++) {
if (strcasecmp(extension, extension_list[i]) == 0)
return 1;
return true;
}
}
/* allow anything not in the normal list but not in common extensions */
if (accept_unknown) {
int is_common = 0;
bool is_common = false;
extension_list = vgmstream_get_common_formats(&extension_list_len);
for (i = 0; i < extension_list_len; i++) {
for (int i = 0; i < extension_list_len; i++) {
if (strcasecmp(extension, extension_list[i]) == 0) {
is_common = 1;
is_common = true;
break;
}
}
if (!is_common)
return 1;
return true;
}
return 0;
return false;
}
void vgmstream_get_title(char* buf, int buf_len, const char* filename, VGMSTREAM* vgmstream, vgmstream_title_t* cfg) {

View File

@ -3,7 +3,6 @@
#include "render.h"
#include "decode.h"
#include "mixing.h"
#include "sbuf.h"
/* VGMSTREAM RENDERING
@ -73,7 +72,10 @@ void render_reset(VGMSTREAM* vgmstream) {
}
}
int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
int render_layout(sbuf_t* sbuf, VGMSTREAM* vgmstream) {
void* buf = sbuf->buf;
int sample_count = sbuf->samples;
if (sample_count == 0)
return 0;
@ -82,9 +84,7 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
// nothing to decode: return blank buf (not ">=" to allow layouts to loop in some cases when == happens)
if (vgmstream->current_sample > vgmstream->num_samples) {
int channels = vgmstream->channels;
sbuf_silence_s16(buf, sample_count, channels, 0);
sbuf_silence_rest(sbuf);
return sample_count;
}
@ -137,10 +137,10 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
render_vgmstream_blocked(buf, sample_count, vgmstream);
break;
case layout_segmented:
render_vgmstream_segmented(buf, sample_count,vgmstream);
render_vgmstream_segmented(sbuf, vgmstream);
break;
case layout_layered:
render_vgmstream_layered(buf, sample_count, vgmstream);
render_vgmstream_layered(sbuf, vgmstream);
break;
default:
break;
@ -148,7 +148,6 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
// decode past stream samples: blank rest of buf
if (vgmstream->current_sample > vgmstream->num_samples) {
int channels = vgmstream->channels;
int32_t excess, decoded;
excess = (vgmstream->current_sample - vgmstream->num_samples);
@ -156,7 +155,7 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
excess = sample_count;
decoded = sample_count - excess;
sbuf_silence_s16(buf, sample_count, channels, decoded);
sbuf_silence_part(sbuf, decoded, sample_count - decoded);
return sample_count;
}
@ -174,19 +173,19 @@ static void play_op_trim(VGMSTREAM* vgmstream, sbuf_t* sbuf) {
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 */
sbuf_t sbuf_tmp = *sbuf;
int buf_samples = sbuf->samples;
while (ps->trim_begin_left) {
int to_do = ps->trim_begin_left;
if (to_do > sbuf->samples)
to_do = sbuf->samples;
if (to_do > buf_samples)
to_do = buf_samples;
render_layout(sbuf->buf, to_do, vgmstream);
sbuf_tmp.samples = to_do;
int done = render_layout(&sbuf_tmp, vgmstream);
/* no mixing */
ps->trim_begin_left -= to_do;
ps->trim_begin_left -= done;
}
}
@ -270,22 +269,25 @@ static void play_op_pad_end(VGMSTREAM* vgmstream, sbuf_t* sbuf) {
//sbuf->filled += ?
}
// clamp final play_position + done samples. Probably doesn't matter, but just in case.
static void play_adjust_totals(VGMSTREAM* vgmstream, sbuf_t* sbuf, int sample_count) {
// clamp final play_position + done samples. Probably doesn't matter (maybe for plugins checking length), but just in case.
static void play_adjust_totals(VGMSTREAM* vgmstream, sbuf_t* sbuf) {
play_state_t* ps = &vgmstream->pstate;
ps->play_position += sbuf->filled;
if (vgmstream->config.play_forever)
return;
if (ps->play_position <= ps->play_duration)
return;
/* 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) {
int excess = ps->play_position - ps->play_duration;
if (excess > sample_count)
excess = sample_count;
int excess = ps->play_position - ps->play_duration;
if (excess > sbuf->samples)
excess = sbuf->samples;
sbuf->filled = (sbuf->samples - excess);
sbuf->filled = (sample_count - excess);
ps->play_position = ps->play_duration;
}
/* clamp */
ps->play_position = ps->play_duration;
}
/*****************************************************************************/
@ -302,47 +304,50 @@ static void play_adjust_totals(VGMSTREAM* vgmstream, sbuf_t* sbuf, int sample_co
* samples to buf.
*/
int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
sbuf_t sbuf = {0};
sbuf_init_s16(&sbuf, buf, sample_count, vgmstream->channels);
int render_main(sbuf_t* sbuf, VGMSTREAM* vgmstream) {
/* simple mode with no play settings (just skip everything below) */
if (!vgmstream->config_enabled) {
render_layout(buf, sample_count, vgmstream);
sbuf.filled = sample_count;
render_layout(sbuf, vgmstream);
sbuf->filled = sbuf->samples;
mix_vgmstream(&sbuf, vgmstream);
mix_vgmstream(sbuf, vgmstream);
return sample_count;
return sbuf->filled;
}
/* trim decoder output (may go anywhere before main render since it doesn't use render output, but easier first) */
play_op_trim(vgmstream, &sbuf);
play_op_trim(vgmstream, sbuf);
/* adds empty samples to buf and moves it */
play_op_pad_begin(vgmstream, &sbuf);
play_op_pad_begin(vgmstream, sbuf);
/* main decode */
sbuf_t sbuf_tmp = sbuf;
/* main decode (use temp buf to "consume") */
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(&sbuf, vgmstream);
sbuf.channels = vgmstream->pstate.output_channels;
int done = render_layout(&sbuf_tmp, vgmstream);
sbuf->filled += done;
mix_vgmstream(sbuf, vgmstream);
/* simple fadeout over decoded data (after mixing since usually results in less samples) */
play_op_fade(vgmstream, &sbuf);
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, &sbuf);
play_op_pad_end(vgmstream, sbuf);
play_adjust_totals(vgmstream, &sbuf, sample_count);
return sbuf.filled;
play_adjust_totals(vgmstream, sbuf);
return sbuf->filled;
}
int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
sbuf_t sbuf = {0};
sbuf_init_s16(&sbuf, buf, sample_count, vgmstream->channels);
return render_main(&sbuf, vgmstream);
}

View File

@ -2,10 +2,12 @@
#define _RENDER_H
#include "../vgmstream.h"
#include "sbuf.h"
void render_free(VGMSTREAM* vgmstream);
void render_reset(VGMSTREAM* vgmstream);
int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream);
int render_layout(sbuf_t* sbuf, VGMSTREAM* vgmstream);
int render_main(sbuf_t* sbuf, VGMSTREAM* vgmstream);
#endif

View File

@ -5,6 +5,14 @@
#include "sbuf.h"
void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels) {
memset(sbuf, 0, sizeof(sbuf_t));
sbuf->buf = buf;
sbuf->samples = samples;
sbuf->channels = channels;
sbuf->fmt = format;
}
void sbuf_init_s16(sbuf_t* sbuf, int16_t* buf, int samples, int channels) {
memset(sbuf, 0, sizeof(sbuf_t));
sbuf->buf = buf;
@ -34,7 +42,6 @@ int sfmt_get_sample_size(sfmt_t fmt) {
}
}
#if 0
void* sbuf_get_filled_buf(sbuf_t* sbuf) {
int sample_size = sfmt_get_sample_size(sbuf->fmt);
@ -42,7 +49,6 @@ void* sbuf_get_filled_buf(sbuf_t* sbuf) {
buf += sbuf->filled * sbuf->channels * sample_size;
return buf;
}
#endif
void sbuf_consume(sbuf_t* sbuf, int count) {
int sample_size = sfmt_get_sample_size(sbuf->fmt);
@ -151,47 +157,196 @@ void sbuf_copy_from_f32(sbuf_t* sbuf, float* src) {
}
}
void sbuf_copy_segments(sample_t* dst, int dst_channels, sample_t* src, int src_channels, int samples_to_do, int samples_filled) {
int pos = samples_filled * dst_channels;
if (src_channels == dst_channels) { /* most common and probably faster */
for (int s = 0; s < samples_to_do * dst_channels; s++) {
dst[pos + s] = src[s];
}
/* ugly thing to avoid repeating functions */
#define sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max) \
while (src_pos < src_max) { \
dst[dst_pos++] = src[src_pos++]; \
}
else {
for (int s = 0; s < samples_to_do; s++) {
for (int ch = 0; ch < src_channels; ch++) {
dst[pos + s * dst_channels + ch] = src[s * src_channels + ch];
}
for (int ch = src_channels; ch < dst_channels; ch++) {
dst[pos + s * dst_channels + ch] = 0;
#define sbuf_copy_segments_internal_s16(dst, src, src_pos, dst_pos, src_max, value) \
while (src_pos < src_max) { \
dst[dst_pos++] = clamp16(float_to_int(src[src_pos++] * value)); \
}
#define sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, value) \
while (src_pos < src_max) { \
dst[dst_pos++] = float_to_int(src[src_pos++] * value); \
}
void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc) {
/* uncommon so probably fine albeit slower-ish, 0'd other channels first */
if (ssrc->channels != sdst->channels) {
sbuf_silence_part(sdst, sdst->filled, ssrc->filled);
sbuf_copy_layers(sdst, ssrc, 0, ssrc->filled);
#if 0
// "faster" but lots of extra ifs, not worth it
while (src_pos < src_max) {
for (int ch = 0; ch < dst_channels; ch++) {
dst[dst_pos++] = ch >= src_channels ? 0 : src[src_pos++];
}
}
#endif
return;
}
int src_pos = 0;
int dst_pos = sdst->filled * sdst->channels;
int src_max = ssrc->filled * ssrc->channels;
// define all posible combos, probably there is a better way to handle this but...
if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_S16) {
int16_t* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max);
}
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max);
}
else if ((sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_F32) || (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_FLT)) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal(dst, src, src_pos, dst_pos, src_max);
}
// to s16
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_F32) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_s16(dst, src, src_pos, dst_pos, src_max, 1.0f);
}
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_FLT) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_s16(dst, src, src_pos, dst_pos, src_max, 32768.0f);
}
// to f32
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_FLT) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, 32768.0f);
}
// to flt
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, (1/32768.0f));
}
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_F32) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_segments_internal_flt(dst, src, src_pos, dst_pos, src_max, (1/32768.0f));
}
}
/* copy interleaving */
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) {
// dst_channels == src_channels isn't likely
for (int src_ch = 0; src_ch < src_channels; src_ch++) {
for (int s = 0; s < samples_to_do; s++) {
int src_pos = s * src_channels + src_ch;
int dst_pos = (samples_filled + s) * dst_channels + dst_ch_start;
dst[dst_pos] = src[src_pos];
}
dst_ch_start++;
//TODO fix missing ->channels
/* ugly thing to avoid repeating functions */
#define sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step) \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = src[src_pos++]; \
} \
dst_pos += dst_ch_step; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = 0; \
} \
dst_pos += dst_ch_step; \
}
}
bool sbuf_realloc(sample_t** dst, int samples, int channels) {
sample_t* outbuf_re = realloc(*dst, samples * channels * sizeof(sample_t));
if (!outbuf_re) return false;
// float +-1.0 <> pcm +-32768.0
#define sbuf_copy_layers_internal_s16(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, value) \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = clamp16(float_to_int(src[src_pos++] * value)); \
} \
dst_pos += dst_ch_step; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = 0; \
} \
dst_pos += dst_ch_step; \
}
*dst = outbuf_re;
return true;
// float +-1.0 <> pcm +-32768.0
#define sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, value) \
for (int s = 0; s < src_filled; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = float_to_int(src[src_pos++] * value); \
} \
dst_pos += dst_ch_step; \
} \
\
for (int s = src_filled; s < dst_expected; s++) { \
for (int src_ch = 0; src_ch < src_channels; src_ch++) { \
dst[dst_pos++] = 0; \
} \
dst_pos += dst_ch_step; \
}
/* copy interleaving: dst ch1 ch2 ch3 ch4 w/ src ch1 ch2 ch1 ch2 = only fill dst ch1 ch2 */
// dst_channels == src_channels isn't likely so ignore that optimization
// sometimes one layer has less samples than others and need to 0-fill rest
void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int dst_expected) {
int src_filled = ssrc->filled;
int src_channels = ssrc->channels;
int dst_ch_step = (sdst->channels - ssrc->channels); \
int src_pos = 0;
int dst_pos = sdst->filled * sdst->channels + dst_ch_start;
// define all posible combos, probably there is a better way to handle this but...
// 1:1
if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_S16) {
int16_t* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step);
}
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step);
}
else if ((sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_F32) || (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_FLT)) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step);
}
// to s16
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_F32) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_s16(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, 1.0f);
}
else if (sdst->fmt == SFMT_S16 && ssrc->fmt == SFMT_FLT) {
int16_t* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_s16(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, 32768.0f);
}
// to f32
else if (sdst->fmt == SFMT_F32 && ssrc->fmt == SFMT_FLT) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, 32768.0f);
}
// to flt
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_S16) {
float* dst = sdst->buf;
int16_t* src = ssrc->buf;
sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, (1/32768.0f));
}
else if (sdst->fmt == SFMT_FLT && ssrc->fmt == SFMT_F32) {
float* dst = sdst->buf;
float* src = ssrc->buf;
sbuf_copy_layers_internal_flt(dst, src, src_pos, dst_pos, src_filled, dst_expected, src_channels, dst_ch_step, (1/32768.0f));
}
}
void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled) {

View File

@ -27,24 +27,22 @@ typedef struct {
/* it's probably slightly faster to make some function inline'd, but aren't called that often to matter (given big enough total samples) */
void sbuf_init(sbuf_t* sbuf, sfmt_t format, void* buf, int samples, int channels);
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);
int sfmt_get_sample_size(sfmt_t fmt);
//void* sbuf_get_filled_buf(sbuf_t* sbuf);
//void sbuf_clamp(sbuf_t* sbuf, int samples);
void* sbuf_get_filled_buf(sbuf_t* sbuf);
/* move buf by samples amount to simplify some code (will lose base buf pointer) */
void sbuf_consume(sbuf_t* sbuf, int count);
// TODO decide if using float 1.0 style or 32767 style (fuzzy PCM changes when doing that)
/* helpers to copy between buffers; note they assume dst and src aren't the same buf */
void sbuf_copy_to_f32(float* dst, sbuf_t* sbuf);
void sbuf_copy_from_f32(sbuf_t* sbuf, float* src);
void sbuf_copy_segments(sample_t* dst, int dst_channels, sample_t* src, int src_channels, int samples_to_do, int samples_filled);
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);
bool sbuf_realloc(sample_t** dst, int samples, int channels);
void sbuf_copy_segments(sbuf_t* sdst, sbuf_t* ssrc);
void sbuf_copy_layers(sbuf_t* sdst, sbuf_t* ssrc, int dst_ch_start, int expected);
void sbuf_silence_s16(sample_t* dst, int samples, int channels, int filled);
void sbuf_silence_rest(sbuf_t* sbuf);

View File

@ -4,6 +4,7 @@
#include "decode.h"
#include "mixing.h"
#include "plugins.h"
#include "sbuf.h"
/* pretend decoder reached loop end so internal state is set like jumping to loop start
* (no effect in some layouts but that is ok) */
@ -18,16 +19,18 @@ static void seek_force_loop_end(VGMSTREAM* vgmstream, int loop_count) {
}
static void seek_force_decode(VGMSTREAM* vgmstream, int samples) {
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 */
void* tmpbuf = vgmstream->tmpbuf;
int buf_samples = vgmstream->tmpbuf_size / vgmstream->channels / sizeof(float); /* base decoder channels, no need to apply mixing */
sbuf_t sbuf_tmp;
sbuf_init(&sbuf_tmp, mixing_get_input_sample_type(vgmstream), tmpbuf, buf_samples, vgmstream->channels);
while (samples) {
int to_do = samples;
if (to_do > buf_samples)
to_do = buf_samples;
render_layout(tmpbuf, to_do, vgmstream);
sbuf_tmp.samples = to_do;
render_layout(&sbuf_tmp, vgmstream);
/* no mixing */
samples -= to_do;
}

View File

@ -12,7 +12,7 @@ typedef struct {
//TODO may be more useful with filled+consumed and not moving *samples?
} s16buf_t;
static void s16buf_silence(sample_t** p_outbuf, int32_t* p_samples_silence, int channels) {
static inline void s16buf_silence(sample_t** p_outbuf, int32_t* p_samples_silence, int channels) {
int samples_silence;
samples_silence = *p_samples_silence;
@ -23,7 +23,7 @@ static void s16buf_silence(sample_t** p_outbuf, int32_t* p_samples_silence, int
*p_samples_silence -= samples_silence;
}
static void s16buf_discard(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_discard) {
static inline void s16buf_discard(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_discard) {
int samples_discard;
samples_discard = *p_samples_discard;
@ -39,7 +39,7 @@ static void s16buf_discard(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_sampl
}
/* copy, move and mark consumed samples */
static void s16buf_consume(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_consume) {
static inline void s16buf_consume(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_consume) {
int samples_consume;
samples_consume = *p_samples_consume;

View File

@ -1,5 +1,5 @@
#include "coding.h"
#include "coding_utils_samples.h"
/* LucasArts' iMUSE decoder, mainly for VIMA (like IMA but with variable frame and code sizes).
* Reverse engineered from various .exe
@ -122,31 +122,6 @@ static const int8_t* index_tables_v2[8] = {
/* ************************** */
typedef struct {
/*const*/ int16_t* samples;
int filled;
int channels;
//todo may be more useful with filled/full? use 2 mark methods?
} sbuf_t;
/* copy, move and mark consumed samples */
static void sbuf_consume(sample_t** p_outbuf, int32_t* p_samples_to_do, sbuf_t* sbuf) {
int samples_consume;
samples_consume = *p_samples_to_do;
if (samples_consume > sbuf->filled)
samples_consume = sbuf->filled;
/* memcpy is safe when filled/samples_copy is 0 (but must pass non-NULL bufs) */
memcpy(*p_outbuf, sbuf->samples, samples_consume * sbuf->channels * sizeof(int16_t));
sbuf->samples += samples_consume * sbuf->channels;
sbuf->filled -= samples_consume;
*p_outbuf += samples_consume * sbuf->channels;
*p_samples_to_do -= samples_consume;
}
static int clamp_s32(int val, int min, int max) {
if (val > max)
return max;
@ -174,7 +149,7 @@ struct imuse_codec_data {
uint16_t adpcm_table[64 * 89];
/* state */
sbuf_t sbuf;
s16buf_t sbuf;
int current_block;
int16_t samples[MAX_BLOCK_SIZE / sizeof(int16_t) * MAX_CHANNELS];
};
@ -309,7 +284,7 @@ fail:
/* **************************************** */
static void decode_vima1(sbuf_t* sbuf, uint8_t* buf, size_t data_left, int block_num, uint16_t* adpcm_table) {
static void decode_vima1(s16buf_t* sbuf, uint8_t* buf, size_t data_left, int block_num, uint16_t* adpcm_table) {
int ch, i, j, s;
int bitpos;
int adpcm_history[MAX_CHANNELS] = {0};
@ -434,7 +409,7 @@ static int decode_block1(imuse_codec_data* data, uint8_t* block, size_t data_lef
return 1;
}
static void decode_data2(sbuf_t* sbuf, uint8_t* buf, size_t data_left, int block_num) {
static void decode_data2(s16buf_t* sbuf, uint8_t* buf, size_t data_left, int block_num) {
int i, j;
int channels = sbuf->channels;
@ -453,7 +428,7 @@ static void decode_data2(sbuf_t* sbuf, uint8_t* buf, size_t data_left, int block
}
}
static void decode_vima2(sbuf_t* sbuf, uint8_t* buf, size_t data_left, uint16_t* adpcm_table) {
static void decode_vima2(s16buf_t* sbuf, uint8_t* buf, size_t data_left, uint16_t* adpcm_table) {
int ch, i, s;
int bitpos;
int adpcm_history[MAX_CHANNELS] = {0};
@ -622,14 +597,14 @@ void decode_imuse(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do)
while (samples_to_do > 0) {
sbuf_t* sbuf = &data->sbuf;
s16buf_t* sbuf = &data->sbuf;
if (sbuf->filled == 0) {
ok = decode_block(sf, data);
if (!ok) goto fail;
}
sbuf_consume(&outbuf, &samples_to_do, sbuf);
s16buf_consume(&outbuf, sbuf, &samples_to_do);
}
return;

View File

@ -12,10 +12,10 @@ struct tac_codec_data {
int encoder_delay;
uint8_t buf[TAC_BLOCK_SIZE];
int feed_block;
bool feed_block;
off_t offset;
int16_t* samples;
int16_t samples[TAC_FRAME_SAMPLES * TAC_CHANNELS];
int frame_samples;
/* frame state */
@ -38,7 +38,7 @@ tac_codec_data* init_tac(STREAMFILE* sf) {
data->handle = tac_init(data->buf, bytes);
if (!data->handle) goto fail;
data->feed_block = 0; /* ok to use current block */
data->feed_block = false; /* ok to use current block */
data->offset = bytes;
data->channels = TAC_CHANNELS;
data->frame_samples = TAC_FRAME_SAMPLES;
@ -46,9 +46,6 @@ tac_codec_data* init_tac(STREAMFILE* sf) {
data->encoder_delay = 0;
data->samples_discard = data->encoder_delay;
data->samples = malloc(data->channels * data->frame_samples * sizeof(int16_t));
if (!data->samples) goto fail;
return data;
fail:
free_tac(data);
@ -56,58 +53,55 @@ fail:
}
static int decode_frame(tac_codec_data* data) {
static bool decode_frame(tac_codec_data* data) {
int err;
data->sbuf.samples = data->samples;
data->sbuf.channels = 2;
data->sbuf.channels = data->channels;
data->sbuf.filled = 0;
err = tac_decode_frame(data->handle, data->buf);
if (err == TAC_PROCESS_NEXT_BLOCK) {
data->feed_block = 1;
return 1;
data->feed_block = true;
return true;
}
if (err == TAC_PROCESS_DONE) {
VGM_LOG("TAC: process done (EOF) %i\n", err);
goto fail; /* shouldn't reach this */
return false; /* shouldn't reach this */
}
if (err != TAC_PROCESS_OK) {
VGM_LOG("TAC: process error %i\n", err);
goto fail;
return false;
}
tac_get_samples_pcm16(data->handle, data->sbuf.samples);
data->sbuf.filled = data->frame_samples;
return 1;
fail:
return 0;
return true;
}
static int read_frame(tac_codec_data* data, STREAMFILE* sf) {
static bool read_frame(tac_codec_data* data, STREAMFILE* sf) {
/* new block must be read only when signaled by lib */
if (data->feed_block) {
int bytes = read_streamfile(data->buf, data->offset, sizeof(data->buf), sf);
data->offset += bytes;
data->feed_block = 0;
if (bytes <= 0) goto fail; /* can read less that buf near EOF */
}
if (!data->feed_block)
return true;
int bytes = read_streamfile(data->buf, data->offset, sizeof(data->buf), sf);
data->offset += bytes;
data->feed_block = 0;
if (bytes <= 0) return false; /* can read less that buf near EOF */
return 1;
fail:
return 0;
return true;
}
void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
tac_codec_data* data = vgmstream->codec_data;
int ok;
bool ok;
while (samples_to_do > 0) {
@ -132,7 +126,7 @@ void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
fail:
/* on error just put some 0 samples */
VGM_LOG("TAC: decode fail at %x, missing %i samples\n", (uint32_t)data->offset, samples_to_do);
s16buf_silence(&outbuf, &samples_to_do, data->channels);
s16buf_silence(&outbuf, &samples_to_do, data->sbuf.channels);
}
@ -141,12 +135,10 @@ void reset_tac(tac_codec_data* data) {
tac_reset(data->handle);
data->offset = 0;
data->feed_block = 1;
data->feed_block = true;
data->offset = 0x00;
data->sbuf.filled = 0;
data->samples_discard = data->encoder_delay;
return;
}
void seek_tac(tac_codec_data* data, int32_t num_sample) {
@ -162,18 +154,18 @@ void seek_tac(tac_codec_data* data, int32_t num_sample) {
if (loop_sample == num_sample) {
tac_set_loop(data->handle); /* direct looping */
data->samples_discard = hdr->loop_discard;
data->feed_block = true;
data->offset = hdr->loop_offset;
data->feed_block = 1;
data->sbuf.filled = 0;
data->samples_discard = hdr->loop_discard;
}
else {
tac_reset(data->handle);
data->samples_discard = num_sample;
data->offset = 0;
data->feed_block = 1;
data->feed_block = true;
data->offset = 0x00;
data->sbuf.filled = 0;
data->samples_discard = num_sample;
}
}
@ -182,6 +174,5 @@ void free_tac(tac_codec_data* data) {
return;
tac_free(data->handle);
free(data->samples);
free(data);
}

View File

@ -392,7 +392,7 @@ static const char* extension_list[] = {
"npsf", //fake extension/header id for .nps (in bigfiles)
"nsa",
"nsopus",
"ntx",
"nfx",
"nub",
"nub2",
"nus3audio",

View File

@ -4,6 +4,7 @@
#include "../base/mixing.h"
#include "../base/plugins.h"
#include "../base/sbuf.h"
#include "../base/render.h"
#define VGMSTREAM_MAX_LAYERS 255
#define VGMSTREAM_LAYER_SAMPLE_BUFFER 8192
@ -12,14 +13,16 @@
/* Decodes samples for layered streams.
* Each decoded vgmstream 'layer' (which may have different codecs and number of channels)
* is mixed into a final buffer, creating a single super-vgmstream. */
void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) {
void render_vgmstream_layered(sbuf_t* sdst, VGMSTREAM* vgmstream) {
layered_layout_data* data = vgmstream->layout_data;
sbuf_t ssrc_tmp;
sbuf_t* ssrc = &ssrc_tmp;
int samples_per_frame = VGMSTREAM_LAYER_SAMPLE_BUFFER;
int samples_this_block = vgmstream->num_samples; /* do all samples if possible */
int samples_filled = 0;
while (samples_filled < sample_count) {
//int samples_filled = 0;
while (sdst->filled < sdst->samples) {
int ch;
if (vgmstream->loop_flag && decode_do_loop(vgmstream)) {
@ -28,8 +31,8 @@ void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM*
}
int samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream);
if (samples_to_do > sample_count - samples_filled)
samples_to_do = sample_count - samples_filled;
if (samples_to_do > sdst->samples - sdst->filled)
samples_to_do = sdst->samples - sdst->filled;
if (samples_to_do <= 0) { /* when decoding more than num_samples */
VGM_LOG_ONCE("LAYERED: wrong samples_to_do\n");
@ -39,26 +42,25 @@ void render_vgmstream_layered(sample_t* outbuf, int32_t sample_count, VGMSTREAM*
/* decode all layers */
ch = 0;
for (int current_layer = 0; current_layer < data->layer_count; current_layer++) {
/* layers may have their own number of channels/format (buf is as big as needed) */
sfmt_t format = mixing_get_input_sample_type(data->layers[current_layer]);
sbuf_init(ssrc, format, data->buffer, samples_to_do, data->layers[current_layer]->channels);
/* layers may have their own number of channels */
int layer_channels;
mixing_info(data->layers[current_layer], NULL, &layer_channels);
render_vgmstream(data->buffer, samples_to_do, data->layers[current_layer]);
render_main(ssrc, data->layers[current_layer]);
/* mix layer samples to main samples */
sbuf_copy_layers(outbuf, data->output_channels, data->buffer, layer_channels, samples_to_do, samples_filled, ch);
ch += layer_channels;
sbuf_copy_layers(sdst, ssrc, ch, samples_to_do);
ch += ssrc->channels;
}
samples_filled += samples_to_do;
sdst->filled += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
}
return;
decode_fail:
sbuf_silence_s16(outbuf, sample_count, data->output_channels, samples_filled);
sbuf_silence_rest(sdst);
}
@ -125,10 +127,11 @@ fail:
}
bool setup_layout_layered(layered_layout_data* data) {
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
int max_input_channels = 0;
int max_output_channels = 0;
int max_sample_size = 0;
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
for (int i = 0; i < data->layer_count; i++) {
if (data->layers[i] == NULL) {
VGM_LOG("LAYERED: no vgmstream in %i\n", i);
@ -140,7 +143,7 @@ bool setup_layout_layered(layered_layout_data* data) {
return false;
}
/* different layers may have different input/output channels */
/* different layers may have different input/output channels or sample formats */
int layer_input_channels, layer_output_channels;
mixing_info(data->layers[i], &layer_input_channels, &layer_output_channels);
@ -163,6 +166,10 @@ bool setup_layout_layered(layered_layout_data* data) {
#endif
}
int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->layers[i]) );
if (max_sample_size < current_sample_size)
max_sample_size = current_sample_size;
/* loops and other values could be mismatched, but should be handled on allocate */
/* init mixing */
@ -179,8 +186,9 @@ bool setup_layout_layered(layered_layout_data* data) {
return false;
/* create internal buffer big enough for mixing all layers */
if (!sbuf_realloc(&data->buffer, VGMSTREAM_LAYER_SAMPLE_BUFFER, max_input_channels))
goto fail;
free(data->buffer);
data->buffer = malloc(VGMSTREAM_LAYER_SAMPLE_BUFFER * max_input_channels * max_sample_size);
if (!data->buffer) goto fail;
data->input_channels = max_input_channels;
data->output_channels = max_output_channels;
@ -190,7 +198,7 @@ fail:
return false; /* caller is expected to free */
}
void free_layout_layered(layered_layout_data *data) {
void free_layout_layered(layered_layout_data* data) {
if (!data)
return;
@ -202,7 +210,7 @@ void free_layout_layered(layered_layout_data *data) {
free(data);
}
void reset_layout_layered(layered_layout_data *data) {
void reset_layout_layered(layered_layout_data* data) {
if (!data)
return;

View File

@ -1,10 +1,11 @@
#ifndef _LAYOUT_H
#define _LAYOUT_H
#ifndef _LAYOUT_H_
#define _LAYOUT_H_
#include "../streamtypes.h"
#include "../vgmstream.h"
#include "../util/reader_sf.h"
#include "../util/log.h"
#include "../base/sbuf.h"
/* basic layouts */
void render_vgmstream_flat(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
@ -21,10 +22,10 @@ typedef struct {
sample_t* buffer;
int input_channels; /* internal buffer channels */
int output_channels; /* resulting channels (after mixing, if applied) */
int mixed_channels; /* segments have different number of channels */
bool mixed_channels; /* segments have different number of channels */
} segmented_layout_data;
void render_vgmstream_segmented(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
void render_vgmstream_segmented(sbuf_t* sbuf, VGMSTREAM* vgmstream);
segmented_layout_data* init_layout_segmented(int segment_count);
bool setup_layout_segmented(segmented_layout_data* data);
void free_layout_segmented(segmented_layout_data* data);
@ -45,7 +46,7 @@ typedef struct {
int curr_layer; /* helper */
} layered_layout_data;
void render_vgmstream_layered(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
void render_vgmstream_layered(sbuf_t* sbuf, VGMSTREAM* vgmstream);
layered_layout_data* init_layout_layered(int layer_count);
bool setup_layout_layered(layered_layout_data* data);
void free_layout_layered(layered_layout_data* data);

View File

@ -4,6 +4,7 @@
#include "../base/mixing.h"
#include "../base/plugins.h"
#include "../base/sbuf.h"
#include "../base/render.h"
#define VGMSTREAM_MAX_SEGMENTS 1024
#define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192
@ -12,18 +13,15 @@
/* Decodes samples for segmented streams.
* Chains together sequential vgmstreams, for data divided into separate sections or files
* (like one part for intro and other for loop segments, which may even use different codecs). */
void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) {
void render_vgmstream_segmented(sbuf_t* sbuf, VGMSTREAM* vgmstream) {
segmented_layout_data* data = vgmstream->layout_data;
bool use_internal_buffer = false;
sbuf_t ssrc_tmp;
sbuf_t* ssrc = &ssrc_tmp;
/* normally uses outbuf directly (faster?) but could need internal buffer if downmixing */
if (vgmstream->channels != data->input_channels || data->mixed_channels) {
use_internal_buffer = true;
}
if (data->current_segment >= data->segment_count) {
VGM_LOG_ONCE("SEGMENT: wrong current segment\n");
sbuf_silence_s16(outbuf, sample_count, data->output_channels, 0);
sbuf_silence_rest(sbuf);
return;
}
@ -31,10 +29,10 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
mixing_info(data->segments[data->current_segment], NULL, &current_channels);
int samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]);
int samples_filled = 0;
while (samples_filled < sample_count) {
while (sbuf->filled < sbuf->samples) {
int samples_to_do;
sample_t* buf;
sfmt_t segment_format;
void* buf_filled = NULL;
if (vgmstream->loop_flag && decode_do_loop(vgmstream)) {
/* handle looping (loop_layout has been called below, changes segments/state) */
@ -62,9 +60,9 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
}
samples_to_do = decode_get_samples_to_do(samples_this_block, sample_count, vgmstream);
if (samples_to_do > sample_count - samples_filled)
samples_to_do = sample_count - samples_filled;
samples_to_do = decode_get_samples_to_do(samples_this_block, sbuf->samples, vgmstream);
if (samples_to_do > sbuf->samples - sbuf->filled)
samples_to_do = sbuf->samples - sbuf->filled;
if (samples_to_do > VGMSTREAM_SEGMENT_SAMPLE_BUFFER /*&& use_internal_buffer*/) /* always for fade/etc mixes */
samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER;
@ -73,25 +71,33 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
goto decode_fail;
}
buf = use_internal_buffer ? data->buffer : &outbuf[samples_filled * data->output_channels];
render_vgmstream(buf, samples_to_do, data->segments[data->current_segment]);
segment_format = mixing_get_input_sample_type(data->segments[data->current_segment]);
sbuf_init(ssrc, segment_format, data->buffer, samples_to_do, data->segments[data->current_segment]->channels);
if (use_internal_buffer) {
sbuf_copy_segments(outbuf, data->output_channels, data->buffer, current_channels, samples_to_do, samples_filled);
// try to use part of outbuf directly if not remixed (minioptimization) //TODO improve detection
if (vgmstream->channels == data->input_channels && sbuf->fmt == segment_format && !data->mixed_channels) {
buf_filled = sbuf_get_filled_buf(sbuf);
ssrc->buf = buf_filled;
}
samples_filled += samples_to_do;
render_main(ssrc, data->segments[data->current_segment]);
// returned buf may have changed
if (ssrc->buf != buf_filled) {
sbuf_copy_segments(sbuf, ssrc);
}
sbuf->filled += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
}
return;
decode_fail:
sbuf_silence_s16(outbuf, sample_count, data->output_channels, samples_filled);
sbuf_silence_rest(sbuf);
}
void seek_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample) {
segmented_layout_data* data = vgmstream->layout_data;
@ -145,8 +151,10 @@ fail:
}
bool setup_layout_segmented(segmented_layout_data* data) {
int max_input_channels = 0, max_output_channels = 0, mixed_channels = 0;
int max_input_channels = 0;
int max_output_channels = 0;
int max_sample_size = 0;
bool mixed_channels = false;
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
for (int i = 0; i < data->segment_count; i++) {
@ -187,7 +195,7 @@ bool setup_layout_segmented(segmented_layout_data* data) {
mixing_info(data->segments[i-1], NULL, &prev_output_channels);
if (segment_output_channels != prev_output_channels) {
mixed_channels = 1;
mixed_channels = true;
//VGM_LOG("SEGMENTED: segment %i has wrong channels %i vs prev channels %i\n", i, segment_output_channels, prev_output_channels);
//goto fail;
}
@ -202,6 +210,10 @@ bool setup_layout_segmented(segmented_layout_data* data) {
// goto fail;
}
int current_sample_size = sfmt_get_sample_size( mixing_get_input_sample_type(data->segments[i]) );
if (max_sample_size < current_sample_size)
max_sample_size = current_sample_size;
/* init mixing */
mixing_setup(data->segments[i], VGMSTREAM_SEGMENT_SAMPLE_BUFFER);
@ -213,8 +225,9 @@ bool setup_layout_segmented(segmented_layout_data* data) {
return false;
/* create internal buffer big enough for mixing */
if (!sbuf_realloc(&data->buffer, VGMSTREAM_SEGMENT_SAMPLE_BUFFER, max_input_channels))
goto fail;
free(data->buffer);
data->buffer = malloc(VGMSTREAM_SEGMENT_SAMPLE_BUFFER * max_input_channels * max_sample_size);
if (!data->buffer) goto fail;
data->input_channels = max_input_channels;
data->output_channels = max_output_channels;

View File

@ -1,7 +1,7 @@
#ifndef _LIBVGMSTREAM_H_
#define _LIBVGMSTREAM_H_
//#define LIBVGMSTREAM_ENABLE 1
#define LIBVGMSTREAM_ENABLE 1
#if LIBVGMSTREAM_ENABLE
/* libvgmstream: vgmstream's public API

View File

@ -811,7 +811,9 @@ static bool parse_psb(STREAMFILE* sf, psb_header_t* psb) {
ctx = psb_init(sf);
if (!ctx) goto fail;
psb_print(ctx);
#ifdef VGM_DEBUG_OUTPUT
//psb_print(ctx);
#endif
/* main process */
psb_get_root(ctx, &nroot);

View File

@ -784,6 +784,8 @@ int psb_node_exists(const psb_node_t* node, const char* key) {
/******************************************************************************/
/* ETC */
#ifdef VGM_DEBUG_OUTPUT
#define PSB_DEPTH_STEP 2
static void print_internal(psb_node_t* curr, int depth) {
@ -862,3 +864,4 @@ void psb_print(psb_context_t* ctx) {
psb_get_root(ctx, &node);
print_internal(&node, 0);
}
#endif

View File

@ -216,18 +216,19 @@ VGMSTREAM* allocate_vgmstream(int channels, int loop_flag) {
if (!vgmstream->loop_ch) goto fail;
}
/* garbage buffer for decode discarding (local bufs may cause stack overflows with segments/layers)
* in theory the bigger the better but in practice there isn't much difference */
vgmstream->tmpbuf_size = 0x10000; /* for all channels */
vgmstream->tmpbuf = malloc(sizeof(sample_t) * vgmstream->tmpbuf_size);
if (!vgmstream->tmpbuf) goto fail;
vgmstream->channels = channels;
vgmstream->loop_flag = loop_flag;
vgmstream->mixer = mixer_init(vgmstream->channels); /* pre-init */
//if (!vgmstream->mixer) goto fail;
//TODO: improve/init later to minimize memory
/* garbage buffer for seeking/discarding (local bufs may cause stack overflows with segments/layers)
* in theory the bigger the better but in practice there isn't much difference. */
vgmstream->tmpbuf_size = 1024 * 2 * channels * sizeof(float);
vgmstream->tmpbuf = malloc(vgmstream->tmpbuf_size);
if (!vgmstream->tmpbuf) goto fail;
/* BEWARE: merge_vgmstream does some free'ing too */
//vgmstream->stream_name_size = STREAM_NAME_SIZE;

View File

@ -239,8 +239,8 @@ typedef struct {
int loop_count; /* counter of complete loops (1=looped once) */
int loop_target; /* max loops before continuing with the stream end (loops forever if not set) */
sample_t* tmpbuf; /* garbage buffer used for seeking/trimming */
size_t tmpbuf_size; /* for all channels (samples = tmpbuf_size / channels) */
void* tmpbuf; /* garbage buffer used for seeking/trimming */
size_t tmpbuf_size; /* for all channels (samples = tmpbuf_size / channels / sample_size) */
} VGMSTREAM;