Updated VGMStream to r1951-0-g4b2dc01c

Updated to add one new extension, too.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2024-08-23 23:06:24 -07:00
parent ee98898dbc
commit 8498bba881
46 changed files with 1198 additions and 1050 deletions

View file

@ -372,7 +372,7 @@
834F7EC52C70A786003AC386 /* api_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 834F7EA12C70A786003AC386 /* api_internal.h */; };
834F7EC62C70A786003AC386 /* api_libsf.c in Sources */ = {isa = PBXBuildFile; fileRef = 834F7EA22C70A786003AC386 /* api_libsf.c */; };
834F7EC72C70A786003AC386 /* api_tags.c in Sources */ = {isa = PBXBuildFile; fileRef = 834F7EA32C70A786003AC386 /* api_tags.c */; };
834F7EC82C70A786003AC386 /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 834F7EA42C70A786003AC386 /* config.c */; };
834F7EC82C70A786003AC386 /* play_config.c in Sources */ = {isa = PBXBuildFile; fileRef = 834F7EA42C70A786003AC386 /* play_config.c */; };
834F7EC92C70A786003AC386 /* decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 834F7EA52C70A786003AC386 /* decode.c */; };
834F7ECA2C70A786003AC386 /* decode.h in Headers */ = {isa = PBXBuildFile; fileRef = 834F7EA62C70A786003AC386 /* decode.h */; };
834F7ECB2C70A786003AC386 /* info.c in Sources */ = {isa = PBXBuildFile; fileRef = 834F7EA72C70A786003AC386 /* info.c */; };
@ -454,6 +454,8 @@
835B9B932730BF2D00F87EE3 /* lpcm_shade.c in Sources */ = {isa = PBXBuildFile; fileRef = 835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */; };
835C883622CC17BE001B4B3F /* bwav.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C883122CC17BD001B4B3F /* bwav.c */; };
835C883722CC17BE001B4B3F /* ogg_vorbis_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */; };
835DF7032C79ABB50008814A /* sbuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 835DF7022C79ABB50008814A /* sbuf.c */; };
835DF7052C79AED70008814A /* play_state.c in Sources */ = {isa = PBXBuildFile; fileRef = 835DF7042C79AED70008814A /* play_state.c */; };
836C052B23F62F1A00FA07C7 /* libatrac9.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 835FC6C623F62AEF006960FA /* libatrac9.framework */; };
836C052C23F62F3100FA07C7 /* libatrac9.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 835FC6C623F62AEF006960FA /* libatrac9.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
836DF622298F83F400CD0580 /* cri_keys.h in Headers */ = {isa = PBXBuildFile; fileRef = 836DF61F298F83F400CD0580 /* cri_keys.h */; };
@ -581,7 +583,6 @@
836F700B18BDC2190095E648 /* ps2_wad.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ECF18BDC2190095E648 /* ps2_wad.c */; };
836F700C18BDC2190095E648 /* wb.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED018BDC2190095E648 /* wb.c */; };
836F700D18BDC2190095E648 /* ps2_wmus.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED118BDC2190095E648 /* ps2_wmus.c */; };
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED318BDC2190095E648 /* ps2_xa30.c */; };
836F701518BDC2190095E648 /* sndp.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6ED918BDC2190095E648 /* sndp.c */; };
836F701E18BDC2190095E648 /* redspark.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE218BDC2190095E648 /* redspark.c */; };
836F701F18BDC2190095E648 /* riff.c in Sources */ = {isa = PBXBuildFile; fileRef = 836F6EE318BDC2190095E648 /* riff.c */; };
@ -1282,7 +1283,7 @@
834F7EA12C70A786003AC386 /* api_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = api_internal.h; sourceTree = "<group>"; };
834F7EA22C70A786003AC386 /* api_libsf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = api_libsf.c; sourceTree = "<group>"; };
834F7EA32C70A786003AC386 /* api_tags.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = api_tags.c; sourceTree = "<group>"; };
834F7EA42C70A786003AC386 /* config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = config.c; sourceTree = "<group>"; };
834F7EA42C70A786003AC386 /* play_config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = play_config.c; sourceTree = "<group>"; };
834F7EA52C70A786003AC386 /* decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = decode.c; sourceTree = "<group>"; };
834F7EA62C70A786003AC386 /* decode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = decode.h; sourceTree = "<group>"; };
834F7EA72C70A786003AC386 /* info.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = info.c; sourceTree = "<group>"; };
@ -1363,6 +1364,8 @@
835B9B8E2730BF2D00F87EE3 /* lpcm_shade.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpcm_shade.c; sourceTree = "<group>"; };
835C883122CC17BD001B4B3F /* bwav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bwav.c; sourceTree = "<group>"; };
835C883522CC17BE001B4B3F /* ogg_vorbis_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ogg_vorbis_streamfile.h; sourceTree = "<group>"; };
835DF7022C79ABB50008814A /* sbuf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sbuf.c; sourceTree = "<group>"; };
835DF7042C79AED70008814A /* play_state.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = play_state.c; sourceTree = "<group>"; };
835FC6C123F62AEE006960FA /* libatrac9.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libatrac9.xcodeproj; path = ../libatrac9/libatrac9.xcodeproj; sourceTree = "<group>"; };
836DF61F298F83F400CD0580 /* cri_keys.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cri_keys.h; sourceTree = "<group>"; };
836DF620298F83F400CD0580 /* bitstream_msb.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitstream_msb.h; sourceTree = "<group>"; };
@ -1491,7 +1494,6 @@
836F6ECF18BDC2190095E648 /* ps2_wad.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wad.c; sourceTree = "<group>"; };
836F6ED018BDC2190095E648 /* wb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wb.c; sourceTree = "<group>"; };
836F6ED118BDC2190095E648 /* ps2_wmus.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_wmus.c; sourceTree = "<group>"; };
836F6ED318BDC2190095E648 /* ps2_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ps2_xa30.c; sourceTree = "<group>"; };
836F6ED918BDC2190095E648 /* sndp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sndp.c; sourceTree = "<group>"; };
836F6EE218BDC2190095E648 /* redspark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = redspark.c; sourceTree = "<group>"; };
836F6EE318BDC2190095E648 /* riff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = riff.c; sourceTree = "<group>"; };
@ -1973,7 +1975,6 @@
834F7EA12C70A786003AC386 /* api_internal.h */,
834F7EA22C70A786003AC386 /* api_libsf.c */,
834F7EA32C70A786003AC386 /* api_tags.c */,
834F7EA42C70A786003AC386 /* config.c */,
834F7EA52C70A786003AC386 /* decode.c */,
834F7EA62C70A786003AC386 /* decode.h */,
834F7EA72C70A786003AC386 /* info.c */,
@ -1986,10 +1987,13 @@
834F7EAE2C70A786003AC386 /* mixing_macros.c */,
834F7EAF2C70A786003AC386 /* mixing.c */,
834F7EB02C70A786003AC386 /* mixing.h */,
834F7EA42C70A786003AC386 /* play_config.c */,
835DF7042C79AED70008814A /* play_state.c */,
834F7EB12C70A786003AC386 /* plugins.c */,
834F7EB22C70A786003AC386 /* plugins.h */,
834F7EB32C70A786003AC386 /* render.c */,
834F7EB42C70A786003AC386 /* render.h */,
835DF7022C79ABB50008814A /* sbuf.c */,
834F7EB52C70A786003AC386 /* sbuf.h */,
834F7EB62C70A786003AC386 /* seek.c */,
834F7EB72C70A786003AC386 /* streamfile_api.c */,
@ -2476,7 +2480,6 @@
836F6ECC18BDC2190095E648 /* ps2_vms.c */,
836F6ECF18BDC2190095E648 /* ps2_wad.c */,
836F6ED118BDC2190095E648 /* ps2_wmus.c */,
836F6ED318BDC2190095E648 /* ps2_xa30.c */,
8315868326F586E200803A3A /* psb.c */,
837CEAE823487F2B00E62A4A /* psf.c */,
836F6E5518BDC2180095E648 /* psnd.c */,
@ -3105,6 +3108,7 @@
83A5F75F198DF021009AF94C /* bfwav.c in Sources */,
83FBB1792A4FF71B00CD0580 /* rstm_rockstar.c in Sources */,
8339B326280FDF4B0076F74B /* text_reader.c in Sources */,
835DF7052C79AED70008814A /* play_state.c in Sources */,
836F702018BDC2190095E648 /* rkv.c in Sources */,
834FE0F4215C79ED000A5D3D /* wsi.c in Sources */,
83D26A7A26E66D98001A9475 /* adp_wildfire.c in Sources */,
@ -3268,6 +3272,7 @@
836F702418BDC2190095E648 /* rwsd.c in Sources */,
834F7DBE2C7093EA003AC386 /* ea_xas_decoder.c in Sources */,
834F7DBD2C7093EA003AC386 /* ea_xa_decoder.c in Sources */,
835DF7032C79ABB50008814A /* sbuf.c in Sources */,
830EBE142004656E0023AA10 /* ktss.c in Sources */,
836F6F6618BDC2190095E648 /* aax.c in Sources */,
8306B0BC20984552000302D4 /* blocked_vs.c in Sources */,
@ -3285,7 +3290,6 @@
834FE101215C79ED000A5D3D /* csmp.c in Sources */,
8306B0A720984552000302D4 /* blocked_xvas.c in Sources */,
8349A9101FE6258200E26435 /* ea_eaac.c in Sources */,
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */,
835B9B912730BF2D00F87EE3 /* lopu_fb.c in Sources */,
8319017C28F67EE100B70711 /* miniz.c in Sources */,
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */,
@ -3525,7 +3529,7 @@
834F7D312C709231003AC386 /* ea_sbk.c in Sources */,
836F6F9518BDC2190095E648 /* kraw.c in Sources */,
834F7E172C709A1D003AC386 /* ea_schl_map_mpf_mus.c in Sources */,
834F7EC82C70A786003AC386 /* config.c in Sources */,
834F7EC82C70A786003AC386 /* play_config.c in Sources */,
836F6FB718BDC2190095E648 /* ngc_ssm.c in Sources */,
8306B0E920984590000302D4 /* opus.c in Sources */,
83709E051ECBC1A4005C03D3 /* ghs.c in Sources */,

View file

@ -51,7 +51,7 @@ static void update_decoder_info(libvgmstream_priv_t* priv, int samples_done) {
priv->dec.done = priv->decode_done;
}
LIBVGMSTREAM_API int libvgmstream_play(libvgmstream_t* lib) {
LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib) {
if (!lib || !lib->priv)
return LIBVGMSTREAM_ERROR_GENERIC;
@ -85,7 +85,7 @@ LIBVGMSTREAM_API int libvgmstream_fill(libvgmstream_t* lib, void* buf, int buf_s
return LIBVGMSTREAM_ERROR_GENERIC;
if (priv->buf.consumed >= priv->buf.samples) {
int err = libvgmstream_play(lib);
int err = libvgmstream_render(lib);
if (err < 0) return err;
}

View file

@ -88,4 +88,8 @@ LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_ti
return LIBVGMSTREAM_OK;
}
LIBVGMSTREAM_API bool libvgmstream_is_virtual_filename(const char* filename) {
return vgmstream_is_virtual_filename(filename);
}
#endif

View file

@ -4,6 +4,7 @@
#include "decode.h"
#include "mixing.h"
#include "plugins.h"
#include "sbuf.h"
/* custom codec handling, not exactly "decode" stuff but here to simplify adding new codecs */
@ -824,18 +825,19 @@ bool decode_uses_internal_offset_updates(VGMSTREAM* vgmstream) {
return vgmstream->coding_type == coding_MS_IMA || vgmstream->coding_type == coding_MS_IMA_mono;
}
/* Decode samples into the buffer. Assume that we have written samples_written into the
/* Decode samples into the buffer. Assume that we have written samples_filled into the
* buffer already, and we have samples_to_do consecutive samples ahead of us (won't call
* more than one frame if configured above to do so).
* Called by layouts since they handle samples written/to_do */
void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_do, sample_t* buffer) {
void decode_vgmstream(VGMSTREAM* vgmstream, int samples_filled, int samples_to_do, sample_t* buffer) {
int ch;
buffer += samples_written * vgmstream->channels; /* passed externally to simplify I guess */
buffer += samples_filled * vgmstream->channels; /* passed externally to simplify I guess */
//samples_to_do -= samples_filled; /* pre-adjusted */
switch (vgmstream->coding_type) {
case coding_SILENCE:
memset(buffer, 0, samples_to_do * vgmstream->channels * sizeof(sample_t));
sbuf_silence(buffer, samples_to_do, vgmstream->channels, 0);
break;
case coding_CRI_ADX:
@ -1601,10 +1603,10 @@ int decode_get_samples_to_do(int samples_this_block, int samples_per_frame, VGMS
return samples_to_do;
}
/* Detect loop start and save values, or detect loop end and restore (loop back).
* Returns 1 if loop was done. */
int decode_do_loop(VGMSTREAM* vgmstream) {
/*if (!vgmstream->loop_flag) return 0;*/
/* Detect loop start and save values, or detect loop end and restore (loop back). Returns true if loop was done. */
bool decode_do_loop(VGMSTREAM* vgmstream) {
//if (!vgmstream->loop_flag) return false;
/* is this the loop end? = new loop, continue from loop_start_sample */
if (vgmstream->current_sample == vgmstream->loop_end_sample) {
@ -1613,8 +1615,8 @@ int decode_do_loop(VGMSTREAM* vgmstream) {
* (only needed with the "play stream end after looping N times" option enabled) */
vgmstream->loop_count++;
if (vgmstream->loop_target && vgmstream->loop_target == vgmstream->loop_count) {
vgmstream->loop_flag = 0; /* could be improved but works ok, will be restored on resets */
return 0;
vgmstream->loop_flag = false; /* could be improved but works ok, will be restored on resets */
return false;
}
/* against everything I hold sacred, preserve adpcm history before looping for certain types */
@ -1623,8 +1625,7 @@ int decode_do_loop(VGMSTREAM* vgmstream) {
vgmstream->meta_type == meta_DSP_CSTR ||
vgmstream->coding_type == coding_PSX ||
vgmstream->coding_type == coding_PSX_badflags) {
int ch;
for (ch = 0; ch < vgmstream->channels; ch++) {
for (int ch = 0; ch < vgmstream->channels; ch++) {
vgmstream->loop_ch[ch].adpcm_history1_16 = vgmstream->ch[ch].adpcm_history1_16;
vgmstream->loop_ch[ch].adpcm_history2_16 = vgmstream->ch[ch].adpcm_history2_16;
vgmstream->loop_ch[ch].adpcm_history1_32 = vgmstream->ch[ch].adpcm_history1_32;
@ -1633,12 +1634,13 @@ int decode_do_loop(VGMSTREAM* vgmstream) {
}
//TODO: improve
/* loop codecs that need special handling, usually:
* - on hit_loop, current offset is copied to loop_ch[].offset
* - some codecs will overwrite loop_ch[].offset with a custom value
* - loop_ch[] is copied to ch[] (with custom value)
* - then codec will use ch[]'s offset
* regular codecs may use copied loop_ch[] offset without issue */
/* codecs with codec_data that decode_seek need special handling, usually:
* - during decode, codec uses vgmstream->ch[].offset to handle current offset
* - on hit_loop, current offset is auto-copied to vgmstream->loop_ch[].offset
* - decode_seek codecs may overwrite vgmstream->loop_ch[].offset with a custom value (such as start_offset)
* - vgmstream->loop_ch[] is copied below to vgmstream->ch[] (with the newly assigned custom value)
* - then codec will use vgmstream->ch[].offset during decode
* regular codecs will use copied vgmstream->loop_ch[].offset without issue */
decode_seek(vgmstream);
/* restore! */
@ -1649,7 +1651,7 @@ int decode_do_loop(VGMSTREAM* vgmstream) {
vgmstream->current_block_samples = vgmstream->loop_block_samples;
vgmstream->current_block_offset = vgmstream->loop_block_offset;
vgmstream->next_block_offset = vgmstream->loop_next_block_offset;
//vgmstream->pstate = vgmstream->lstate; /* play state is applied over loops */
vgmstream->full_block_size = vgmstream->loop_full_block_size;
/* loop layouts (after restore, in case layout needs state manipulations) */
switch(vgmstream->layout_type) {
@ -1663,24 +1665,30 @@ int decode_do_loop(VGMSTREAM* vgmstream) {
break;
}
return 1; /* looped */
/* play state is applied over loops and stream decoding, so it's not restored on loops */
//vgmstream->pstate = vgmstream->lstate;
return true; /* has looped */
}
/* is this the loop start? save if we haven't saved yet (right when first loop starts) */
if (!vgmstream->hit_loop && vgmstream->current_sample == vgmstream->loop_start_sample) {
/* save! */
memcpy(vgmstream->loop_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels);
memcpy(vgmstream->loop_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL) * vgmstream->channels);
vgmstream->loop_current_sample = vgmstream->current_sample;
vgmstream->loop_samples_into_block = vgmstream->samples_into_block;
vgmstream->loop_block_size = vgmstream->current_block_size;
vgmstream->loop_block_samples = vgmstream->current_block_samples;
vgmstream->loop_block_offset = vgmstream->current_block_offset;
vgmstream->loop_next_block_offset = vgmstream->next_block_offset;
//vgmstream->lstate = vgmstream->pstate; /* play state is applied over loops */
vgmstream->loop_full_block_size = vgmstream->full_block_size;
/* play state is applied over loops and stream decoding, so it's not saved on loops */
//vgmstream->lstate = vgmstream->pstate;
vgmstream->hit_loop = true; /* info that loop is now ready to use */
}
return 0; /* not looped */
return false; /* has not looped */
}

View file

@ -7,12 +7,12 @@ void decode_free(VGMSTREAM* vgmstream);
void decode_seek(VGMSTREAM* vgmstream);
void decode_reset(VGMSTREAM* vgmstream);
/* Decode samples into the buffer. Assume that we have written samples_written into the
/* Decode samples into the buffer. Assume that we have written samples_filled into the
* buffer already, and we have samples_to_do consecutive samples ahead of us. */
void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_do, sample_t* buffer);
void decode_vgmstream(VGMSTREAM* vgmstream, int samples_filled, int samples_to_do, sample_t* buffer);
/* Detect loop start and save values, or detect loop end and restore (loop back). Returns 1 if loop was done. */
int decode_do_loop(VGMSTREAM* vgmstream);
/* Detect loop start and save values, or detect loop end and restore (loop back). Returns true if loop was done. */
bool decode_do_loop(VGMSTREAM* vgmstream);
/* Calculate number of consecutive samples to do (taking into account stopping for loop start and end) */
int decode_get_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM* vgmstream);

View file

@ -6,6 +6,8 @@
#include "../util/channel_mappings.h"
#include "../util/sf_utils.h"
#define TEMPSIZE (256+32)
/*******************************************************************************/
/* TEXT */
/*******************************************************************************/
@ -21,7 +23,6 @@ static void describe_get_time(int32_t samples, int sample_rate, double* p_time_m
/* Write a description of the stream into array pointed by desc, which must be length bytes long.
* Will always be null-terminated if length > 0 */
void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) {
#define TEMPSIZE (256+32)
char temp[TEMPSIZE];
double time_mm, time_ss;

View file

@ -115,7 +115,7 @@ void mixer_op_downmix(mixer_t* mixer, int32_t sample_count, mix_op_t* op) {
sbuf_tmp[ch] = sbuf[ch]; /* copy untouched channels */
}
for (int ch = op->ch_dst; ch < max_channels; ch++) {
for (int ch = op->ch_dst; ch < max_channels - 1; ch++) {
sbuf_tmp[ch] = sbuf[ch + 1]; /* 'pull' dropped channels back */
}

View file

@ -13,50 +13,50 @@ static void copy_time(bool* dst_flag, int32_t* dst_time, double* dst_time_s, boo
*dst_time_s = *src_time_s;
}
//todo reuse in txtp?
// config that has been set internally via TXTP
static void load_default_config(play_config_t* def, play_config_t* tcfg) {
/* loop limit: txtp #L > txtp #l > player #L > player #l */
if (tcfg->play_forever) {
def->play_forever = 1;
def->ignore_loop = 0;
def->play_forever = true;
def->ignore_loop = false;
}
if (tcfg->loop_count_set) {
def->loop_count = tcfg->loop_count;
def->loop_count_set = 1;
def->ignore_loop = 0;
def->loop_count_set = true;
def->ignore_loop = false;
if (!tcfg->play_forever)
def->play_forever = 0;
def->play_forever = false;
}
/* fade priority: #F > #f, #d */
if (tcfg->ignore_fade) {
def->ignore_fade = 1;
def->ignore_fade = true;
}
if (tcfg->fade_delay_set) {
def->fade_delay = tcfg->fade_delay;
def->fade_delay_set = 1;
def->fade_delay_set = true;
}
if (tcfg->fade_time_set) {
def->fade_time = tcfg->fade_time;
def->fade_time_set = 1;
def->fade_time_set = true;
}
/* loop priority: #i > #e > #E (respect player's ignore too) */
if (tcfg->really_force_loop) {
//def->ignore_loop = 0;
def->force_loop = 0;
def->really_force_loop = 1;
//def->ignore_loop = false;
def->force_loop = false;
def->really_force_loop = true;
}
if (tcfg->force_loop) {
//def->ignore_loop = 0;
def->force_loop = 1;
def->really_force_loop = 0;
//def->ignore_loop = false;
def->force_loop = true;
def->really_force_loop = false;
}
if (tcfg->ignore_loop) {
def->ignore_loop = 1;
def->force_loop = 0;
def->really_force_loop = 0;
def->ignore_loop = true;
def->force_loop = false;
def->really_force_loop = false;
}
copy_time(&def->pad_begin_set, &def->pad_begin, &def->pad_begin_s, &tcfg->pad_begin_set, &tcfg->pad_begin, &tcfg->pad_begin_s);
@ -69,7 +69,8 @@ static void load_default_config(play_config_t* def, play_config_t* tcfg) {
def->is_txtp = tcfg->is_txtp;
}
static void load_player_config(play_config_t* def, vgmstream_cfg_t* vcfg) {
/* config that has been set externally by plugins */
static void load_external_config(play_config_t* def, vgmstream_cfg_t* vcfg) {
def->play_forever = vcfg->play_forever;
def->ignore_loop = vcfg->ignore_loop;
def->force_loop = vcfg->force_loop;
@ -77,31 +78,32 @@ static void load_player_config(play_config_t* def, vgmstream_cfg_t* vcfg) {
def->ignore_fade = vcfg->ignore_fade;
def->loop_count = vcfg->loop_count;
def->loop_count_set = 1;
def->loop_count_set = true;
def->fade_delay = vcfg->fade_delay;
def->fade_delay_set = 1;
def->fade_delay_set = true;
def->fade_time = vcfg->fade_time;
def->fade_time_set = 1;
def->fade_time_set = true;
}
/* apply play config to vgmstream */
void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) {
play_config_t defs = {0};
play_config_t* def = &defs; /* for convenience... */
play_config_t* tcfg = &vgmstream->config;
load_player_config(def, vcfg);
def->config_set = 1;
load_external_config(def, vcfg);
def->config_set = true;
if (!vcfg->disable_config_override)
load_default_config(def, tcfg);
if (!vcfg->allow_play_forever)
def->play_forever = 0;
def->play_forever = false;
/* copy final config back */
*tcfg = *def;
vgmstream->config_enabled = def->config_set;
setup_state_vgmstream(vgmstream);
setup_vgmstream_play_state(vgmstream);
}

View file

@ -0,0 +1,185 @@
#include "../vgmstream.h"
//#include "../layout/layout.h"
//#include "render.h"
//#include "decode.h"
//#include "mixing.h"
//#include "plugins.h"
int vgmstream_get_play_forever(VGMSTREAM* vgmstream) {
return vgmstream->config.play_forever;
}
void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled) {
/* sometimes we need to enable/disable right before playback
* (play config is left untouched, should mix ok as this flag is only used during
* render, while config is always prepared as if play forever wasn't enabled) */
vgmstream->config.play_forever = enabled;
setup_vgmstream(vgmstream); /* update config */
}
int32_t vgmstream_get_samples(VGMSTREAM* vgmstream) {
if (!vgmstream->config_enabled || !vgmstream->config.config_set)
return vgmstream->num_samples;
return vgmstream->pstate.play_duration;
}
/* calculate samples based on player's config */
int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream) {
if (vgmstream->loop_flag) {
if (vgmstream->loop_target == (int)looptimes) { /* set externally, as this function is info-only */
/* Continue playing the file normally after looping, instead of fading.
* Most files cut abruply after the loop, but some do have proper endings.
* With looptimes = 1 this option should give the same output vs loop disabled */
int loop_count = (int)looptimes; /* no half loops allowed */
return vgmstream->loop_start_sample
+ (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count
+ (vgmstream->num_samples - vgmstream->loop_end_sample);
}
else {
return vgmstream->loop_start_sample
+ (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes
+ (fadedelayseconds + fadeseconds) * vgmstream->sample_rate;
}
}
else {
return vgmstream->num_samples;
}
}
/*****************************************************************************/
/* apply config like forced loops */
static void setup_state_modifiers(VGMSTREAM* vgmstream) {
play_config_t* pc = &vgmstream->config;
/* apply final config */
if (pc->really_force_loop) {
vgmstream_force_loop(vgmstream, true, 0, vgmstream->num_samples);
}
if (pc->force_loop && !vgmstream->loop_flag) {
vgmstream_force_loop(vgmstream, true, 0, vgmstream->num_samples);
}
if (pc->ignore_loop) {
vgmstream_force_loop(vgmstream, false, 0, 0);
}
if (!vgmstream->loop_flag) {
pc->play_forever = false;
}
if (pc->play_forever) {
pc->ignore_fade = false;
}
/* loop N times, but also play stream end instead of fading out */
if (pc->ignore_fade) {
vgmstream_set_loop_target(vgmstream, (int)pc->loop_count);
pc->fade_time = 0;
pc->fade_delay = 0;
}
}
/* apply config like trims */
static void setup_state_processing(VGMSTREAM* vgmstream) {
play_state_t* ps = &vgmstream->pstate;
play_config_t* pc = &vgmstream->config;
double sample_rate = vgmstream->sample_rate;
/* time to samples */
if (pc->pad_begin_s)
pc->pad_begin = pc->pad_begin_s * sample_rate;
if (pc->pad_end_s)
pc->pad_end = pc->pad_end_s * sample_rate;
if (pc->trim_begin_s)
pc->trim_begin = pc->trim_begin_s * sample_rate;
if (pc->trim_end_s)
pc->trim_end = pc->trim_end_s * sample_rate;
if (pc->body_time_s)
pc->body_time = pc->body_time_s * sample_rate;
//todo fade time also set to samples
/* samples before all decode */
ps->pad_begin_duration = pc->pad_begin;
/* removed samples from first decode */
ps->trim_begin_duration = pc->trim_begin;
/* main samples part */
ps->body_duration = 0;
if (pc->body_time) {
ps->body_duration += pc->body_time; /* whether it loops or not */
}
else if (vgmstream->loop_flag) {
double loop_count = 1.0;
if (pc->loop_count_set) /* may set 0.0 on purpose I guess */
loop_count = pc->loop_count;
ps->body_duration += vgmstream->loop_start_sample;
if (pc->ignore_fade) {
ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)loop_count;
ps->body_duration += (vgmstream->num_samples - vgmstream->loop_end_sample);
}
else {
ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count;
}
}
else {
ps->body_duration += vgmstream->num_samples;
}
/* samples from some modify body */
if (pc->trim_begin)
ps->body_duration -= pc->trim_begin;
if (pc->trim_end)
ps->body_duration -= pc->trim_end;
if (pc->fade_delay && vgmstream->loop_flag)
ps->body_duration += pc->fade_delay * vgmstream->sample_rate;
/* samples from fade part */
if (pc->fade_time && vgmstream->loop_flag)
ps->fade_duration = pc->fade_time * vgmstream->sample_rate;
/* samples from last part (anything beyond this is empty, unless play forever is set) */
ps->pad_end_duration = pc->pad_end;
/* final count */
ps->play_duration = ps->pad_begin_duration + ps->body_duration + ps->fade_duration + ps->pad_end_duration;
ps->play_position = 0;
/* values too big can overflow, just ignore */
if (ps->pad_begin_duration < 0)
ps->pad_begin_duration = 0;
if (ps->body_duration < 0)
ps->body_duration = 0;
if (ps->fade_duration < 0)
ps->fade_duration = 0;
if (ps->pad_end_duration < 0)
ps->pad_end_duration = 0;
if (ps->play_duration < 0)
ps->play_duration = 0;
ps->pad_begin_left = ps->pad_begin_duration;
ps->trim_begin_left = ps->trim_begin_duration;
ps->fade_left = ps->fade_duration;
ps->fade_start = ps->pad_begin_duration + ps->body_duration;
//ps->pad_end_left = ps->pad_end_duration;
ps->pad_end_start = ps->fade_start + ps->fade_duration;
/* other info (updated once mixing is enabled) */
ps->input_channels = vgmstream->channels;
ps->output_channels = vgmstream->channels;
}
/* apply play config to internal state */
void setup_vgmstream_play_state(VGMSTREAM* vgmstream) {
if (!vgmstream->config.config_set)
return;
setup_state_modifiers(vgmstream);
setup_state_processing(vgmstream);
/* save new config for resets */
setup_vgmstream(vgmstream);
}

View file

@ -3,7 +3,7 @@
#include "render.h"
#include "decode.h"
#include "mixing.h"
#include "plugins.h"
#include "sbuf.h"
/* VGMSTREAM RENDERING
@ -47,179 +47,6 @@
* This mainly applies to TXTP, segments/layers in metas usually don't need to trigger config mode.
*/
int vgmstream_get_play_forever(VGMSTREAM* vgmstream) {
return vgmstream->config.play_forever;
}
void vgmstream_set_play_forever(VGMSTREAM* vgmstream, int enabled) {
/* sometimes we need to enable/disable right before playback
* (play config is left untouched, should mix ok as this flag is only used during
* render, while config is always prepared as if play forever wasn't enabled) */
vgmstream->config.play_forever = enabled;
setup_vgmstream(vgmstream); /* update config */
}
int32_t vgmstream_get_samples(VGMSTREAM* vgmstream) {
if (!vgmstream->config_enabled || !vgmstream->config.config_set)
return vgmstream->num_samples;
return vgmstream->pstate.play_duration;
}
/* calculate samples based on player's config */
int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream) {
if (vgmstream->loop_flag) {
if (vgmstream->loop_target == (int)looptimes) { /* set externally, as this function is info-only */
/* Continue playing the file normally after looping, instead of fading.
* Most files cut abruply after the loop, but some do have proper endings.
* With looptimes = 1 this option should give the same output vs loop disabled */
int loop_count = (int)looptimes; /* no half loops allowed */
return vgmstream->loop_start_sample
+ (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count
+ (vgmstream->num_samples - vgmstream->loop_end_sample);
}
else {
return vgmstream->loop_start_sample
+ (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * looptimes
+ (fadedelayseconds + fadeseconds) * vgmstream->sample_rate;
}
}
else {
return vgmstream->num_samples;
}
}
/*****************************************************************************/
static void setup_state_modifiers(VGMSTREAM* vgmstream) {
play_config_t* pc = &vgmstream->config;
/* apply final config */
if (pc->really_force_loop) {
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
}
if (pc->force_loop && !vgmstream->loop_flag) {
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
}
if (pc->ignore_loop) {
vgmstream_force_loop(vgmstream, 0, 0,0);
}
if (!vgmstream->loop_flag) {
pc->play_forever = 0;
}
if (pc->play_forever) {
pc->ignore_fade = 0;
}
/* loop N times, but also play stream end instead of fading out */
if (pc->ignore_fade) {
vgmstream_set_loop_target(vgmstream, (int)pc->loop_count);
pc->fade_time = 0;
pc->fade_delay = 0;
}
}
static void setup_state_processing(VGMSTREAM* vgmstream) {
play_state_t* ps = &vgmstream->pstate;
play_config_t* pc = &vgmstream->config;
double sample_rate = vgmstream->sample_rate;
/* time to samples */
if (pc->pad_begin_s)
pc->pad_begin = pc->pad_begin_s * sample_rate;
if (pc->pad_end_s)
pc->pad_end = pc->pad_end_s * sample_rate;
if (pc->trim_begin_s)
pc->trim_begin = pc->trim_begin_s * sample_rate;
if (pc->trim_end_s)
pc->trim_end = pc->trim_end_s * sample_rate;
if (pc->body_time_s)
pc->body_time = pc->body_time_s * sample_rate;
//todo fade time also set to samples
/* samples before all decode */
ps->pad_begin_duration = pc->pad_begin;
/* removed samples from first decode */
ps->trim_begin_duration = pc->trim_begin;
/* main samples part */
ps->body_duration = 0;
if (pc->body_time) {
ps->body_duration += pc->body_time; /* whether it loops or not */
}
else if (vgmstream->loop_flag) {
double loop_count = 1.0;
if (pc->loop_count_set) /* may set 0.0 on purpose I guess */
loop_count = pc->loop_count;
ps->body_duration += vgmstream->loop_start_sample;
if (pc->ignore_fade) {
ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * (int)loop_count;
ps->body_duration += (vgmstream->num_samples - vgmstream->loop_end_sample);
}
else {
ps->body_duration += (vgmstream->loop_end_sample - vgmstream->loop_start_sample) * loop_count;
}
}
else {
ps->body_duration += vgmstream->num_samples;
}
/* samples from some modify body */
if (pc->trim_begin)
ps->body_duration -= pc->trim_begin;
if (pc->trim_end)
ps->body_duration -= pc->trim_end;
if (pc->fade_delay && vgmstream->loop_flag)
ps->body_duration += pc->fade_delay * vgmstream->sample_rate;
/* samples from fade part */
if (pc->fade_time && vgmstream->loop_flag)
ps->fade_duration = pc->fade_time * vgmstream->sample_rate;
/* samples from last part (anything beyond this is empty, unless play forever is set) */
ps->pad_end_duration = pc->pad_end;
/* final count */
ps->play_duration = ps->pad_begin_duration + ps->body_duration + ps->fade_duration + ps->pad_end_duration;
ps->play_position = 0;
/* values too big can overflow, just ignore */
if (ps->pad_begin_duration < 0)
ps->pad_begin_duration = 0;
if (ps->body_duration < 0)
ps->body_duration = 0;
if (ps->fade_duration < 0)
ps->fade_duration = 0;
if (ps->pad_end_duration < 0)
ps->pad_end_duration = 0;
if (ps->play_duration < 0)
ps->play_duration = 0;
ps->pad_begin_left = ps->pad_begin_duration;
ps->trim_begin_left = ps->trim_begin_duration;
ps->fade_left = ps->fade_duration;
ps->fade_start = ps->pad_begin_duration + ps->body_duration;
//ps->pad_end_left = ps->pad_end_duration;
ps->pad_end_start = ps->fade_start + ps->fade_duration;
/* other info (updated once mixing is enabled) */
ps->input_channels = vgmstream->channels;
ps->output_channels = vgmstream->channels;
}
void setup_state_vgmstream(VGMSTREAM* vgmstream) {
if (!vgmstream->config.config_set)
return;
setup_state_modifiers(vgmstream);
setup_state_processing(vgmstream);
setup_vgmstream(vgmstream); /* save current config for reset */
}
/*****************************************************************************/
void render_free(VGMSTREAM* vgmstream) {
@ -247,11 +74,13 @@ void render_reset(VGMSTREAM* vgmstream) {
}
int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
if (sample_count == 0)
return 0;
/* current_sample goes between loop points (if looped) or up to max samples,
* must detect beyond that decoders would encounter garbage data */
/* not ">=" to allow layouts to loop in some cases when == happens */
// 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;
@ -277,7 +106,7 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
case layout_blocked_str_snds:
case layout_blocked_ws_aud:
case layout_blocked_dec:
case layout_blocked_vs:
case layout_blocked_vs_mh:
case layout_blocked_mul:
case layout_blocked_gsb:
case layout_blocked_xvas:
@ -317,6 +146,7 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
break;
}
// decode past stream samples: blank rest of buf
if (vgmstream->current_sample > vgmstream->num_samples) {
int channels = vgmstream->channels;
int32_t excess, decoded;
@ -333,85 +163,123 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
return sample_count;
}
/*****************************************************************************/
static void render_trim(VGMSTREAM* vgmstream) {
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 */
typedef struct {
//sbuf_t sbuf;
int16_t* tmpbuf;
int samples_to_do;
int samples_done;
} render_helper_t;
while (vgmstream->pstate.trim_begin_left) {
int to_do = vgmstream->pstate.trim_begin_left;
// consumes samples from decoder
static void play_op_trim(VGMSTREAM* vgmstream, render_helper_t* renderer) {
play_state_t* ps = &vgmstream->pstate;
if (!ps->trim_begin_left)
return;
if (!renderer->samples_to_do)
return;
// simpler using external buf?
//sample_t* tmpbuf = vgmstream->tmpbuf;
//size_t tmpbuf_size = vgmstream->tmpbuf_size;
//int32_t buf_samples = tmpbuf_size / vgmstream->channels; /* base channels, no need to apply mixing */
sample_t* tmpbuf = renderer->tmpbuf;
int buf_samples = renderer->samples_to_do;
while (ps->trim_begin_left) {
int to_do = ps->trim_begin_left;
if (to_do > buf_samples)
to_do = buf_samples;
render_layout(tmpbuf, to_do, vgmstream);
/* no mixing */
vgmstream->pstate.trim_begin_left -= to_do;
ps->trim_begin_left -= to_do;
}
}
static int render_pad_begin(VGMSTREAM* vgmstream, sample_t* buf, int samples_to_do) {
int channels = vgmstream->pstate.output_channels;
int to_do = vgmstream->pstate.pad_begin_left;
if (to_do > samples_to_do)
to_do = samples_to_do;
// adds empty samples to buf
static void play_op_pad_begin(VGMSTREAM* vgmstream, render_helper_t* renderer) {
play_state_t* ps = &vgmstream->pstate;
if (!ps->pad_begin_left)
return;
//if (ps->play_position > ps->play_begin_duration) //implicit
// return;
memset(buf, 0, to_do * sizeof(sample_t) * channels);
vgmstream->pstate.pad_begin_left -= to_do;
int channels = ps->output_channels;
int buf_samples = renderer->samples_to_do;
return to_do;
int to_do = ps->pad_begin_left;
if (to_do > buf_samples)
to_do = buf_samples;
memset(renderer->tmpbuf, 0, to_do * sizeof(sample_t) * channels);
ps->pad_begin_left -= to_do;
renderer->samples_done += to_do;
renderer->samples_to_do -= to_do;
renderer->tmpbuf += to_do * channels; /* as if mixed */
}
static int render_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_left) {
// fades (modifies volumes) part of buf
static void play_op_fade(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) {
play_config_t* pc = &vgmstream->config;
play_state_t* ps = &vgmstream->pstate;
//play_config_t* pc = &vgmstream->config;
//if (!ps->fade_left || pc->play_forever)
// return;
//if (ps->play_position + samples_done < ps->fade_start)
// return;
if (pc->play_forever || !ps->fade_left)
return;
if (ps->play_position + samples_done < ps->fade_start)
return;
{
int s, ch, start, fade_pos;
int channels = ps->output_channels;
int32_t to_do = ps->fade_left;
int start, fade_pos;
int channels = ps->output_channels;
int32_t to_do = ps->fade_left;
if (ps->play_position < ps->fade_start) {
start = samples_left - (ps->play_position + samples_left - ps->fade_start);
fade_pos = 0;
}
else {
start = 0;
fade_pos = ps->play_position - ps->fade_start;
}
if (to_do > samples_left - start)
to_do = samples_left - start;
//TODO: use delta fadedness to improve performance?
for (s = start; s < start + to_do; s++, fade_pos++) {
double fadedness = (double)(ps->fade_duration - fade_pos) / ps->fade_duration;
for (ch = 0; ch < channels; ch++) {
buf[s*channels + ch] = (sample_t)buf[s*channels + ch] * fadedness;
}
}
ps->fade_left -= to_do;
/* next samples after fade end would be pad end/silence, so we can just memset */
memset(buf + (start + to_do) * channels, 0, (samples_left - to_do - start) * sizeof(sample_t) * channels);
return start + to_do;
if (ps->play_position < ps->fade_start) {
start = samples_done - (ps->play_position + samples_done - ps->fade_start);
fade_pos = 0;
}
else {
start = 0;
fade_pos = ps->play_position - ps->fade_start;
}
if (to_do > samples_done - start)
to_do = samples_done - start;
//TODO: use delta fadedness to improve performance?
for (int s = start; s < start + to_do; s++, fade_pos++) {
double fadedness = (double)(ps->fade_duration - fade_pos) / ps->fade_duration;
for (int ch = 0; ch < channels; ch++) {
buf[s * channels + ch] = (sample_t)(buf[s*channels + ch] * fadedness);
}
}
ps->fade_left -= to_do;
/* next samples after fade end would be pad end/silence, so we can just memset */
memset(buf + (start + to_do) * channels, 0, (samples_done - to_do - start) * sizeof(sample_t) * channels);
}
static int render_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_left) {
// adds null samples after decode
// pad-end works like fades, where part of buf is samples and part is padding (blank)
// (beyond pad end normally is silence, except with segmented layout)
static int play_op_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_done) {
play_config_t* pc = &vgmstream->config;
play_state_t* ps = &vgmstream->pstate;
if (pc->play_forever)
return 0;
if (samples_done == 0)
return 0;
if (ps->play_position + samples_done < ps->pad_end_start)
return 0;
int channels = vgmstream->pstate.output_channels;
int skip = 0;
int32_t to_do;
/* pad end works like fades, where part of buf samples and part padding (silent),
* calc exact totals (beyond pad end normally is silence, except with segmented layout) */
if (ps->play_position < ps->pad_end_start) {
skip = ps->pad_end_start - ps->play_position;
to_do = ps->pad_end_duration;
@ -421,92 +289,86 @@ static int render_pad_end(VGMSTREAM* vgmstream, sample_t* buf, int samples_left)
to_do = (ps->pad_end_start + ps->pad_end_duration) - ps->play_position;
}
if (to_do > samples_left - skip)
to_do = samples_left - skip;
if (to_do > samples_done - skip)
to_do = samples_done - skip;
memset(buf + (skip * channels), 0, to_do * sizeof(sample_t) * channels);
return skip + to_do;
}
/* Decode data into sample buffer. Controls the "external" part of the decoding,
* while layout/decode control the "internal" part. */
int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
// clamp final play_position + done samples. Probably doesn't matter, but just in case.
static void play_adjust_totals(VGMSTREAM* vgmstream, render_helper_t* renderer, int sample_count) {
play_state_t* ps = &vgmstream->pstate;
int samples_to_do = sample_count;
int samples_done = 0;
int done;
sample_t* tmpbuf = buf;
ps->play_position += renderer->samples_done;
/* simple mode with no settings (just skip everything below) */
if (!vgmstream->config_enabled) {
render_layout(buf, samples_to_do, vgmstream);
mix_vgmstream(buf, samples_to_do, vgmstream);
return samples_to_do;
}
/* trim may go first since it doesn't need output nor changes totals */
if (ps->trim_begin_left) {
render_trim(vgmstream);
}
/* adds empty samples to buf */
if (ps->pad_begin_left) {
done = render_pad_begin(vgmstream, tmpbuf, samples_to_do);
samples_done += done;
samples_to_do -= done;
tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */
}
/* end padding (before to avoid decoding if possible, but must be inside pad region) */
if (!vgmstream->config.play_forever
&& ps->play_position /*+ samples_to_do*/ >= ps->pad_end_start
&& samples_to_do) {
done = render_pad_end(vgmstream, tmpbuf, samples_to_do);
samples_done += done;
samples_to_do -= done;
tmpbuf += done * vgmstream->pstate.output_channels; /* as if mixed */
}
/* main decode */
{ //if (samples_to_do) /* 0 ok, less likely */
done = render_layout(tmpbuf, samples_to_do, vgmstream);
mix_vgmstream(tmpbuf, done, vgmstream);
samples_done += done;
if (!vgmstream->config.play_forever) {
/* simple fadeout */
if (ps->fade_left && ps->play_position + done >= ps->fade_start) {
render_fade(vgmstream, tmpbuf, done);
}
/* silence leftover buf samples (rarely used when no fade is set) */
if (ps->play_position + done >= ps->pad_end_start) {
render_pad_end(vgmstream, tmpbuf, done);
}
}
tmpbuf += done * vgmstream->pstate.output_channels;
}
vgmstream->pstate.play_position += samples_done;
/* signal end */
if (!vgmstream->config.play_forever
&& ps->play_position > ps->play_duration) {
/* 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;
samples_done = (sample_count - excess);
renderer->samples_done = (sample_count - excess);
ps->play_position = ps->play_duration;
}
return samples_done;
}
/*****************************************************************************/
/* Decode data into sample buffer. Controls the "external" part of the decoding,
* while layout/decode control the "internal" part.
*
* A stream would be "externally" rendered like this:
* [ pad-begin ]( trim )[ decoded data * N loops ][ pad-end ]
* \ end-fade |
*
* Which part we are in depends on play_position. Because vgmstream render's
* buf may fall anywhere in the middle of all that. Since some ops add "fake" (non-decoded)
* samples to buf, we need to
*/
int render_vgmstream(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) {
render_helper_t renderer = {0};
renderer.tmpbuf = buf;
renderer.samples_done = 0;
renderer.samples_to_do = sample_count;
//sbuf_init16(&renderer.sbuf, buf, sample_count, vgmstream->channels);
/* simple mode with no settings (just skip everything below) */
if (!vgmstream->config_enabled) {
render_layout(buf, renderer.samples_to_do, vgmstream);
mix_vgmstream(buf, renderer.samples_to_do, vgmstream);
return renderer.samples_to_do;
}
/* adds empty samples to buf and moves it */
play_op_pad_begin(vgmstream, &renderer);
/* trim decoder output (may go anywhere before main render since it doesn't use render output) */
play_op_trim(vgmstream, &renderer);
/* main decode */
int done = render_layout(renderer.tmpbuf, renderer.samples_to_do, vgmstream);
mix_vgmstream(renderer.tmpbuf, done, vgmstream);
/* simple fadeout over decoded data (after mixing since usually results in less samples) */
play_op_fade(vgmstream, renderer.tmpbuf, done);
/* silence leftover buf samples (after fade as rarely may mix decoded buf + trim samples when no fade is set)
* (could be done before render to "consume" buf but doesn't matter much) */
play_op_pad_end(vgmstream, renderer.tmpbuf, done);
renderer.samples_done += done;
//renderer.samples_to_do -= done; //not useful at this point
//renderer.tmpbuf += done * vgmstream->pstate.output_channels;
play_adjust_totals(vgmstream, &renderer, sample_count);
return renderer.samples_done;
}

View file

@ -0,0 +1,85 @@
#include <stdlib.h>
#include <string.h>
#include "../util.h"
#include "sbuf.h"
#if 0
/* skips N samples from current sbuf */
void sbuf_init16(sbuf_t* sbuf, int16_t* buf, int samples, int channels) {
memset(sbuf, 0, sizeof(sbuf_t));
sbuf->buf = buf;
sbuf->samples = samples;
sbuf->channels = channels;
sbuf->fmt = SFMT_S16;
}
#endif
// TODO decide if using float 1.0 style or 32767 style (fuzzy PCM changes when doing that)
void sbuf_copy_s16_to_f32(float* buf_f32, int16_t* buf_s16, int samples, int channels) {
for (int s = 0; s < samples * channels; s++) {
buf_f32[s] = (float)buf_s16[s]; // / 32767.0f
}
}
void sbuf_copy_f32_to_s16(int16_t* buf_s16, float* buf_f32, int samples, int channels) {
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast, as it's the fastest
*/
for (int s = 0; s < samples * channels; s++) {
buf_s16[s] = clamp16( buf_f32[s]); // * 32767.0f
}
}
void sbuf_copy_samples(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];
}
}
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;
}
}
}
}
/* 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++;
}
}
void sbuf_silence(sample_t* dst, int samples, int channels, int filled) {
memset(dst + filled * channels, 0, (samples - filled) * channels * sizeof(sample_t));
}
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;
*dst = outbuf_re;
return true;
}

View file

@ -1,28 +1,52 @@
#ifndef _SBUF_H
#define _SBUF_H
#ifndef _SBUF_H_
#define _SBUF_H_
#include "../streamtypes.h"
// TODO decide if using float 1.0 style or 32767 style (fuzzy PCM changes when doing that)
static inline void sbuf_copy_s16_to_f32(float* buf_f32, int16_t* buf_s16, int samples, int channels) {
for (int s = 0; s < samples * channels; s++) {
buf_f32[s] = (float)buf_s16[s]; // / 32767.0f
}
}
#if 0
/* interleaved: buffer for all channels = [ch*s] = (ch1 ch2 ch1 ch2 ch1 ch2 ch1 ch2 ...) */
/* planar: buffer per channel = [ch][s] = (c1 c1 c1 c1 ...) (c2 c2 c2 c2 ...) */
typedef enum {
SFMT_NONE,
SFMT_S16,
SFMT_F32,
//SFMT_S24,
//SFMT_S32,
//SFMT_S16P,
//SFMT_F32P,
} sfmt_t;
static inline void sbuf_copy_f32_to_s16(int16_t* buf_s16, float* buf_f32, int samples, int channels) {
/* when casting float to int, value is simply truncated:
* - (int)1.7 = 1, (int)-1.7 = -1
* alts for more accurate rounding could be:
* - (int)floor(f)
* - (int)(f < 0 ? f - 0.5f : f + 0.5f)
* - (((int) (f1 + 32768.5)) - 32768)
* - etc
* but since +-1 isn't really audible we'll just cast as it's the fastest
*/
for (int s = 0; s < samples * channels; s++) {
buf_s16[s] = clamp16( buf_f32[s]); // * 32767.0f
}
}
typedef struct {
void* buf; /* current sample buffer */
int samples; /* max samples */
int channels; /* interleaved step or planar buffers */
sfmt_t fmt; /* buffer type */
//int filled; /* samples in buffer */
//int planar;
} sbuf_t;
void sbuf_init16(sbuf_t* sbuf, int16_t* buf, int samples, int channels);
void sbuf_clamp(sbuf_t* sbuf, int samples);
/* skips N samples from current sbuf */
void sbuf_consume(sbuf_t* sbuf, int samples);
#endif
/* it's probably slightly faster to make those inline'd, but aren't called that often to matter (given big enough total samples) */
// TODO decide if using float 1.0 style or 32767 style (fuzzy PCM changes when doing that)
void sbuf_copy_s16_to_f32(float* buf_f32, int16_t* buf_s16, int samples, int channels);
void sbuf_copy_f32_to_s16(int16_t* buf_s16, float* buf_f32, int samples, int channels);
void sbuf_copy_samples(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);
void sbuf_silence(sample_t* dst, int samples, int channels, int filled);
bool sbuf_realloc(sample_t** dst, int samples, int channels);
#endif

View file

@ -21,19 +21,19 @@ struct VGMSTREAM_TAGS {
char targetpath[VGMSTREAM_TAGS_LINE_MAX];
/* tag section for filename (see comments below) */
int section_found;
bool section_found;
off_t section_start;
off_t section_end;
off_t offset;
/* commands */
int autotrack_on;
int autotrack_written;
bool autotrack_on;
bool autotrack_written;
int track_count;
int exact_match;
bool exact_match;
int autoalbum_on;
int autoalbum_written;
bool autoalbum_on;
bool autoalbum_written;
};
@ -50,7 +50,7 @@ static void tags_clean(VGMSTREAM_TAGS* tag) {
}
VGMSTREAM_TAGS* vgmstream_tags_init(const char* *tag_key, const char* *tag_val) {
VGMSTREAM_TAGS* tags = malloc(sizeof(VGMSTREAM_TAGS));
VGMSTREAM_TAGS* tags = calloc(1, sizeof(VGMSTREAM_TAGS));
if (!tags) goto fail;
*tag_key = tags->key;
@ -102,7 +102,7 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
if (tags->autotrack_on && !tags->autotrack_written) {
sprintf(tags->key, "%s", "TRACK");
sprintf(tags->val, "%i", tags->track_count);
tags->autotrack_written = 1;
tags->autotrack_written = true;
return 1;
}
@ -119,7 +119,7 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
sprintf(tags->key, "%s", "ALBUM");
sprintf(tags->val, "%s", path+1);
tags->autoalbum_written = 1;
tags->autoalbum_written = true;
return 1;
}
@ -150,13 +150,13 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
if (ok == 1 || ok == 2) {
int key_len = n2 - n1;
if (strncasecmp(tags->key, "AUTOTRACK", key_len) == 0) {
tags->autotrack_on = 1;
tags->autotrack_on = true;
}
else if (strncasecmp(tags->key, "AUTOALBUM", key_len) == 0) {
tags->autoalbum_on = 1;
tags->autoalbum_on = true;
}
else if (strncasecmp(tags->key, "EXACTMATCH", key_len) == 0) {
tags->exact_match = 1;
tags->exact_match = true;
}
continue; /* not an actual tag */
@ -210,7 +210,7 @@ int vgmstream_tags_next_tag(VGMSTREAM_TAGS* tags, STREAMFILE* tagfile) {
if (filename_found) {
/* section ok, start would be set before this (or be 0) */
tags->section_end = tags->offset;
tags->section_found = 1;
tags->section_found = true;
tags->offset = tags->section_start;
}
else {

View file

@ -489,7 +489,7 @@ static ffmpeg_codec_data* init_ffmpeg_mp4_custom(STREAMFILE* sf, mp4_custom_t* m
if (buf_len > 0x100000) /* ??? */
goto fail;
buf = malloc(buf_len);
buf = calloc(1, buf_len);
if (!buf) goto fail;
bytes = make_m4a_header(buf, buf_len, mp4, sf, type); /* before changing stream_offset/size */

View file

@ -320,7 +320,7 @@ circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags) {
circus_handle_t* handle = NULL;
int scale_index, err;
handle = malloc(sizeof(circus_handle_t));
handle = calloc(1, sizeof(circus_handle_t));
if (!handle) goto fail;
handle->start = start;

View file

@ -146,7 +146,7 @@ static void THuff_SetPositionData(THuff* self, THuffPositionData* s);
//------------------------------------------------------------------------------
//create
static THuff* THuff_Create(TStream* buf) {
THuff* self = malloc(sizeof(THuff));
THuff* self = calloc(1, sizeof(THuff));
if (!self) return NULL;
//define stream
@ -558,7 +558,7 @@ struct TCompressWaveData {
//-----------------------------------------------------------
//create
TCompressWaveData* TCompressWaveData_Create(void) {
TCompressWaveData* self = malloc(sizeof(TCompressWaveData));
TCompressWaveData* self = calloc(1, sizeof(TCompressWaveData));
if (!self) return NULL;
#if 0
self->Data = NULL;

View file

@ -80,7 +80,7 @@ static int is_use_runlength(NWAData* nwa) {
NWAData* nwalib_open(STREAMFILE* sf) {
uint8_t header[0x2c] = {0};
int i;
NWAData* const nwa = malloc(sizeof(NWAData));
NWAData* const nwa = calloc(1, sizeof(NWAData));
if (!nwa) goto fail;
//NWAData::ReadHeader

View file

@ -1140,7 +1140,7 @@ tac_handle_t* tac_init(const uint8_t* buf, int buf_size) {
if (buf_size < TAC_BLOCK_SIZE)
goto fail;
handle = malloc(sizeof(tac_handle_t));
handle = calloc(1, sizeof(tac_handle_t));
if (!handle) goto fail;
{

View file

@ -392,6 +392,7 @@ static const char* extension_list[] = {
"npsf", //fake extension/header id for .nps (in bigfiles)
"nsa",
"nsopus",
"ntx",
"nub",
"nub2",
"nus3audio",
@ -956,7 +957,7 @@ static const layout_info layout_info_list[] = {
{layout_blocked_str_snds, "blocked (.str SNDS)"},
{layout_blocked_ws_aud, "blocked (Westwood Studios .aud)"},
{layout_blocked_dec, "blocked (DEC)"},
{layout_blocked_vs, "blocked (Melbourne House VS)"},
{layout_blocked_vs_mh, "blocked (Melbourne House VS)"},
{layout_blocked_mul, "blocked (MUL)"},
{layout_blocked_gsb, "blocked (GSB)"},
{layout_blocked_thp, "blocked (THP)"},
@ -1074,7 +1075,6 @@ static const meta_info meta_info_list[] = {
{meta_FSB5, "FMOD FSB5 header"},
{meta_RWAX, "Konami RWAX header"},
{meta_XWB, "Microsoft XWB header"},
{meta_PS2_XA30, "Reflections XA30 PS2 header"},
{meta_MUSC, "Krome MUSC header"},
{meta_MUSX, "Eurocom MUSX header"},
{meta_FILP, "cavia FILp header"},
@ -1103,7 +1103,7 @@ static const meta_info meta_info_list[] = {
{meta_SDT, "High Voltage .sdt header"},
{meta_WVS, "Swingin' Ape .WVS header"},
{meta_DEC, "Falcom .DEC RIFF header"},
{meta_VS, "Melbourne House .VS header"},
{meta_VS_MH, "Melbourne House .VS header"},
{meta_STR_SEGA, "Sega Stream Asset Builder header"},
{meta_STR_SEGA_custom, "Sega Stream Asset Builder header (custom)"},
{meta_XMU, "Outrage XMU header"},

View file

@ -1,38 +1,39 @@
#include "layout.h"
#include "../vgmstream.h"
#include "../base/decode.h"
#include "../base/sbuf.h"
#include "../coding/coding.h"
/* Decodes samples for blocked streams.
* Data is divided into headered blocks with a bunch of data. The layout calls external helper functions
* when a block is decoded, and those must parse the new block and move offsets accordingly. */
void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream) {
int samples_written = 0;
int frame_size, samples_per_frame, samples_this_block;
void render_vgmstream_blocked(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) {
frame_size = decode_get_frame_size(vgmstream);
samples_per_frame = decode_get_samples_per_frame(vgmstream);
samples_this_block = 0;
int frame_size = decode_get_frame_size(vgmstream);
int samples_per_frame = decode_get_samples_per_frame(vgmstream);
int samples_this_block = 0;
if (vgmstream->current_block_samples) {
samples_this_block = vgmstream->current_block_samples;
} else if (frame_size == 0) { /* assume 4 bit */ //TODO: decode_get_frame_size() really should return bits... */
}
else if (frame_size == 0) {
//TO-DO: this case doesn't seem possible, codecs that return frame_size 0 (should) set current_block_samples
samples_this_block = vgmstream->current_block_size * 2 * samples_per_frame;
} else {
}
else {
samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame;
}
while (samples_written < sample_count) {
int samples_filled = 0;
while (samples_filled < sample_count) {
int samples_to_do;
if (vgmstream->loop_flag && decode_do_loop(vgmstream)) {
/* handle looping, readjust back to loop start values */
if (vgmstream->current_block_samples) {
samples_this_block = vgmstream->current_block_samples;
} else if (frame_size == 0) { /* assume 4 bit */ //TODO: decode_get_frame_size() really should return bits... */
} else if (frame_size == 0) { /* assume 4 bit */
samples_this_block = vgmstream->current_block_size * 2 * samples_per_frame;
} else {
samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame;
@ -42,28 +43,26 @@ void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM*
if (samples_this_block < 0) {
/* probably block bug or EOF, next calcs would give wrong values/segfaults/infinite loop */
VGM_LOG("layout_blocked: wrong block samples at 0x%x\n", (uint32_t)vgmstream->current_block_offset);
memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t));
break;
VGM_LOG("BLOCKED: wrong block samples\n");
goto decode_fail;
}
if (vgmstream->current_block_offset < 0 || vgmstream->current_block_offset == 0xFFFFFFFF) {
/* probably block bug or EOF, block functions won't be able to read anything useful/infinite loop */
VGM_LOG("layout_blocked: wrong block offset found\n");
memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t));
break;
VGM_LOG("BLOCKED: wrong block offset found\n");
goto decode_fail;
}
samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream);
if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_written;
if (samples_to_do > sample_count - samples_filled)
samples_to_do = sample_count - samples_filled;
if (samples_to_do > 0) {
/* samples_this_block = 0 is allowed (empty block, do nothing then move to next block) */
decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
decode_vgmstream(vgmstream, samples_filled, samples_to_do, outbuf);
}
samples_written += samples_to_do;
samples_filled += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
@ -71,23 +70,29 @@ void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM*
/* move to next block when all samples are consumed */
if (vgmstream->samples_into_block == samples_this_block
/*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */ //todo
block_update(vgmstream->next_block_offset,vgmstream);
block_update(vgmstream->next_block_offset, vgmstream);
/* update since these may change each block */
frame_size = decode_get_frame_size(vgmstream);
samples_per_frame = decode_get_samples_per_frame(vgmstream);
if (vgmstream->current_block_samples) {
samples_this_block = vgmstream->current_block_samples;
} else if (frame_size == 0) { /* assume 4 bit */ //TODO: decode_get_frame_size() really should return bits... */
}
else if (frame_size == 0) {
//TO-DO: this case doesn't seem possible, codecs that return frame_size 0 (should) set current_block_samples
samples_this_block = vgmstream->current_block_size * 2 * samples_per_frame;
} else {
}
else {
samples_this_block = vgmstream->current_block_size / frame_size * samples_per_frame;
}
vgmstream->samples_into_block = 0;
}
}
return;
decode_fail:
sbuf_silence(outbuf, sample_count, vgmstream->channels, samples_filled);
}
/* helper functions to parse new block */
@ -132,8 +137,8 @@ void block_update(off_t block_offset, VGMSTREAM* vgmstream) {
case layout_blocked_gsb:
block_update_gsb(block_offset,vgmstream);
break;
case layout_blocked_vs:
block_update_vs(block_offset,vgmstream);
case layout_blocked_vs_mh:
block_update_vs_mh(block_offset,vgmstream);
break;
case layout_blocked_xvas:
block_update_xvas(block_offset,vgmstream);
@ -214,43 +219,3 @@ void block_update(off_t block_offset, VGMSTREAM* vgmstream) {
break;
}
}
void blocked_count_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset) {
if (vgmstream == NULL)
return;
int block_samples;
off_t max_offset = get_streamfile_size(sf);
vgmstream->next_block_offset = offset;
do {
block_update(vgmstream->next_block_offset, vgmstream);
if (vgmstream->current_block_samples < 0 || vgmstream->current_block_size == 0xFFFFFFFF)
break;
if (vgmstream->current_block_samples) {
block_samples = vgmstream->current_block_samples;
}
else {
switch(vgmstream->coding_type) {
case coding_PCM16LE:
case coding_PCM16_int: block_samples = pcm16_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_PCM8_int:
case coding_PCM8_U_int: block_samples = pcm8_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_XBOX_IMA_mono:
case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_PSX: block_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break;
default:
VGM_LOG("BLOCKED: missing codec\n");
return;
}
}
vgmstream->num_samples += block_samples;
}
while (vgmstream->next_block_offset < max_offset);
block_update(offset, vgmstream); /* reset */
}

View file

@ -2,13 +2,12 @@
#include "../vgmstream.h"
/* mini-blocks of size + data */
void block_update_vs(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int i;
void block_update_vs_mh(off_t block_offset, VGMSTREAM* vgmstream) {
STREAMFILE* sf = vgmstream->ch[0].streamfile;
for (i = 0; i < vgmstream->channels; i++) {
for (int i = 0; i < vgmstream->channels; i++) {
vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = read_32bitLE(vgmstream->current_block_offset,streamFile);
vgmstream->current_block_size = read_32bitLE(vgmstream->current_block_offset,sf);
vgmstream->next_block_offset = vgmstream->current_block_offset + vgmstream->current_block_size + 0x04;
vgmstream->ch[i].offset = vgmstream->current_block_offset + 0x04;
if (i == 0) block_offset=vgmstream->next_block_offset;

View file

@ -1,43 +1,42 @@
#include "layout.h"
#include "../vgmstream.h"
#include "../base/decode.h"
#include "../base/sbuf.h"
/* Decodes samples for flat streams.
* Data forms a single stream, and the decoder may internally skip chunks and move offsets as needed. */
void render_vgmstream_flat(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) {
int samples_written = 0;
int samples_per_frame, samples_this_block;
samples_per_frame = decode_get_samples_per_frame(vgmstream);
samples_this_block = vgmstream->num_samples; /* do all samples if possible */
int samples_per_frame = decode_get_samples_per_frame(vgmstream);
int samples_this_block = vgmstream->num_samples; /* do all samples if possible */
while (samples_written < sample_count) {
int samples_to_do;
/* write samples */
int samples_filled = 0;
while (samples_filled < sample_count) {
if (vgmstream->loop_flag && decode_do_loop(vgmstream)) {
/* handle looping */
continue;
}
samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream);
if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_written;
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 == 0) { /* when decoding more than num_samples */
VGM_LOG_ONCE("FLAT: samples_to_do 0\n");
if (samples_to_do <= 0) { /* when decoding more than num_samples */
VGM_LOG_ONCE("FLAT: wrong samples_to_do\n");
goto decode_fail;
}
decode_vgmstream(vgmstream, samples_written, samples_to_do, outbuf);
decode_vgmstream(vgmstream, samples_filled, samples_to_do, outbuf);
samples_written += samples_to_do;
samples_filled += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
}
return;
decode_fail:
memset(outbuf + samples_written * vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t));
sbuf_silence(outbuf, sample_count, vgmstream->channels, samples_filled);
}

View file

@ -1,157 +1,197 @@
#include "layout.h"
#include "../vgmstream.h"
#include "../base/decode.h"
#include "../base/sbuf.h"
typedef struct {
/* default */
int samples_per_frame_d;
int samples_this_block_d;
/* first */
int samples_per_frame_f;
int samples_this_block_f;
/* last */
int samples_per_frame_l;
int samples_this_block_l;
bool has_interleave_first;
bool has_interleave_last;
bool has_interleave_internal_updates;
} layout_config_t;
static bool setup_helper(layout_config_t* layout, VGMSTREAM* vgmstream) {
//TO-DO: this could be pre-calc'd after main init
layout->has_interleave_first = vgmstream->interleave_first_block_size && vgmstream->channels > 1;
layout->has_interleave_last = vgmstream->interleave_last_block_size && vgmstream->channels > 1;
layout->has_interleave_internal_updates = vgmstream->codec_internal_updates;
{
int frame_size_d = decode_get_frame_size(vgmstream);
layout->samples_per_frame_d = decode_get_samples_per_frame(vgmstream);
if (frame_size_d == 0 || layout->samples_per_frame_d == 0)
goto fail;
layout->samples_this_block_d = vgmstream->interleave_block_size / frame_size_d * layout->samples_per_frame_d;
}
if (layout->has_interleave_first) {
int frame_size_f = decode_get_frame_size(vgmstream);
layout->samples_per_frame_f = decode_get_samples_per_frame(vgmstream); //todo samples per shortframe
if (frame_size_f == 0 || layout->samples_per_frame_f == 0)
goto fail;
layout->samples_this_block_f = vgmstream->interleave_first_block_size / frame_size_f * layout->samples_per_frame_f;
}
if (layout->has_interleave_last) {
int frame_size_l = decode_get_shortframe_size(vgmstream);
layout->samples_per_frame_l = decode_get_samples_per_shortframe(vgmstream);
if (frame_size_l == 0 || layout->samples_per_frame_l == 0) goto fail;
layout->samples_this_block_l = vgmstream->interleave_last_block_size / frame_size_l * layout->samples_per_frame_l;
}
return true;
fail:
return false;
}
static void update_default_values(layout_config_t* layout, VGMSTREAM* vgmstream, int* p_samples_per_frame, int* p_samples_this_block) {
if (layout->has_interleave_first &&
vgmstream->current_sample < layout->samples_this_block_f) {
*p_samples_per_frame = layout->samples_per_frame_f;
*p_samples_this_block = layout->samples_this_block_f;
}
else if (layout->has_interleave_last &&
vgmstream->current_sample - vgmstream->samples_into_block + layout->samples_this_block_d > vgmstream->num_samples) {
*p_samples_per_frame = layout->samples_per_frame_l;
*p_samples_this_block = layout->samples_this_block_l;
}
else {
*p_samples_per_frame = layout->samples_per_frame_d;
*p_samples_this_block = layout->samples_this_block_d;
}
}
static void update_loop_values(layout_config_t* layout, VGMSTREAM* vgmstream, int* p_samples_per_frame, int* p_samples_this_block) {
if (layout->has_interleave_first &&
vgmstream->current_sample < layout->samples_this_block_f) {
/* use first interleave*/
*p_samples_per_frame = layout->samples_per_frame_f;
*p_samples_this_block = layout->samples_this_block_f;
if (*p_samples_this_block == 0 && vgmstream->channels == 1)
*p_samples_this_block = vgmstream->num_samples;
}
else if (layout->has_interleave_last) { /* assumes that won't loop back into a interleave_last */
*p_samples_per_frame = layout->samples_per_frame_d;
*p_samples_this_block = layout->samples_this_block_d;
if (*p_samples_this_block == 0 && vgmstream->channels == 1)
*p_samples_this_block = vgmstream->num_samples;
}
}
static void update_offsets(layout_config_t* layout, VGMSTREAM* vgmstream, int* p_samples_per_frame, int* p_samples_this_block) {
int channels = vgmstream->channels;
if (layout->has_interleave_first &&
vgmstream->current_sample == layout->samples_this_block_f) {
/* interleave during first interleave: restore standard frame size after going past first interleave */
*p_samples_per_frame = layout->samples_per_frame_d;
*p_samples_this_block = layout->samples_this_block_d;
if (*p_samples_this_block == 0 && channels == 1)
*p_samples_this_block = vgmstream->num_samples;
for (int ch = 0; ch < channels; ch++) {
off_t skip = vgmstream->interleave_first_skip * (channels - 1 - ch) +
vgmstream->interleave_first_block_size * (channels - ch) +
vgmstream->interleave_block_size * ch;
vgmstream->ch[ch].offset += skip;
}
}
else if (layout->has_interleave_last &&
vgmstream->current_sample + *p_samples_this_block > vgmstream->num_samples) {
/* interleave during last interleave: adjust values again if inside last interleave */
*p_samples_per_frame = layout->samples_per_frame_l;
*p_samples_this_block = layout->samples_this_block_l;
if (*p_samples_this_block == 0 && channels == 1)
*p_samples_this_block = vgmstream->num_samples;
for (int ch = 0; ch < channels; ch++) {
off_t skip = vgmstream->interleave_block_size * (channels - ch) +
vgmstream->interleave_last_block_size * ch;
vgmstream->ch[ch].offset += skip;
}
}
else if (layout->has_interleave_internal_updates) {
/* interleave for some decoders that have already moved offsets over their data, so skip other channels's data */
for (int ch = 0; ch < channels; ch++) {
off_t skip = vgmstream->interleave_block_size * (channels - 1);
vgmstream->ch[ch].offset += skip;
}
}
else {
/* regular interleave */
for (int ch = 0; ch < channels; ch++) {
off_t skip = vgmstream->interleave_block_size * channels;
vgmstream->ch[ch].offset += skip;
}
}
vgmstream->samples_into_block = 0;
}
/* Decodes samples for interleaved streams.
* Data has interleaved chunks per channel, and once one is decoded the layout moves offsets,
* skipping other chunks (essentially a simplified variety of blocked layout).
* Incompatible with decoders that move offsets. */
void render_vgmstream_interleave(sample_t * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
int samples_written = 0;
int samples_per_frame, samples_this_block; /* used */
int samples_per_frame_d = 0, samples_this_block_d = 0; /* default */
int samples_per_frame_f = 0, samples_this_block_f = 0; /* first */
int samples_per_frame_l = 0, samples_this_block_l = 0; /* last */
int has_interleave_first = vgmstream->interleave_first_block_size && vgmstream->channels > 1;
int has_interleave_last = vgmstream->interleave_last_block_size && vgmstream->channels > 1;
int has_interleave_internal_updates = vgmstream->codec_internal_updates;
void render_vgmstream_interleave(sample_t* outbuf, int32_t sample_count, VGMSTREAM* vgmstream) {
layout_config_t layout = {0};
if (!setup_helper(&layout, vgmstream)) {
VGM_LOG_ONCE("INTERLEAVE: wrong config found\n");
sbuf_silence(outbuf, sample_count, vgmstream->channels, 0);
return;
}
/* setup */
{
int frame_size_d = decode_get_frame_size(vgmstream);
samples_per_frame_d = decode_get_samples_per_frame(vgmstream);
if (frame_size_d == 0 || samples_per_frame_d == 0) goto fail;
samples_this_block_d = vgmstream->interleave_block_size / frame_size_d * samples_per_frame_d;
}
if (has_interleave_first) {
int frame_size_f = decode_get_frame_size(vgmstream);
samples_per_frame_f = decode_get_samples_per_frame(vgmstream); //todo samples per shortframe
if (frame_size_f == 0 || samples_per_frame_f == 0) goto fail;
samples_this_block_f = vgmstream->interleave_first_block_size / frame_size_f * samples_per_frame_f;
}
if (has_interleave_last) {
int frame_size_l = decode_get_shortframe_size(vgmstream);
samples_per_frame_l = decode_get_samples_per_shortframe(vgmstream);
if (frame_size_l == 0 || samples_per_frame_l == 0) goto fail;
samples_this_block_l = vgmstream->interleave_last_block_size / frame_size_l * samples_per_frame_l;
}
/* set current values */
if (has_interleave_first &&
vgmstream->current_sample < samples_this_block_f) {
samples_per_frame = samples_per_frame_f;
samples_this_block = samples_this_block_f;
}
else if (has_interleave_last &&
vgmstream->current_sample - vgmstream->samples_into_block + samples_this_block_d > vgmstream->num_samples) {
samples_per_frame = samples_per_frame_l;
samples_this_block = samples_this_block_l;
}
else {
samples_per_frame = samples_per_frame_d;
samples_this_block = samples_this_block_d;
}
int samples_per_frame, samples_this_block;
update_default_values(&layout, vgmstream, &samples_per_frame, &samples_this_block);
/* mono interleaved stream with no layout set, just behave like flat layout */
if (samples_this_block == 0 && vgmstream->channels == 1)
samples_this_block = vgmstream->num_samples;
/* write samples */
while (samples_written < sample_count) {
int samples_to_do;
int samples_filled = 0;
while (samples_filled < sample_count) {
if (vgmstream->loop_flag && decode_do_loop(vgmstream)) {
/* handle looping, restore standard interleave sizes */
if (has_interleave_first &&
vgmstream->current_sample < samples_this_block_f) {
/* use first interleave*/
samples_per_frame = samples_per_frame_f;
samples_this_block = samples_this_block_f;
if (samples_this_block == 0 && vgmstream->channels == 1)
samples_this_block = vgmstream->num_samples;
}
else if (has_interleave_last) { /* assumes that won't loop back into a interleave_last */
samples_per_frame = samples_per_frame_d;
samples_this_block = samples_this_block_d;
if (samples_this_block == 0 && vgmstream->channels == 1)
samples_this_block = vgmstream->num_samples;
}
update_loop_values(&layout, vgmstream, &samples_per_frame, &samples_this_block);
continue;
}
samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream);
if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_written;
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 == 0) { /* happens when interleave is not set */
goto fail;
if (samples_to_do <= 0) { /* happens when interleave is not set */
VGM_LOG_ONCE("INTERLEAVE: wrong samples_to_do\n");
goto decode_fail;
}
decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
decode_vgmstream(vgmstream, samples_filled, samples_to_do, outbuf);
samples_written += samples_to_do;
samples_filled += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
/* move to next interleaved block when all samples are consumed */
if (vgmstream->samples_into_block == samples_this_block) {
int ch;
if (has_interleave_first &&
vgmstream->current_sample == samples_this_block_f) {
/* restore standard frame size after going past first interleave */
samples_per_frame = samples_per_frame_d;
samples_this_block = samples_this_block_d;
if (samples_this_block == 0 && vgmstream->channels == 1)
samples_this_block = vgmstream->num_samples;
for (ch = 0; ch < vgmstream->channels; ch++) {
off_t skip =
vgmstream->interleave_first_skip*(vgmstream->channels-1-ch) +
vgmstream->interleave_first_block_size*(vgmstream->channels-ch) +
vgmstream->interleave_block_size*ch;
vgmstream->ch[ch].offset += skip;
}
}
else if (has_interleave_last &&
vgmstream->current_sample + samples_this_block > vgmstream->num_samples) {
/* adjust values again if inside last interleave */
samples_per_frame = samples_per_frame_l;
samples_this_block = samples_this_block_l;
if (samples_this_block == 0 && vgmstream->channels == 1)
samples_this_block = vgmstream->num_samples;
for (ch = 0; ch < vgmstream->channels; ch++) {
off_t skip =
vgmstream->interleave_block_size*(vgmstream->channels-ch) +
vgmstream->interleave_last_block_size*ch;
vgmstream->ch[ch].offset += skip;
}
}
else if (has_interleave_internal_updates) {
for (ch = 0; ch < vgmstream->channels; ch++) {
off_t skip = vgmstream->interleave_block_size * (vgmstream->channels - 1);
vgmstream->ch[ch].offset += skip;
}
}
else {
for (ch = 0; ch < vgmstream->channels; ch++) {
off_t skip = vgmstream->interleave_block_size * vgmstream->channels;
vgmstream->ch[ch].offset += skip;
}
}
vgmstream->samples_into_block = 0;
update_offsets(&layout, vgmstream, &samples_per_frame, &samples_this_block);
}
}
return;
fail:
VGM_LOG_ONCE("layout_interleave: wrong values found\n");
memset(buffer + samples_written*vgmstream->channels, 0, (sample_count - samples_written) * vgmstream->channels * sizeof(sample_t));
decode_fail:
sbuf_silence(outbuf, sample_count, vgmstream->channels, samples_filled);
}

View file

@ -3,84 +3,69 @@
#include "../base/decode.h"
#include "../base/mixing.h"
#include "../base/plugins.h"
#include "../base/sbuf.h"
#define VGMSTREAM_MAX_LAYERS 255
#define VGMSTREAM_LAYER_SAMPLE_BUFFER 8192
/* Decodes samples for layered streams.
* Similar to flat layout, but decoded vgmstream are mixed into a final buffer, each vgmstream
* may have different codecs and number of channels, creating a single super-vgmstream.
* Usually combined with custom streamfiles to handle data interleaved in weird ways. */
* 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) {
int samples_written = 0;
layered_layout_data* data = vgmstream->layout_data;
int samples_per_frame, samples_this_block;
samples_per_frame = VGMSTREAM_LAYER_SAMPLE_BUFFER;
samples_this_block = vgmstream->num_samples; /* do all samples if possible */
while (samples_written < sample_count) {
int samples_to_do;
int layer, ch;
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 ch;
if (vgmstream->loop_flag && decode_do_loop(vgmstream)) {
/* handle looping (loop_layout has been called below) */
/* handle looping (loop_layout has been called inside) */
continue;
}
samples_to_do = decode_get_samples_to_do(samples_this_block, samples_per_frame, vgmstream);
if (samples_to_do > sample_count - samples_written)
samples_to_do = sample_count - samples_written;
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 <= 0) { /* when decoding more than num_samples */
VGM_LOG_ONCE("LAYERED: samples_to_do 0\n");
VGM_LOG_ONCE("LAYERED: wrong samples_to_do\n");
goto decode_fail;
}
/* decode all layers */
ch = 0;
for (layer = 0; layer < data->layer_count; layer++) {
int s, layer_ch, layer_channels;
for (int current_layer = 0; current_layer < data->layer_count; current_layer++) {
/* layers may have its own number of channels */
mixing_info(data->layers[layer], NULL, &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[layer]);
render_vgmstream(data->buffer, samples_to_do, data->layers[current_layer]);
/* mix layer samples to main samples */
for (layer_ch = 0; layer_ch < layer_channels; layer_ch++) {
for (s = 0; s < samples_to_do; s++) {
size_t layer_sample = s*layer_channels + layer_ch;
size_t buffer_sample = (samples_written+s)*data->output_channels + ch;
outbuf[buffer_sample] = data->buffer[layer_sample];
}
ch++;
}
sbuf_copy_layers(outbuf, data->output_channels, data->buffer, layer_channels, samples_to_do, samples_filled, ch);
ch += layer_channels;
}
samples_written += samples_to_do;
samples_filled += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
}
return;
decode_fail:
memset(outbuf + samples_written * data->output_channels, 0, (sample_count - samples_written) * data->output_channels * sizeof(sample_t));
sbuf_silence(outbuf, sample_count, data->output_channels, samples_filled);
}
void seek_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample) {
int layer;
layered_layout_data* data = vgmstream->layout_data;
for (layer = 0; layer < data->layer_count; layer++) {
for (int layer = 0; layer < data->layer_count; layer++) {
seek_vgmstream(data->layers[layer], seek_sample);
}
@ -89,11 +74,9 @@ void seek_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample) {
}
void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample) {
int layer;
layered_layout_data* data = vgmstream->layout_data;
for (layer = 0; layer < data->layer_count; layer++) {
for (int layer = 0; layer < data->layer_count; layer++) {
if (data->external_looping) {
/* looping is applied over resulting decode, as each layer is its own "solid" block with
* config and needs 'external' seeking */
@ -130,37 +113,35 @@ layered_layout_data* init_layout_layered(int layer_count) {
data = calloc(1, sizeof(layered_layout_data));
if (!data) goto fail;
data->layer_count = layer_count;
data->layers = calloc(layer_count, sizeof(VGMSTREAM*));
if (!data->layers) goto fail;
data->layer_count = layer_count;
return data;
fail:
free_layout_layered(data);
return NULL;
}
int setup_layout_layered(layered_layout_data* data) {
int i, max_input_channels = 0, max_output_channels = 0;
sample_t *outbuf_re = NULL;
bool setup_layout_layered(layered_layout_data* data) {
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
for (i = 0; i < data->layer_count; i++) {
int layer_input_channels, layer_output_channels;
int max_input_channels = 0;
int max_output_channels = 0;
for (int i = 0; i < data->layer_count; i++) {
if (data->layers[i] == NULL) {
VGM_LOG("LAYERED: no vgmstream in %i\n", i);
goto fail;
return false;
}
if (data->layers[i]->num_samples <= 0) {
VGM_LOG("LAYERED: no samples in %i\n", i);
goto fail;
return false;
}
/* different layers may have different input/output channels */
int layer_input_channels, layer_output_channels;
mixing_info(data->layers[i], &layer_input_channels, &layer_output_channels);
max_output_channels += layer_output_channels;
@ -171,12 +152,15 @@ int setup_layout_layered(layered_layout_data* data) {
/* a bit weird, but no matter */
if (data->layers[i]->sample_rate != data->layers[i-1]->sample_rate) {
VGM_LOG("LAYERED: layer %i has different sample rate\n", i);
//TO-DO: setup resampling
}
/* also weird */
#if 0
/* also weird but less so */
if (data->layers[i]->coding_type != data->layers[i-1]->coding_type) {
VGM_LOG("LAYERED: layer %i has different coding type\n", i);
}
#endif
}
/* loops and other values could be mismatched, but should be handled on allocate */
@ -192,44 +176,37 @@ int setup_layout_layered(layered_layout_data* data) {
}
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
goto fail;
return false;
/* create internal buffer big enough for mixing */
outbuf_re = realloc(data->buffer, VGMSTREAM_LAYER_SAMPLE_BUFFER*max_input_channels*sizeof(sample_t));
if (!outbuf_re) goto fail;
data->buffer = outbuf_re;
/* create internal buffer big enough for mixing all layers */
if (!sbuf_realloc(&data->buffer, VGMSTREAM_LAYER_SAMPLE_BUFFER, max_input_channels))
goto fail;
data->input_channels = max_input_channels;
data->output_channels = max_output_channels;
return 1;
return true;
fail:
return 0; /* caller is expected to free */
return false; /* caller is expected to free */
}
void free_layout_layered(layered_layout_data *data) {
int i;
if (!data)
return;
if (data->layers) {
for (i = 0; i < data->layer_count; i++) {
close_vgmstream(data->layers[i]);
}
free(data->layers);
for (int i = 0; i < data->layer_count; i++) {
close_vgmstream(data->layers[i]);
}
free(data->layers);
free(data->buffer);
free(data);
}
void reset_layout_layered(layered_layout_data *data) {
int i;
if (!data)
return;
for (i = 0; i < data->layer_count; i++) {
for (int i = 0; i < data->layer_count; i++) {
reset_vgmstream(data->layers[i]);
}
}

View file

@ -11,6 +11,7 @@ void render_vgmstream_flat(sample_t* buffer, int32_t sample_count, VGMSTREAM* vg
void render_vgmstream_interleave(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
/* segmented layout */
/* for files made of "continuous" segments, one per section of a song (using a complete sub-VGMSTREAM) */
typedef struct {
@ -25,12 +26,13 @@ typedef struct {
void render_vgmstream_segmented(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
segmented_layout_data* init_layout_segmented(int segment_count);
int setup_layout_segmented(segmented_layout_data* data);
bool setup_layout_segmented(segmented_layout_data* data);
void free_layout_segmented(segmented_layout_data* data);
void reset_layout_segmented(segmented_layout_data* data);
void seek_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample);
void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t loop_sample);
/* layered layout */
/* for files made of "parallel" layers, one per group of channels (using a complete sub-VGMSTREAM) */
typedef struct {
@ -45,16 +47,16 @@ typedef struct {
void render_vgmstream_layered(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
layered_layout_data* init_layout_layered(int layer_count);
int setup_layout_layered(layered_layout_data* data);
bool setup_layout_layered(layered_layout_data* data);
void free_layout_layered(layered_layout_data* data);
void reset_layout_layered(layered_layout_data* data);
void seek_layout_layered(VGMSTREAM* vgmstream, int32_t seek_sample);
void loop_layout_layered(VGMSTREAM* vgmstream, int32_t loop_sample);
/* blocked layouts */
void render_vgmstream_blocked(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream);
void block_update(off_t block_offset, VGMSTREAM* vgmstream);
void blocked_count_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t offset);
void block_update_ast(off_t block_ofset, VGMSTREAM* vgmstream);
void block_update_mxch(off_t block_ofset, VGMSTREAM* vgmstream);
@ -67,7 +69,7 @@ void block_update_wsi(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_str_snds(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_ws_aud(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_dec(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_vs(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_vs_mh(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_mul(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_gsb(off_t block_offset, VGMSTREAM* vgmstream);
void block_update_xvas(off_t block_offset, VGMSTREAM* vgmstream);

View file

@ -3,36 +3,38 @@
#include "../base/decode.h"
#include "../base/mixing.h"
#include "../base/plugins.h"
#include "../base/sbuf.h"
#define VGMSTREAM_MAX_SEGMENTS 1024
#define VGMSTREAM_SEGMENT_SAMPLE_BUFFER 8192
static inline void copy_samples(sample_t* outbuf, segmented_layout_data* data, int current_channels, int32_t samples_to_do, int32_t samples_written);
/* 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) {
int samples_written = 0, samples_this_block;
segmented_layout_data* data = vgmstream->layout_data;
int use_internal_buffer = 0;
int current_channels = 0;
bool use_internal_buffer = false;
/* normally uses outbuf directly (faster?) but could need internal buffer if downmixing */
if (vgmstream->channels != data->input_channels || data->mixed_channels) {
use_internal_buffer = 1;
use_internal_buffer = true;
}
if (data->current_segment >= data->segment_count) {
VGM_LOG_ONCE("SEGMENT: wrong current segment\n");
goto decode_fail;
sbuf_silence(outbuf, sample_count, data->output_channels, 0);
return;
}
samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]);
int current_channels = 0;
mixing_info(data->segments[data->current_segment], NULL, &current_channels);
int samples_this_block = vgmstream_get_samples(data->segments[data->current_segment]);
while (samples_written < sample_count) {
int samples_filled = 0;
while (samples_filled < sample_count) {
int samples_to_do;
sample_t* buf;
if (vgmstream->loop_flag && decode_do_loop(vgmstream)) {
/* handle looping (loop_layout has been called below, changes segments/state) */
@ -61,8 +63,8 @@ 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_written)
samples_to_do = sample_count - samples_written;
if (samples_to_do > sample_count - samples_filled)
samples_to_do = sample_count - samples_filled;
if (samples_to_do > VGMSTREAM_SEGMENT_SAMPLE_BUFFER /*&& use_internal_buffer*/) /* always for fade/etc mixes */
samples_to_do = VGMSTREAM_SEGMENT_SAMPLE_BUFFER;
@ -71,56 +73,30 @@ void render_vgmstream_segmented(sample_t* outbuf, int32_t sample_count, VGMSTREA
goto decode_fail;
}
render_vgmstream(
use_internal_buffer ?
data->buffer : &outbuf[samples_written * data->output_channels],
samples_to_do,
data->segments[data->current_segment]);
buf = use_internal_buffer ? data->buffer : &outbuf[samples_filled * data->output_channels];
render_vgmstream(buf, samples_to_do, data->segments[data->current_segment]);
if (use_internal_buffer) {
copy_samples(outbuf, data, current_channels, samples_to_do, samples_written);
sbuf_copy_samples(outbuf, data->output_channels, data->buffer, current_channels, samples_to_do, samples_filled);
}
samples_written += samples_to_do;
samples_filled += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block += samples_to_do;
}
return;
decode_fail:
memset(outbuf + samples_written * data->output_channels, 0, (sample_count - samples_written) * data->output_channels * sizeof(sample_t));
sbuf_silence(outbuf, sample_count, data->output_channels, samples_filled);
}
static inline void copy_samples(sample_t* outbuf, segmented_layout_data* data, int current_channels, int32_t samples_to_do, int32_t samples_written) {
int ch_out = data->output_channels;
int ch_in = current_channels;
int pos = samples_written * ch_out;
int s;
if (ch_in == ch_out) { /* most common and probably faster */
for (s = 0; s < samples_to_do * ch_out; s++) {
outbuf[pos + s] = data->buffer[s];
}
}
else {
int ch;
for (s = 0; s < samples_to_do; s++) {
for (ch = 0; ch < ch_in; ch++) {
outbuf[pos + s*ch_out + ch] = data->buffer[s*ch_in + ch];
}
for (ch = ch_in; ch < ch_out; ch++) {
outbuf[pos + s*ch_out + ch] = 0;
}
}
}
}
void seek_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample) {
int segment, total_samples;
segmented_layout_data* data = vgmstream->layout_data;
segment = 0;
total_samples = 0;
int segment = 0;
int total_samples = 0;
while (total_samples < vgmstream->num_samples) {
int32_t segment_samples = vgmstream_get_samples(data->segments[segment]);
@ -133,13 +109,13 @@ void seek_layout_segmented(VGMSTREAM* vgmstream, int32_t seek_sample) {
vgmstream->samples_into_block = seek_relative;
break;
}
total_samples += segment_samples;
segment++;
}
if (segment == data->segment_count) {
VGM_LOG("SEGMENTED: can't find seek segment\n");
}
// ???
VGM_ASSERT(segment == data->segment_count, "SEGMENTED: can't find seek segment\n");
}
void loop_layout_segmented(VGMSTREAM* vgmstream, int32_t loop_sample) {
@ -156,35 +132,33 @@ segmented_layout_data* init_layout_segmented(int segment_count) {
data = calloc(1, sizeof(segmented_layout_data));
if (!data) goto fail;
data->segment_count = segment_count;
data->current_segment = 0;
data->segments = calloc(segment_count, sizeof(VGMSTREAM*));
if (!data->segments) goto fail;
data->segment_count = segment_count;
data->current_segment = 0;
return data;
fail:
free_layout_segmented(data);
return NULL;
}
int setup_layout_segmented(segmented_layout_data* data) {
int i, max_input_channels = 0, max_output_channels = 0, mixed_channels = 0;
sample_t *outbuf_re = NULL;
bool setup_layout_segmented(segmented_layout_data* data) {
int max_input_channels = 0, max_output_channels = 0, mixed_channels = 0;
/* setup each VGMSTREAM (roughly equivalent to vgmstream.c's init_vgmstream_internal stuff) */
for (i = 0; i < data->segment_count; i++) {
int segment_input_channels, segment_output_channels;
for (int i = 0; i < data->segment_count; i++) {
if (data->segments[i] == NULL) {
VGM_LOG("SEGMENTED: no vgmstream in segment %i\n", i);
goto fail;
return false;
}
if (data->segments[i]->num_samples <= 0) {
VGM_LOG("SEGMENTED: no samples in segment %i\n", i);
goto fail;
return false;
}
/* allow config if set for fine-tuned parts (usually TXTP only) */
@ -200,8 +174,8 @@ int setup_layout_segmented(segmented_layout_data* data) {
}
}
/* different segments may have different input or output channels, we
* need to know maxs to properly handle */
/* different segments may have different input or output channels (in rare cases of using ex. 2ch + 4ch) */
int segment_input_channels, segment_output_channels;
mixing_info(data->segments[i], &segment_input_channels, &segment_output_channels);
if (max_input_channels < segment_input_channels)
max_input_channels = segment_input_channels;
@ -236,56 +210,52 @@ int setup_layout_segmented(segmented_layout_data* data) {
}
if (max_output_channels > VGMSTREAM_MAX_CHANNELS || max_input_channels > VGMSTREAM_MAX_CHANNELS)
goto fail;
return false;
/* create internal buffer big enough for mixing */
outbuf_re = realloc(data->buffer, VGMSTREAM_SEGMENT_SAMPLE_BUFFER*max_input_channels*sizeof(sample_t));
if (!outbuf_re) goto fail;
data->buffer = outbuf_re;
if (!sbuf_realloc(&data->buffer, VGMSTREAM_SEGMENT_SAMPLE_BUFFER, max_input_channels))
goto fail;
data->input_channels = max_input_channels;
data->output_channels = max_output_channels;
data->mixed_channels = mixed_channels;
return 1;
return true;
fail:
return 0; /* caller is expected to free */
return false; /* caller is expected to free */
}
void free_layout_segmented(segmented_layout_data* data) {
int i, j;
if (!data)
return;
if (data->segments) {
for (i = 0; i < data->segment_count; i++) {
int is_repeat = 0;
for (int i = 0; i < data->segment_count; i++) {
bool is_repeat = false;
/* segments are allowed to be repeated so don't close the same thing twice */
for (j = 0; j < i; j++) {
if (data->segments[i] == data->segments[j])
is_repeat = 1;
/* segments are allowed to be repeated so don't close the same thing twice */
for (int j = 0; j < i; j++) {
if (data->segments[i] == data->segments[j]) {
is_repeat = true;
break;
}
if (is_repeat)
continue;
close_vgmstream(data->segments[i]);
}
free(data->segments);
if (is_repeat)
continue;
close_vgmstream(data->segments[i]);
}
free(data->segments);
free(data->buffer);
free(data);
}
void reset_layout_segmented(segmented_layout_data* data) {
int i;
if (!data)
return;
data->current_segment = 0;
for (i = 0; i < data->segment_count; i++) {
for (int i = 0; i < data->segment_count; i++) {
reset_vgmstream(data->segments[i]);
}
data->current_segment = 0;
}

View file

@ -1,30 +1,29 @@
/* libvgmstream: vgmstream's public API */
#ifndef _LIBVGMSTREAM_H_
#define _LIBVGMSTREAM_H_
//#define LIBVGMSTREAM_ENABLE 1
#if LIBVGMSTREAM_ENABLE
/* By default vgmstream behaves like a decoder (decode samples until stream end), but you can configure
/* libvgmstream: vgmstream's public API
*
* Basic usage (also see api_example.c):
* - libvgmstream_init(...) // create context
* - libvgmstream_setup(...) // setup config (if needed)
* - libvgmstream_open_song(...) // open format
* - libvgmstream_render(...) // main decode
* - output samples + repeat libvgmstream_render until stream is done
* - libvgmstream_free(...) // cleanup
*
* By default vgmstream behaves like a decoder (returns samples until stream end), but you can configure
* it to loop N times or even downmix. In other words, it also behaves a bit like a player.
* It exposes multiple convenience stuff mainly for various plugins that mostly repeat the same features.
* All this may make the API still WIP and a bit twisted, probably will improve later. Probably.
*
* It exposes multiple convenience stuff mainly for various plugins with similar features.
* This may make the API a bit odd, will probably improve later. Probably.
*
* Notes:
* - now there is an API internals (vgmstream.h) may change in the future
* - may dynamically allocate stuff as needed (mainly some buffers, but varies per format)
* - now there is an API, internals (vgmstream.h) may change in the future so avoid accesing them
* - some details described in the API may not happen at the moment (defined for future changes)
* - uses long-winded libvgmstream_* names since internals alredy use the vgmstream_* 'namespace', #define as needed
* - c-strings should be in UTF-8
*
* Basic usage (also see api_example.c):
* - libvgmstream_init(...) // base context
* - libvgmstream_setup(...) // config if needed
* - libvgmstream_open_song(...) // setup format
* - libvgmstream_play(...) // main decode
* - output samples + repeat libvgmstream_play until stream is done
* - libvgmstream_free(...) // cleanup
*/
@ -108,7 +107,9 @@ typedef struct {
int64_t stream_samples; // file's max samples (not final play duration)
int64_t loop_start; // loop start sample
int64_t loop_end; // loop end sample
bool loop_flag; // if file loops (false + defined loops means looping was forcefully disabled)
bool loop_flag; // if file loops
// ** false + defined loops means looping was forcefully disabled
// ** true + undefined loops means the file loops in a way not representable by loop points
bool play_forever; // if file loops forever based on current config (meaning _play never stops)
int64_t play_samples; // totals after all calculations (after applying loop/fade/etc config)
@ -191,8 +192,8 @@ typedef struct {
} libvgmstream_config_t;
/* pass default config, that will be applied to song on open
* - invalid config or complex cases (ex. some TXTP) may ignore these settings.
* - called without a song loaded (before _open or after _close), otherwise ignored.
* - invalid config or complex cases (ex. some TXTP) may ignore these settings
* - should be called without a song loaded (before _open or after _close)
* - without config vgmstream will decode the current stream once
*/
LIBVGMSTREAM_API void libvgmstream_setup(libvgmstream_t* lib, libvgmstream_config_t* cfg);
@ -227,7 +228,7 @@ LIBVGMSTREAM_API void libvgmstream_close_song(libvgmstream_t* lib);
* - vgmstream supplies its own buffer, updated on lib->decoder->* values (may change between calls)
* - returns < 0 on error
*/
LIBVGMSTREAM_API int libvgmstream_play(libvgmstream_t* lib);
LIBVGMSTREAM_API int libvgmstream_render(libvgmstream_t* lib);
/* Same as _play, but fills some external buffer (also updates lib->decoder->* values)
* - returns < 0 on error, or N = number of filled samples.
@ -297,7 +298,7 @@ typedef struct {
/* Returns if vgmstream can parse a filename by extension, to reject some files earlier
* - doesn't check file contents (that's only done on _open)
* - config may be NULL
* - mainly for plugins that fail early; libvgmstream doesn't use this
* - mainly for plugins that want to fail early; libvgmstream doesn't use this
*/
LIBVGMSTREAM_API bool libvgmstream_is_valid(const char* filename, libvgmstream_valid_t* cfg);
@ -321,6 +322,9 @@ LIBVGMSTREAM_API int libvgmstream_get_title(libvgmstream_t* lib, libvgmstream_ti
*/
LIBVGMSTREAM_API int libvgmstream_format_describe(libvgmstream_t* lib, char* dst, int dst_size);
/* Return true if vgmstream detects from the filename that file can be used even if doesn't physically exist.
*/
LIBVGMSTREAM_API bool libvgmstream_is_virtual_filename(const char* filename);
/*****************************************************************************/

View file

@ -319,7 +319,7 @@ static int preload_acb_waveform(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload Waveform=%i\n", *p_rows);
acb->Waveform = malloc(*p_rows * sizeof(Waveform_t));
acb->Waveform = calloc(1, *p_rows * sizeof(Waveform_t));
if (!acb->Waveform) goto fail;
c_Id = utf_get_column(Table, "Id");
@ -410,7 +410,7 @@ static int preload_acb_synth(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload Synth=%i\n", *p_rows);
acb->Synth = malloc(*p_rows * sizeof(Synth_t));
acb->Synth = calloc(1, *p_rows * sizeof(Synth_t));
if (!acb->Synth) goto fail;
c_Type = utf_get_column(Table, "Type");
@ -613,7 +613,7 @@ static int preload_acb_trackcommand(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload TrackEvent/Command=%i\n", *p_rows);
acb->TrackCommand = malloc(*p_rows * sizeof(TrackCommand_t));
acb->TrackCommand = calloc(1, *p_rows * sizeof(TrackCommand_t));
if (!acb->TrackCommand) goto fail;
c_Command = utf_get_column(Table, "Command");
@ -665,7 +665,7 @@ static int preload_acb_track(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload Track=%i\n", *p_rows);
acb->Track = malloc(*p_rows * sizeof(Track_t));
acb->Track = calloc(1, *p_rows * sizeof(Track_t));
if (!acb->Track) goto fail;
c_EventIndex = utf_get_column(Table, "EventIndex");
@ -724,7 +724,7 @@ static int preload_acb_sequence(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload Sequence=%i\n", *p_rows);
acb->Sequence = malloc(*p_rows * sizeof(Sequence_t));
acb->Sequence = calloc(1, *p_rows * sizeof(Sequence_t));
if (!acb->Sequence) goto fail;
c_NumTracks = utf_get_column(Table, "NumTracks");
@ -826,7 +826,7 @@ static int preload_acb_block(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload Block=%i\n", *p_rows);
acb->Block = malloc(*p_rows * sizeof(Block_t));
acb->Block = calloc(1, *p_rows * sizeof(Block_t));
if (!acb->Block) goto fail;
c_NumTracks = utf_get_column(Table, "NumTracks");
@ -891,7 +891,7 @@ static int preload_acb_blocksequence(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload BlockSequence=%i\n", *p_rows);
acb->BlockSequence = malloc(*p_rows * sizeof(BlockSequence_t));
acb->BlockSequence = calloc(1, *p_rows * sizeof(BlockSequence_t));
if (!acb->BlockSequence) goto fail;
c_NumTracks = utf_get_column(Table, "NumTracks");
@ -972,7 +972,7 @@ static int preload_acb_cue(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload Cue=%i\n", *p_rows);
acb->Cue = malloc(*p_rows * sizeof(Cue_t));
acb->Cue = calloc(1, *p_rows * sizeof(Cue_t));
if (!acb->Cue) goto fail;
c_ReferenceType = utf_get_column(Table, "ReferenceType");
@ -1064,7 +1064,7 @@ static int preload_acb_cuename(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload CueName=%i\n", *p_rows);
acb->CueName = malloc(*p_rows * sizeof(CueName_t));
acb->CueName = calloc(1, *p_rows * sizeof(CueName_t));
if (!acb->CueName) goto fail;
c_CueIndex = utf_get_column(Table, "CueIndex");
@ -1118,7 +1118,7 @@ static int preload_acb_waveformextensiondata(acb_header* acb) {
return 1;
//;VGM_LOG("acb: preload WaveformExtensionData=%i\n", *p_rows);
acb->WaveformExtensionData = malloc(*p_rows * sizeof(WaveformExtensionData_t));
acb->WaveformExtensionData = calloc(1, *p_rows * sizeof(WaveformExtensionData_t));
if (!acb->WaveformExtensionData) goto fail;
c_LoopStart = utf_get_column(Table, "LoopStart");

View file

@ -60,7 +60,7 @@ static void close_bar(BARSTREAMFILE *streamFile) {
/*static*/ STREAMFILE *wrap_bar_STREAMFILE(STREAMFILE *file) {
BARSTREAMFILE *streamfile = malloc(sizeof(BARSTREAMFILE));
BARSTREAMFILE *streamfile = calloc(1, sizeof(BARSTREAMFILE));
if (!streamfile)
return NULL;

View file

@ -3,59 +3,60 @@
/* BCSTM - Nintendo 3DS format */
VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_bcstm(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
off_t info_offset = 0, seek_offset = 0, data_offset = 0;
int channel_count, loop_flag, codec;
int is_camelot_ima = 0;
int channels, loop_flag, codec;
bool is_camelot_ima = false;
/* checks */
if ( !check_extensions(streamFile,"bcstm") )
goto fail;
if (!is_id32be(0x00, sf, "CSTM"))
return NULL;
if (!check_extensions(sf,"bcstm"))
return NULL;
/* CSTM header */
if (read_32bitBE(0x00, streamFile) != 0x4353544D) /* "CSTM" */
goto fail;
/* 0x06(2): header size (0x40), 0x08: version (0x00000400), 0x0c: file size (not accurate for Camelot IMA) */
// 04: BOM
// 06: header size (0x40)
// 08: version (0x00000400)
// 0c: file size (not accurate for Camelot IMA)
/* check BOM */
if ((uint16_t)read_16bitLE(0x04, streamFile) != 0xFEFF)
goto fail;
if (read_u16le(0x04, sf) != 0xFEFF)
return NULL;
/* get sections (should always appear in the same order) */
{
int i;
int section_count = read_16bitLE(0x10, streamFile);
for (i = 0; i < section_count; i++) {
/* 0x00: id, 0x02(2): padding, 0x04(4): offset, 0x08(4): size */
uint16_t section_id = read_16bitLE(0x14 + i*0x0c+0x00, streamFile);
switch(section_id) {
case 0x4000: info_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
case 0x4001: seek_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
case 0x4002: data_offset = read_32bitLE(0x14 + i*0x0c+0x04, streamFile); break;
//case 0x4003: /* off_t regn_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ /* ? */
//case 0x4004: /* off_t pdat_offset = read_32bitLE(0x18 + i * 0xc, streamFile); */ /* ? */
default:
break;
}
/* get sections (should always appear in the same order though) */
int section_count = read_u16le(0x10, sf);
for (int i = 0; i < section_count; i++) {
// 00: id
// 02 padding
// 04: offset
// 08: size
uint16_t section_id = read_u16le(0x14 + i * 0x0c + 0x00, sf);
switch(section_id) {
case 0x4000: info_offset = read_u32le(0x14 + i * 0x0c + 0x04, sf); break;
case 0x4001: seek_offset = read_u32le(0x14 + i * 0x0c + 0x04, sf); break;
case 0x4002: data_offset = read_u32le(0x14 + i * 0x0c + 0x04, sf); break;
//case 0x4003: regn_offset = read_u32le(0x18 + i * 0x0c + 0x04, sf); break; // ?
//case 0x4004: pdat_offset = read_u32le(0x18 + i * 0x0c + 0x04, sf); break; // ?
default:
break;
}
}
/* INFO section */
if (read_32bitBE(info_offset, streamFile) != 0x494E464F) /* "INFO" */
goto fail;
codec = read_8bit(info_offset + 0x20, streamFile);
loop_flag = read_8bit(info_offset + 0x21, streamFile);
channel_count = read_8bit(info_offset + 0x22, streamFile);
if (!is_id32be(info_offset, sf, "INFO"))
return NULL;
codec = read_u8(info_offset + 0x20, sf);
loop_flag = read_8bit(info_offset + 0x21, sf);
channels = read_8bit(info_offset + 0x22, sf);
/* Camelot games have some weird codec hijack [Mario Tennis Open (3DS), Mario Golf: World Tour (3DS)] */
if (codec == 0x02 && read_32bitBE(seek_offset, streamFile) != 0x5345454B) { /* "SEEK" */
if (codec == 0x02 && !is_id32be(seek_offset, sf, "SEEK")) {
if (seek_offset == 0) goto fail;
start_offset = seek_offset;
is_camelot_ima = 1;
is_camelot_ima = true;
}
else {
if (data_offset == 0) goto fail;
@ -64,33 +65,24 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) {
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(info_offset + 0x24, streamFile);
vgmstream->num_samples = read_32bitLE(info_offset + 0x2c, streamFile);
vgmstream->loop_start_sample = read_32bitLE(info_offset + 0x28, streamFile);
vgmstream->sample_rate = read_s32le(info_offset + 0x24, sf);
vgmstream->num_samples = read_s32le(info_offset + 0x2c, sf);
vgmstream->loop_start_sample = read_s32le(info_offset + 0x28, sf);
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->meta_type = meta_CSTM;
vgmstream->layout_type = (channel_count == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(info_offset + 0x34, streamFile);
vgmstream->interleave_last_block_size = read_32bitLE(info_offset + 0x44, streamFile);
vgmstream->layout_type = (channels == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = read_u32le(info_offset + 0x34, sf);
vgmstream->interleave_last_block_size = read_u32le(info_offset + 0x44, sf);
/* Camelot doesn't follow header values */
if (is_camelot_ima) {
size_t block_samples, last_samples;
size_t data_size = get_streamfile_size(streamFile) - start_offset;
size_t data_size = get_streamfile_size(sf) - start_offset;
vgmstream->interleave_block_size = 0x200;
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*channel_count)) / channel_count;
/* align the loop points back to avoid pops in some IMA streams, seems to help some Mario Golf songs but not all */
block_samples = ima_bytes_to_samples(vgmstream->interleave_block_size*channel_count,channel_count); // 5000?
last_samples = ima_bytes_to_samples(vgmstream->interleave_last_block_size*channel_count,channel_count);
if (vgmstream->loop_start_sample > block_samples) {
vgmstream->loop_start_sample -= last_samples;
vgmstream->loop_end_sample -= last_samples;
}
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size * channels)) / channels;
}
switch(codec) {
@ -107,16 +99,15 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_NW_IMA;
}
else {
int i, c;
off_t channel_indexes, channel_info_offset, coefs_offset;
channel_indexes = info_offset+0x08 + read_32bitLE(info_offset + 0x1C, streamFile);
for (i = 0; i < vgmstream->channels; i++) {
channel_info_offset = channel_indexes + read_32bitLE(channel_indexes+0x04+(i*0x08)+0x04, streamFile);
coefs_offset = channel_info_offset + read_32bitLE(channel_info_offset+0x04, streamFile);
channel_indexes = info_offset+0x08 + read_u32le(info_offset + 0x1C, sf);
for (int i = 0; i < vgmstream->channels; i++) {
channel_info_offset = channel_indexes + read_u32le(channel_indexes + 0x04 + (i * 0x08) + 0x04, sf);
coefs_offset = channel_info_offset + read_u32le(channel_info_offset + 0x04, sf);
for (c = 0; c < 16; c++) {
vgmstream->ch[i].adpcm_coef[c] = read_16bitLE(coefs_offset + c*2, streamFile);
for (int c = 0; c < 16; c++) {
vgmstream->ch[i].adpcm_coef[c] = read_s16le(coefs_offset + c * 0x02, sf);
}
}
}
@ -126,7 +117,7 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE *streamFile) {
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View file

@ -2,46 +2,48 @@
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../util/endianness.h"
#include "../util/layout_utils.h"
/* EA WVE (Ad10) - from early Electronic Arts movies [Wing Commander 3/4 (PS1), Madden NHL 97 (PC)-w95] */
VGMSTREAM* init_vgmstream_ea_wve_ad10(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels;
int big_endian, is_ps1;
/* checks */
/* .wve: common
* .mov: Madden NHL 97 (also uses .wve) */
if (!check_extensions(sf, "wve,mov"))
goto fail;
start_offset = 0x00;
if (!is_id32be(0x00, sf, "AABB") && /* video block */
!is_id32be(0x00, sf, "Ad10") && /* audio block */
!is_id32be(0x00, sf, "Ad11")) /* last audio block, but could be first */
goto fail;
return NULL;
big_endian = guess_endian32(0x04, sf);
/* .wve: common
* .mov: Madden NHL 97 (also uses .wve) */
if (!check_extensions(sf, "wve,mov"))
return NULL;
if (is_id32be(0x00, sf, "AABB"))
bool big_endian = guess_endian32(0x04, sf);
start_offset = 0x00;
if (is_id32be(0x00, sf, "AABB")){
start_offset += big_endian ? read_u32be(0x04, sf) : read_u32le(0x04, sf);
}
loop_flag = 0;
bool is_ps1;
if (ps_check_format(sf, start_offset + 0x08, 0x40)) {
/* no header = no channels, but seems if the first PS-ADPCM header is 00 then it's mono, somehow
* (ex. Wing Commander 3 intro / Wing Commander 4 = stereo, rest of Wing Commander 3 = mono) */
channels = read_u8(start_offset + 0x08,sf) != 0 ? 2 : 1;
is_ps1 = 1;
VGM_LOG("ps1");
is_ps1 = true;
}
else {
channels = 1;
is_ps1 = 0;
is_ps1 = false;
}
loop_flag = false;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
@ -59,10 +61,14 @@ VGMSTREAM* init_vgmstream_ea_wve_ad10(STREAMFILE* sf) {
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
blocked_count_samples(vgmstream, sf, start_offset);
{
blocked_counter_t cfg = {0};
cfg.offset = start_offset;
blocked_count_samples(vgmstream, sf, &cfg);
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;

View file

@ -216,8 +216,6 @@ VGMSTREAM* init_vgmstream_rwax(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_xwb(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_xa30(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_musc(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_musx(STREAMFILE * streamFile);
@ -269,7 +267,7 @@ VGMSTREAM* init_vgmstream_str_sega_custom(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_dec(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_vs(STREAMFILE *streamFile);
VGMSTREAM* init_vgmstream_vs_mh(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_xmu(STREAMFILE *streamFile);

View file

@ -7,17 +7,14 @@ VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf) {
#ifdef VGM_USE_MPEG
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset;
int loop_flag = 0;
mpeg_frame_info info = {0};
uint32_t header_id;
/* checks */
header_id = read_u32be(0x00, sf);
uint32_t header_id = read_u32be(0x00, sf);
if ((header_id & 0xFFF00000) != 0xFFF00000 &&
(header_id & 0xFFFFFF00) != get_id32be("ID3\0") &&
(header_id & 0xFFFFFF00) != get_id32be("TAG\0"))
goto fail;
return NULL;
/* detect base offset, since some tags with images are big
* (init_mpeg only skips tags in a small-ish buffer) */
@ -29,8 +26,9 @@ VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf) {
start_offset += tag_size;
}
mpeg_frame_info info = {0};
if (!mpeg_get_frame_info(sf, start_offset, &info))
goto fail;
return NULL;
/* .mp3/mp2: standard
* .lmp3/lmp2: for plugins
@ -38,11 +36,12 @@ VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf) {
* .imf: Colors (Gizmondo)
* .aix: Classic Compendium 2 (Gizmondo)
* .wav/lwav: The Seventh Seal (PC)
* .nfx: Grand Theft Auto III (Android)
* (extensionless): Interstellar Flames 2 (Gizmondo) */
if (!check_extensions(sf, "mp3,mp2,lmp3,lmp2,mus,imf,aix,wav,lwav,"))
goto fail;
if (!check_extensions(sf, "mp3,mp2,lmp3,lmp2,mus,imf,aix,wav,lwav,nfx,"))
return NULL;
loop_flag = 0;
bool loop_flag = 0;
/* build VGMSTREAM */

View file

@ -1,55 +0,0 @@
#include "meta.h"
#include "../coding/coding.h"
/* XA30 - found in Driver: Parallel Lines (PS2) */
VGMSTREAM * init_vgmstream_ps2_xa30(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
size_t file_size, data_size;
/* check extension, case insensitive */
/* ".xa30" is just the ID, the real filename inside the file uses .XA */
if (!check_extensions(streamFile,"xa,xa30"))
goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x58413330) /* "XA30" */
goto fail;
if (read_32bitLE(0x04,streamFile) <= 2) goto fail; /* extra check to avoid PS2/PC XA30 mixup */
loop_flag = 0;
channel_count = 1 ; /* 0x08(2): interleave? 0x0a(2): channels? (always 1 in practice) */
start_offset = read_32bitLE(0x0C,streamFile);
file_size = get_streamfile_size(streamFile);
data_size = read_32bitLE(0x14,streamFile); /* always off by 0x800 */
if (data_size-0x0800 != file_size) goto fail;
data_size = file_size - start_offset;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
vgmstream->num_samples = ps_bytes_to_samples(data_size,channel_count); /* 0x10: some num_samples value (but smaller) */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_PS2_XA30;
/* the rest of the header has unknown values (and several repeats) and the filename */
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View file

@ -131,7 +131,7 @@ static void apply_settings(VGMSTREAM* vgmstream, txtp_entry_t* current) {
/* default play config (last after sample rate mods/mixing/etc) */
txtp_copy_config(&vgmstream->config, &current->config);
setup_state_vgmstream(vgmstream);
setup_vgmstream_play_state(vgmstream);
/* config is enabled in layouts or externally (for compatibility, since we don't know yet if this
* VGMSTREAM will part of a layout, or is enabled externally to not mess up plugins's calcs) */
}

View file

@ -2,6 +2,7 @@
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../util/endianness.h"
#include "../util/layout_utils.h"
/* VID1 - Factor 5/DivX format GC/Xbox games [Gun (GC), Tony Hawk's American Wasteland (GC), Enter The Matrix (Xbox)]*/
@ -10,16 +11,16 @@ VGMSTREAM* init_vgmstream_vid1(STREAMFILE* sf) {
uint32_t start_offset, header_offset;
int loop_flag = 0, channels, sample_rate;
uint32_t codec;
int big_endian;
bool big_endian;
read_u32_t read_u32;
/* checks */
if (is_id32be(0x00, sf, "VID1")) { /* BE (GC) */
big_endian = 1;
big_endian = true;
}
else if (is_id32le(0x00,sf, "VID1")) { /* LE (Xbox) */
big_endian = 0;
big_endian = false;
}
else {
return NULL;
@ -110,24 +111,10 @@ VGMSTREAM* init_vgmstream_vid1(STREAMFILE* sf) {
/* calc num_samples as playable data size varies between files/blocks */
if (vgmstream->layout_type == layout_blocked_vid1) {
int block_samples;
blocked_counter_t cfg = {0};
cfg.offset = start_offset;
vgmstream->next_block_offset = start_offset;
do {
block_update(vgmstream->next_block_offset, vgmstream);
if (vgmstream->current_block_samples < 0)
break;
switch(vgmstream->coding_type) {
case coding_PCM16_int: block_samples = pcm_bytes_to_samples(vgmstream->current_block_size, 1, 16); break;
case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break;
default: goto fail;
}
vgmstream->num_samples += block_samples;
}
while (vgmstream->next_block_offset < get_streamfile_size(sf));
block_update(start_offset, vgmstream);
blocked_count_samples(vgmstream, sf, &cfg);
}
return vgmstream;

View file

@ -1,19 +1,31 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "../util/layout_utils.h"
/* .VS - from Melbourne House games [Men in Black II (PS2), Grand Prix Challenge (PS2) */
VGMSTREAM* init_vgmstream_vs(STREAMFILE* sf) {
VGMSTREAM* init_vgmstream_vs_mh(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
int loop_flag, channels;
/* checks */
if (!check_extensions(sf, "vs"))
goto fail;
if (read_u32be(0x00,sf) != 0xC8000000)
goto fail;
return NULL;
if (!check_extensions(sf, "vs"))
return NULL;
/* extra checks since format is too simple */
int sample_rate = read_s32le(0x04,sf);
if (sample_rate != 48000 && sample_rate != 44100)
return NULL;
if (read_u32le(0x08,sf) != 0x1000)
return NULL;
if (!ps_check_format(sf, 0x0c, 0x1000))
return NULL;
loop_flag = 0;
channels = 2;
@ -24,24 +36,19 @@ VGMSTREAM* init_vgmstream_vs(STREAMFILE* sf) {
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_VS;
vgmstream->sample_rate = read_s32le(0x04,sf);
vgmstream->meta_type = meta_VS_MH;
vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_blocked_vs;
vgmstream->layout_type = layout_blocked_vs_mh;
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
/* calc num_samples */
{
vgmstream->next_block_offset = start_offset;
do {
block_update(vgmstream->next_block_offset,vgmstream);
vgmstream->num_samples += ps_bytes_to_samples(vgmstream->current_block_size, 1);
}
while (vgmstream->next_block_offset < get_streamfile_size(sf));
block_update(start_offset, vgmstream);
blocked_counter_t cfg = {0};
cfg.offset = start_offset;
blocked_count_samples(vgmstream, sf, &cfg);
}
return vgmstream;

View file

@ -1,62 +1,99 @@
#include "meta.h"
#include "../coding/coding.h"
/* XA30 - found in Reflections games [Driver: Parallel Lines (PC), Driver 3 (PC)] */
VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
/* XA30 - from Reflections games [Driver: Parallel Lines (PC/PS2), Driver 3 (PC)] */
VGMSTREAM* init_vgmstream_xa_xa30(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
/* checks */
if (!is_id32be(0x00,sf, "XA30") && /* [Driver: Parallel Lines (PC/PS2)] */
!is_id32be(0x00,sf, "e4x\x92")) /* [Driver 3 (PC)] */
return NULL;
/* .XA: actual extension
* .xa30/e4x: header ID */
if (!check_extensions(sf,"xa,xa30,e4x"))
return NULL;
off_t start_offset;
int loop_flag, channel_count, codec, interleave_block_size;
size_t stream_size;
int total_subsongs, target_subsong = streamFile->stream_index;
int loop_flag, channels, interleave, sample_rate;
uint32_t codec, stream_size, file_size;
int total_subsongs, target_subsong = sf->stream_index;
if (read_u32le(0x04,sf) > 2) {
/* PS2 */
sample_rate = read_s32le(0x04,sf);
interleave = read_u16le(0x08, sf); // assumed, always 0x8000
channels = read_u16le(0x0a, sf); // assumed, always 1
start_offset = read_u32le(0x0C,sf);
// 10: some samples value? (smaller than real samples)
stream_size = read_u32le(0x14,sf); // always off by 0x800
// 18: fixed values
// 1c: null
// rest of the header: garbage/repeated values or config (includes stream name)
/* check extension, case insensitive */
/* ".xa30/e4x" is just the ID, the real filename should be .XA */
if (!check_extensions(streamFile,"xa,xa30,e4x"))
goto fail;
if (channels != 1)
return NULL;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x58413330 && /* "XA30" [Driver: Parallel Lines (PC)]*/
read_32bitBE(0x00,streamFile) != 0x65347892) /* "e4x\92" [Driver 3 (PC)]*/
goto fail;
if (read_32bitLE(0x04,streamFile) != 2) /* channels?, also extra check to avoid PS2/PC XA30 mixup */
goto fail;
codec = 0xFF; //fake codec to simplify
total_subsongs = 0;
total_subsongs = read_32bitLE(0x14,streamFile) != 0 ? 2 : 1; /* second stream offset (only in Driver 3) */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
file_size = get_streamfile_size(sf);
if (stream_size - 0x0800 != file_size)
return NULL;
stream_size = file_size - start_offset;
}
else {
/* PC */
total_subsongs = read_u32le(0x14,sf) != 0 ? 2 : 1; /* second stream offset (only in Driver 3) */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
channels = read_s32le(0x04,sf); // assumed, always 2
sample_rate = read_s32le(0x08,sf);
codec = read_u32le(0x0c,sf);
start_offset = read_u32le(0x10 + 0x04 * (target_subsong - 1),sf);
stream_size = read_u32le(0x18 + 0x04 * (target_subsong - 1),sf);
//20: fixed: IMA=00016000, PCM=00056000
interleave = read_u32le(0x24, sf);
// rest of the header is null
if (channels != 2)
return NULL;
}
loop_flag = 0;
channel_count = 2; /* 0x04: channels? (always 2 in practice) */
codec = read_32bitLE(0x0c,streamFile);
start_offset = read_32bitLE(0x10 + 0x04*(target_subsong-1),streamFile);
stream_size = read_32bitLE(0x18 + 0x04*(target_subsong-1),streamFile);
interleave_block_size = read_32bitLE(0x24, streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x08,streamFile);
/* 0x20: always IMA=00016000, PCM=00056000 PCM?, rest of the header is null */
vgmstream->meta_type = meta_XA_XA30;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_XA_XA30;
switch(codec) {
case 0x00: /* PCM (rare, seen in Driver 3) */
case 0x00: /* Driver 3 (PC)-rare */
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave_block_size / 2;
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
vgmstream->interleave_block_size = interleave / 2;
vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, channels);
break;
case 0x01: /* MS-IMA variation */
case 0x01:
vgmstream->coding_type = coding_REF_IMA;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = interleave_block_size;
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, vgmstream->interleave_block_size, channel_count);
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, interleave, channels);
break;
case 0xFF:
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = interleave;
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
break;
default:
@ -64,12 +101,9 @@ VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) {
}
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;

View file

@ -244,7 +244,7 @@ blowfish_ctx* blowfish_init_ecb(uint8_t* key, int32_t key_len) {
uint32_t xl, xr;
uint8_t tmpkey[18*4];
blowfish_ctx* ctx = malloc(sizeof(blowfish_ctx));
blowfish_ctx* ctx = calloc(1, sizeof(blowfish_ctx));
if (!ctx) return NULL;

View file

@ -145,7 +145,7 @@ utf_context* utf_open(STREAMFILE* sf, uint32_t table_offset, int* p_rows, const
utf->table_name = utf->string_table + utf->name_offset;
utf->schema = malloc(utf->columns * sizeof(struct utf_column_t));
utf->schema = calloc(1, utf->columns * sizeof(struct utf_column_t));
if (!utf->schema) goto fail;
for (i = 0; i < utf->columns; i++) {

View file

@ -293,3 +293,44 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
void blocked_count_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, blocked_counter_t* cfg) {
if (vgmstream == NULL)
return;
int block_samples;
off_t max_offset = get_streamfile_size(sf);
vgmstream->next_block_offset = cfg->offset;
do {
block_update(vgmstream->next_block_offset, vgmstream);
if (vgmstream->current_block_samples < 0 || vgmstream->current_block_size == 0xFFFFFFFF)
break;
if (vgmstream->current_block_samples) {
block_samples = vgmstream->current_block_samples;
}
else {
switch(vgmstream->coding_type) {
case coding_PCM16LE:
case coding_PCM16_int: block_samples = pcm16_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_PCM8_int:
case coding_PCM8_U_int: block_samples = pcm8_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_XBOX_IMA_mono:
case coding_XBOX_IMA: block_samples = xbox_ima_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_NGC_DSP: block_samples = dsp_bytes_to_samples(vgmstream->current_block_size, 1); break;
case coding_PSX: block_samples = ps_bytes_to_samples(vgmstream->current_block_size,1); break;
default:
VGM_LOG("BLOCKED: missing codec\n");
return;
}
}
vgmstream->num_samples += block_samples;
}
while (vgmstream->next_block_offset < max_offset);
block_update(cfg->offset, vgmstream); /* reset */
}

View file

@ -22,4 +22,11 @@ bool layered_add_done(VGMSTREAM* vs);
VGMSTREAM* allocate_layered_vgmstream(layered_layout_data* data);
VGMSTREAM* allocate_segmented_vgmstream(segmented_layout_data* data, int loop_flag, int loop_start_segment, int loop_end_segment);
typedef struct {
off_t offset;
} blocked_counter_t;
void blocked_count_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, blocked_counter_t* cfg);
#endif

View file

@ -194,8 +194,15 @@ typedef struct {
bool allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */
int format_id; /* internal format ID */
/* decoder config/state */
int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */
int codec_config; /* flags for codecs or layouts with minor variations; meaning is up to them (may change during decode) */
bool codec_internal_updates; /* temp(?) kludge (see vgmstream_open_stream/decode) */
int32_t ws_output_size; /* WS ADPCM: output bytes for this block */
/* layout/block state */
int32_t current_sample; /* sample point within the file (for loop detection) */
int32_t current_sample; /* sample point within the stream (for loop detection) */
int32_t samples_into_block; /* number of samples into the current block/interleave/segment/etc */
off_t current_block_offset; /* start of this block (offset of block header) */
size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */
@ -203,28 +210,24 @@ typedef struct {
off_t next_block_offset; /* offset of header of the next block */
size_t full_block_size; /* actual data size of an entire block (ie. may be fixed, include padding/headers, etc) */
/* loop state (saved when loop is hit to restore later) */
/* layout/block state copy for loops (saved on loop_start and restored later on loop_end) */
int32_t loop_current_sample; /* saved from current_sample (same as loop_start_sample, but more state-like) */
int32_t loop_samples_into_block;/* saved from samples_into_block */
off_t loop_block_offset; /* saved from current_block_offset */
size_t loop_block_size; /* saved from current_block_size */
int32_t loop_block_samples; /* saved from current_block_samples */
off_t loop_next_block_offset; /* saved from next_block_offset */
size_t loop_full_block_size; /* saved from full_block_size (probably unnecessary) */
bool hit_loop; /* save config when loop is hit, but first time only */
/* decoder config/state */
int codec_endian; /* little/big endian marker; name is left vague but usually means big endian */
int codec_config; /* flags for codecs or layouts with minor variations; meaning is up to them */
bool codec_internal_updates; /* temp(?) kludge (see vgmstream_open_stream/decode) */
int32_t ws_output_size; /* WS ADPCM: output bytes for this block */
/* main state */
VGMSTREAMCHANNEL* ch; /* array of channels */
VGMSTREAMCHANNEL* start_ch; /* shallow copy of channels as they were at the beginning of the stream (for resets) */
VGMSTREAMCHANNEL* ch; /* array of channels with current offset + per-channel codec config */
VGMSTREAMCHANNEL* loop_ch; /* shallow copy of channels as they were at the loop point (for loops) */
void* start_vgmstream; /* shallow copy of the VGMSTREAM as it was at the beginning of the stream (for resets) */
VGMSTREAMCHANNEL* start_ch; /* shallow copy of channels as they were at the beginning of the stream (for resets) */
void* mixer; /* state for mixing effects */
@ -250,7 +253,6 @@ typedef struct {
} VGMSTREAM;
// VGMStream description in structure format
typedef struct {
int sample_rate;
int channels;
@ -334,11 +336,10 @@ void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t ou
void get_vgmstream_layout_description(VGMSTREAM* vgmstream, char* out, size_t out_size);
void get_vgmstream_meta_description(VGMSTREAM* vgmstream, char* out, size_t out_size);
//TODO: remove, unused internally
/* calculate the number of samples to be played based on looping parameters */
int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM* vgmstream);
void setup_state_vgmstream(VGMSTREAM* vgmstream);
/* Force enable/disable internal looping. Should be done before playing anything (or after reset),
* and not all codecs support arbitrary loop values ATM. */
void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sample, int loop_end_sample);
@ -346,4 +347,6 @@ void vgmstream_force_loop(VGMSTREAM* vgmstream, int loop_flag, int loop_start_sa
/* Set number of max loops to do, then play up to stream end (for songs with proper endings) */
void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target);
void setup_vgmstream_play_state(VGMSTREAM* vgmstream);
#endif

View file

@ -66,7 +66,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_fsb5,
init_vgmstream_rwax,
init_vgmstream_xwb,
init_vgmstream_ps2_xa30,
init_vgmstream_musc,
init_vgmstream_musx,
init_vgmstream_filp,
@ -90,7 +89,6 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_str_sega,
init_vgmstream_str_sega_custom,
init_vgmstream_dec,
init_vgmstream_vs,
init_vgmstream_xmu,
init_vgmstream_xvas,
init_vgmstream_sat_sap,
@ -527,6 +525,7 @@ init_vgmstream_t init_vgmstream_functions[] = {
init_vgmstream_rage_aud,
init_vgmstream_asd_naxat,
init_vgmstream_pcm_kceje,
init_vgmstream_vs_mh,
/* need companion files */
init_vgmstream_pos,
init_vgmstream_sli_loops,

View file

@ -208,7 +208,7 @@ typedef enum {
layout_blocked_ws_aud,
layout_blocked_dec,
layout_blocked_xvas,
layout_blocked_vs,
layout_blocked_vs_mh,
layout_blocked_mul,
layout_blocked_gsb,
layout_blocked_thp,
@ -333,7 +333,6 @@ typedef enum {
meta_FSB5, /* FMOD Sample Bank, version 5 */
meta_RWAX,
meta_XWB, /* Microsoft XACT framework (Xbox, X360, Windows) */
meta_PS2_XA30, /* Driver - Parallel Lines (PS2) */
meta_MUSC, /* Krome PS2 games */
meta_MUSX,
meta_FILP, /* Resident Evil - Dead Aim */
@ -428,7 +427,7 @@ typedef enum {
meta_ACM, /* InterPlay ACM header */
meta_MUS_ACM, /* MUS playlist of InterPlay ACM files */
meta_DEC, /* Falcom PC games (Xanadu Next, Gurumin) */
meta_VS, /* Men in Black .vs */
meta_VS_MH,
meta_BGW,
meta_SPW,
meta_STS,

View file

@ -694,6 +694,7 @@
<string>npsf</string>
<string>nsa</string>
<string>nsopus</string>
<string>ntx</string>
<string>nub</string>
<string>nub2</string>
<string>nus3audio</string>