mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
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:
commit
8205c522c5
15
doc/USAGE.md
15
doc/USAGE.md
@ -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,
|
||||
|
@ -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*/);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
219
src/base/sbuf.c
219
src/base/sbuf.c
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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, ¤t_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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user