Merge pull request #1577 from bnnm/api-misc7

- Add .ntx extension [GTA3 (Android)]
- Remove Camelot .bcstm loop hack
- cleanup
This commit is contained in:
bnnm 2024-08-18 23:44:40 +02:00 committed by GitHub
commit 0c9088a2a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 1329 additions and 1160 deletions

View File

@ -121,7 +121,7 @@ static int api_example(const char* infile) {
buf_bytes = err * sizeof(short) * lib->format->channels;
}
else {
err = libvgmstream_play(lib);
err = libvgmstream_render(lib);
if (err < 0) goto fail;
buf = lib->decoder->buf;

View File

@ -94,7 +94,7 @@ as explained below, but often will use default values. Accepted codec strings:
# * Variation with modified encoding
# - PCM8_SB PCM 8-bit with sign bit
# * Variation with modified encoding
# * For few rare games [Sonic CD (SCD)]
# * For few rare games [Sonic CD (SCD), Road Avenger (SCD)]
# - PCM24LE PCM 24-bit little endian
# * For few rare games [100% Orange Juice (PC)-sfx]
# * Interleave is multiple of 0x3 (default)

View File

@ -216,14 +216,29 @@ WAV/MP3 conversions ahead of time.
The tag syntax follows the conventions established in Apple's HTTP Live Streaming
standard, whose docs discuss extending M3U with arbitrary tags.
### Related projects
We only manage the above components, but there are other projects using
vgmstream that may useful for other cases. A few of them:
- Web browser player: https://github.com/KatieFrogs/vgmstream-web
- AIMP plugin: https://github.com/ArtemIzmaylov/aimp_vgmstream
- DeaDBeeF plugin: https://github.com/jchv/deadbeef-vgmstream
- Python bindings: https://github.com/hugeBlack/pyvgmstream
- 3DS port: https://github.com/TricksterGuy/3ds-vgmstream
- Reaper plugin: https://github.com/maxton/reaper_vgmstream
- Simple GUI: https://github.com/BENICHN/VGMGUI
They may not be up to date though, and since they aren't part of vgmstream
issues should be directed to each project.
## Special cases
vgmstream aims to support most audio formats as-is, but some files require extra
handling.
### Subsongs
Certain container formats have multiple audio files, usually called "subsongs", often
not meant to be extracted (no simple separation from container).
Certain container formats have multiple audio files, usually called "subsongs",
which usually are not meant to be extracted as single files (can't easily separate
from their container).
By default vgmstream plays first subsong and reports total subsongs, if the format
is able to contain them. Easiest to use would be the *foobar/winamp/Audacious*
@ -233,17 +248,17 @@ With CLI tools, you can select a subsong using the `-s` flag followed by a numbe
for example: `vgmstream-cli -s 5 file.bank` or `vgmstream123 -s 5 file.bank`.
Using *vgmstream-cli* you can convert multiple subsongs at once using the `-S` flag.
**WARNING, MAY TAKE A LOT OF SPACE!** Some files have been observed to contain +20000
**WARNING, MAY TAKE A LOT OF SPACE!** Some containers have been observed to contain +20000
subsongs, so don't use this lightly. Remember to set an output name (`-o`) with subsong
wildcards (or leave it alone for the defaults).
wildcards (or leave it alone for good defaults).
- `vgmstream-cli -s 1 -S 100 file.bank`: writes from subsong 1 to subsong 100
- `vgmstream-cli -s 101 -S 0 file.bank`: writes from subsong 101 to max subsong (automatically changes 0 to max)
- `vgmstream-cli -S 0 file.bank`: writes from subsong 1 to max subsong
- `vgmstream-cli -s 1 -S 5 -o bgm.wav file.bank`: writes 5 subsongs, but all overwrite the same file = wrong.
- `vgmstream-cli -s 1 -S 5 -o bgm_?02s.wav file.bank`: writes 5 subsongs, each named differently = correct.
For other players without support, or to play only a few choice subsongs, you
can create multiple `.txtp` (explained later) to select one, like `bgm.sxd#10.txtp`
For players without subsong support, or to play only a few choice subsongs you can
create multiple `.txtp` (explained later) to select one subsong, like `bgm.sxd#10.txtp`
(plays subsong 10 in `bgm.sxd`).
You can use this python script to autogenerate one `.txtp` per subsong:
@ -693,16 +708,20 @@ order). The format is meant to be both a quick playlist and tags, but the tagfil
itself just 'looks' like an M3U. you can load files manually or using other playlists
and still get tags.
Currently there is no way to simplify adding tags and you need to manually add them,
but format is just a text file. You can use your player to save a playlist in `.m3u`
format sinde the folder with your files, then edit it with any text editor.
Format is:
```
# ignored comment
# comment (ignored)
# $GLOBAL_COMMAND (extra features)
# @GLOBAL_TAG text (applies all following tracks)
# %LOCAL_TAG text (applies to next track only)
filename1
filename1.ext
# %LOCAL_TAG text (applies to next track only)
filename2
filename2.ext
```
Accepted tags depend on the player (foobar: any; Winamp: see ATF config, Audacious:
few standard ones), typically *ALBUM/ARTIST/TITLE/DISC/TRACK/COMPOSER/etc*, lower
@ -727,7 +746,7 @@ Example:
# * Global tags apply to all songs, unless overwritten
# Better use ARTIST instead of ALBUMARTIST (more compatible)
# Tags usually go in CAPS for readability but no differences
#
# $AUTOTRACK
# * This adds TRACK tags automatically from 1 to N
@ -842,10 +861,11 @@ BGM01.adx #I 1.0 90.0 .txtp
### Issues
If your player isn't picking tags make sure vgmstream is detecting the song
(as other plugins can steal its extensions, see above), `.m3u` is properly
named and that filenames inside match the song filename. For Winamp you need
to make sure *options > titles > advanced title formatting* checkbox is set and
the format defined.
and "vgmstream version" or such text shows in the file properties (as other
plugins can steal its extensions, see above), `.m3u` is properly named and
that filenames inside match the song filename. For Winamp you need to make
sure *options > titles > advanced title formatting* checkbox is set and the
format defined.
When tags change behavior varies depending on player:
- *Winamp*: should refresh tags when a different file is played.

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);
}

185
src/base/play_state.c Normal file
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;
}

85
src/base/sbuf.c Normal file
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

@ -219,7 +219,6 @@
<ClCompile Include="base\api_helpers.c" />
<ClCompile Include="base\api_libsf.c" />
<ClCompile Include="base\api_tags.c" />
<ClCompile Include="base\config.c" />
<ClCompile Include="base\decode.c" />
<ClCompile Include="base\info.c" />
<ClCompile Include="base\mixer.c" />
@ -228,8 +227,11 @@
<ClCompile Include="base\mixing.c" />
<ClCompile Include="base\mixing_commands.c" />
<ClCompile Include="base\mixing_macros.c" />
<ClCompile Include="base\play_config.c" />
<ClCompile Include="base\play_state.c" />
<ClCompile Include="base\plugins.c" />
<ClCompile Include="base\render.c" />
<ClCompile Include="base\sbuf.c" />
<ClCompile Include="base\seek.c" />
<ClCompile Include="base\streamfile_api.c" />
<ClCompile Include="base\streamfile_buffer.c" />
@ -630,7 +632,6 @@
<ClCompile Include="meta\ps2_vgv.c" />
<ClCompile Include="meta\ps2_vms.c" />
<ClCompile Include="meta\ps2_wmus.c" />
<ClCompile Include="meta\ps2_xa30.c" />
<ClCompile Include="meta\psb.c" />
<ClCompile Include="meta\psf.c" />
<ClCompile Include="meta\psnd.c" />

View File

@ -487,9 +487,6 @@
<ClCompile Include="base\api_tags.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\config.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\decode.c">
<Filter>base\Source Files</Filter>
</ClCompile>
@ -514,12 +511,21 @@
<ClCompile Include="base\mixing_macros.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\play_config.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\play_state.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\plugins.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\render.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\sbuf.c">
<Filter>base\Source Files</Filter>
</ClCompile>
<ClCompile Include="base\seek.c">
<Filter>base\Source Files</Filter>
</ClCompile>
@ -1720,9 +1726,6 @@
<ClCompile Include="meta\ps2_wmus.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_xa30.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\psb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

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,76 +1,110 @@
#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;
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;
/* 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;
/* 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;
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;
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);
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->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) */
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);
break;
case 0x01: /* MS-IMA variation */
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);
break;
default:
goto fail;
}
/* open the file for reading */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
#include "meta.h"
#include "../coding/coding.h"
/* 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, 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)
if (channels != 1)
return NULL;
codec = 0xFF; //fake codec to simplify
total_subsongs = 0;
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;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_XA_XA30;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
switch(codec) {
case 0x00: /* Driver 3 (PC)-rare */
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave / 2;
vgmstream->num_samples = pcm16_bytes_to_samples(stream_size, channels);
break;
case 0x01:
vgmstream->coding_type = coding_REF_IMA;
vgmstream->layout_type = layout_none;
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:
goto fail;
}
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

@ -186,8 +186,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) */
@ -195,28 +202,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 */
@ -242,7 +245,6 @@ typedef struct {
} VGMSTREAM;
// VGMStream description in structure format
typedef struct {
int sample_rate;
int channels;
@ -326,11 +328,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);
@ -338,4 +339,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

@ -60,29 +60,29 @@ static void ini_get_filename(In_Module* input_module, TCHAR *inifile) {
if (IsWindow(input_module->hMainWindow) && SendMessage(input_module->hMainWindow, WM_WA_IPC,0,IPC_GETVERSION) >= 0x5000) {
/* newer Winamp with per-user settings */
TCHAR *ini_dir = (TCHAR *)SendMessage(input_module->hMainWindow, WM_WA_IPC, 0, IPC_GETINIDIRECTORY);
cfg_strncpy(inifile, ini_dir, PATH_LIMIT);
cfg_strncpy(inifile, ini_dir, WINAMP_PATH_LIMIT);
cfg_strncat(inifile, TEXT("\\Plugins\\"), PATH_LIMIT);
cfg_strncat(inifile, TEXT("\\Plugins\\"), WINAMP_PATH_LIMIT);
/* can't be certain that \Plugins already exists in the user dir */
CreateDirectory(inifile,NULL);
cfg_strncat(inifile, CONFIG_INI_NAME, PATH_LIMIT);
cfg_strncat(inifile, CONFIG_INI_NAME, WINAMP_PATH_LIMIT);
}
else {
/* older winamp with single settings */
TCHAR *lastSlash;
GetModuleFileName(NULL, inifile, PATH_LIMIT);
GetModuleFileName(NULL, inifile, WINAMP_PATH_LIMIT);
lastSlash = cfg_strrchr(inifile, TEXT('\\'));
*(lastSlash + 1) = 0;
/* XMPlay doesn't have a "plugins" subfolder */
if (settings.is_xmplay)
cfg_strncat(inifile, CONFIG_INI_NAME,PATH_LIMIT);
cfg_strncat(inifile, CONFIG_INI_NAME, WINAMP_PATH_LIMIT);
else
cfg_strncat(inifile, TEXT("Plugins\\") CONFIG_INI_NAME,PATH_LIMIT);
cfg_strncat(inifile, TEXT("Plugins\\") CONFIG_INI_NAME, WINAMP_PATH_LIMIT);
/* Maybe should query IPC_GETINIDIRECTORY and use that, not sure what ancient Winamps need.
* There must be some proper way to handle dirs since other Winamp plugins save config in
* XMPlay correctly (this feels like archaeology, try later) */
@ -144,7 +144,7 @@ static void ini_set_b(const char *inifile, const char *entry, int val) {
}
/*static*/ void load_config(In_Module* input_module, winamp_settings_t* settings, winamp_settings_t* defaults) {
TCHAR inifile[PATH_LIMIT];
TCHAR inifile[WINAMP_PATH_LIMIT];
ini_get_filename(input_module, inifile);
@ -176,7 +176,7 @@ static void ini_set_b(const char *inifile, const char *entry, int val) {
}
static void save_config(In_Module* input_module, winamp_settings_t* settings) {
TCHAR inifile[PATH_LIMIT];
TCHAR inifile[WINAMP_PATH_LIMIT];
ini_get_filename(input_module, inifile);

View File

@ -61,14 +61,14 @@ static void wasf_get_name(WINAMP_STREAMFILE* sf, char* buffer, size_t length) {
}
static STREAMFILE* wasf_open(WINAMP_STREAMFILE* sf, const char* const filename, size_t buffersize) {
in_char wpath[PATH_LIMIT];
in_char wpath[WINAMP_PATH_LIMIT];
if (!filename)
return NULL;
/* no need to wfdopen here, may use standard IO */
/* STREAMFILEs carry char/UTF8 names, convert to wchar for Winamp */
wa_char_to_ichar(wpath, PATH_LIMIT, filename);
wa_char_to_ichar(wpath, WINAMP_PATH_LIMIT, filename);
return open_winamp_streamfile_by_ipath(wpath);
}
@ -109,11 +109,11 @@ fail:
STREAMFILE* open_winamp_streamfile_by_ipath(const in_char* wpath) {
FILE* infile = NULL;
STREAMFILE* sf;
char path[PATH_LIMIT];
char path[WINAMP_PATH_LIMIT];
/* convert to UTF-8 if needed for internal use */
wa_ichar_to_char(path,PATH_LIMIT, wpath);
wa_ichar_to_char(path, WINAMP_PATH_LIMIT, wpath);
/* open a FILE from a Winamp (possibly UTF-16) path */
infile = wa_fopen(wpath);

View File

@ -89,9 +89,9 @@ bool split_subsongs(const in_char* filename, int subsong_index, VGMSTREAM* vgmst
/* The only way to pass info around in Winamp is encoding it into the filename, so a fake name
* is created with the index. Then, winamp_Play (and related) intercepts and reads the index. */
for (i = 0; i < vgmstream->num_streams; i++) {
in_char stream_fn[PATH_LIMIT];
in_char stream_fn[WINAMP_PATH_LIMIT];
make_fn_subsong(stream_fn,PATH_LIMIT, filename, (i+1)); /* encode index in filename */
make_fn_subsong(stream_fn, WINAMP_PATH_LIMIT, filename, (i+1)); /* encode index in filename */
/* insert at index */
{

View File

@ -51,15 +51,15 @@ const char* tagfile_name = "!tags.m3u";
HANDLE decode_thread_handle = INVALID_HANDLE_VALUE;
VGMSTREAM* vgmstream = NULL;
in_char lastfn[PATH_LIMIT] = {0}; /* name of the currently playing file */
in_char lastfn[WINAMP_PATH_LIMIT] = {0}; /* name of the currently playing file */
winamp_settings_t defaults;
winamp_settings_t settings;
winamp_state_t state;
short sample_buffer[SAMPLE_BUFFER_SIZE*2 * VGMSTREAM_MAX_CHANNELS]; //todo maybe should be dynamic
short sample_buffer[SAMPLE_BUFFER_SIZE * 2 * VGMSTREAM_MAX_CHANNELS]; //todo maybe should be dynamic
/* info cache (optimization) */
in_char info_fn[PATH_LIMIT] = {0};
in_char info_fn[WINAMP_PATH_LIMIT] = {0};
in_char info_title[GETFILEINFO_TITLE_LENGTH];
int info_time;
int info_valid;
@ -116,11 +116,11 @@ static VGMSTREAM* init_vgmstream_winamp(const in_char* fn, int stream_index) {
/* opens vgmstream with (possibly) an index */
static VGMSTREAM* init_vgmstream_winamp_fileinfo(const in_char* fn) {
in_char filename[PATH_LIMIT];
in_char filename[WINAMP_PATH_LIMIT];
int stream_index = 0;
/* check for info encoded in the filename */
parse_fn_string(fn, NULL, filename,PATH_LIMIT);
parse_fn_string(fn, NULL, filename, WINAMP_PATH_LIMIT);
parse_fn_int(fn, wa_L("$s"), &stream_index);
return init_vgmstream_winamp(filename, stream_index);
@ -141,14 +141,14 @@ static int is_xmplay() {
/* unicode utils */
static void get_title(in_char* dst, int dst_size, const in_char* fn, VGMSTREAM* infostream) {
in_char filename[PATH_LIMIT];
char buffer[PATH_LIMIT];
char filename_utf8[PATH_LIMIT];
in_char filename[WINAMP_PATH_LIMIT];
char buffer[WINAMP_PATH_LIMIT];
char filename_utf8[WINAMP_PATH_LIMIT];
parse_fn_string(fn, NULL, filename,PATH_LIMIT);
parse_fn_string(fn, NULL, filename,WINAMP_PATH_LIMIT);
//parse_fn_int(fn, wa_L("$s"), &stream_index);
wa_ichar_to_char(filename_utf8, PATH_LIMIT, filename);
wa_ichar_to_char(filename_utf8, WINAMP_PATH_LIMIT, filename);
/* infostream gets added at first with index 0, then once played it re-adds proper numbers */
if (infostream) {
@ -187,7 +187,7 @@ static double get_album_gain_volume(const in_char* fn) {
if (settings.gain_type == REPLAYGAIN_NONE)
return 1.0;
//;{ char f8[PATH_LIMIT]; wa_ichar_to_char(f8,PATH_LIMIT,(in_char*)fn); vgm_logi("get_album_gain_volume: file %s\n", f8); }
//;{ char f8[WINAMP_PATH_LIMIT]; wa_ichar_to_char(f8,WINAMP_PATH_LIMIT,(in_char*)fn); vgm_logi("get_album_gain_volume: file %s\n", f8); }
replaygain[0] = '\0'; /* reset each time to make sure we read actual tags */
if (settings.gain_type == REPLAYGAIN_ALBUM
@ -281,7 +281,7 @@ void winamp_Quit() {
int winamp_IsOurFile(const in_char *fn) {
VGMSTREAM* infostream;
vgmstream_ctx_valid_cfg cfg = {0};
char filename_utf8[PATH_LIMIT];
char filename_utf8[WINAMP_PATH_LIMIT];
int valid;
/* Winamp file opening 101:
@ -315,7 +315,7 @@ int winamp_IsOurFile(const in_char *fn) {
cfg.accept_unknown = settings.exts_unknown_on;
cfg.accept_common = settings.exts_common_on;
wa_ichar_to_char(filename_utf8, PATH_LIMIT, fn);
wa_ichar_to_char(filename_utf8, WINAMP_PATH_LIMIT, fn);
//;vgm_logi("winamp_IsOurFile: %s\n", filename_utf8);
@ -324,7 +324,7 @@ int winamp_IsOurFile(const in_char *fn) {
* open/get info from the file (slower so keep some cache) */
info_valid = 0; /* may not be playable */
wa_strncpy(info_fn, fn, PATH_LIMIT); /* copy now for repeat calls */
wa_strncpy(info_fn, fn, WINAMP_PATH_LIMIT); /* copy now for repeat calls */
/* basic extension check */
valid = vgmstream_ctx_is_valid(filename_utf8, &cfg);
@ -369,17 +369,17 @@ int winamp_IsOurFile(const in_char *fn) {
/* request to start playing a file */
int winamp_Play(const in_char *fn) {
int max_latency;
in_char filename[PATH_LIMIT];
in_char filename[WINAMP_PATH_LIMIT];
int stream_index = 0;
//;{ char f8[PATH_LIMIT]; wa_ichar_to_char(f8,PATH_LIMIT,fn); vgm_logi("winamp_Play: file %s\n", f8); }
//;{ char f8[WINAMP_PATH_LIMIT]; wa_ichar_to_char(f8,WINAMP_PATH_LIMIT,fn); vgm_logi("winamp_Play: file %s\n", f8); }
/* shouldn't happen */
if (vgmstream)
return 1;
/* check for info encoded in the filename */
parse_fn_string(fn, NULL, filename,PATH_LIMIT);
parse_fn_string(fn, NULL, filename,WINAMP_PATH_LIMIT);
parse_fn_int(fn, wa_L("$s"), &stream_index);
/* open the stream */
@ -412,7 +412,7 @@ int winamp_Play(const in_char *fn) {
/* save original name */
wa_strncpy(lastfn,fn,PATH_LIMIT);
wa_strncpy(lastfn,fn,WINAMP_PATH_LIMIT);
/* open the output plugin */
max_latency = input_module.outMod->Open(vgmstream->sample_rate, state.output_channels, 16, 0, 0);
@ -530,7 +530,7 @@ int winamp_InfoBox(const in_char *fn, HWND hwnd) {
if (!vgmstream)
return 0;
describe_vgmstream(vgmstream,description,sizeof(description));
describe_vgmstream(vgmstream, description, sizeof(description));
}
else {
/* some other file in playlist given by filename */
@ -544,7 +544,7 @@ int winamp_InfoBox(const in_char *fn, HWND hwnd) {
vgmstream_mixing_autodownmix(infostream, settings.downmix_channels);
vgmstream_mixing_enable(infostream, 0, NULL, NULL);
describe_vgmstream(infostream,description,sizeof(description));
describe_vgmstream(infostream, description, sizeof(description));
close_vgmstream(infostream);
infostream = NULL;
@ -592,7 +592,7 @@ void winamp_GetFileInfo(const in_char *fn, in_char *title, int *length_in_ms) {
}
else {
VGMSTREAM* infostream = NULL;
//;{ char f8[PATH_LIMIT]; wa_ichar_to_char(f8,PATH_LIMIT,fn); vgm_logi("winamp_GetFileInfo: file %s\n", f8); }
//;{ char f8[WINAMP_PATH_LIMIT]; wa_ichar_to_char(f8,WINAMP_PATH_LIMIT,fn); vgm_logi("winamp_GetFileInfo: file %s\n", f8); }
/* not changed from last IsOurFile (most common) */
if (info_valid && wa_strcmp(fn, info_fn) == 0) {
@ -789,13 +789,13 @@ __declspec(dllexport) In_Module * winampGetInModule2() {
/* IN_TAGS */
/* ************************************* */
/* could malloc and stuff but totals aren't much bigger than PATH_LIMITs anyway */
/* could malloc and stuff but totals aren't much bigger than WINAMP_PATH_LIMITs anyway */
#define WINAMP_TAGS_ENTRY_MAX 30
#define WINAMP_TAGS_ENTRY_SIZE 2048
typedef struct {
int loaded;
in_char filename[PATH_LIMIT]; /* tags are loaded for this file */
in_char filename[WINAMP_PATH_LIMIT]; /* tags are loaded for this file */
int tag_count;
char keys[WINAMP_TAGS_ENTRY_MAX][WINAMP_TAGS_ENTRY_SIZE+1];
@ -809,10 +809,10 @@ winamp_tags last_tags;
* Winamp requests one tag at a time and may reask for the same tag several times */
static void load_tagfile_info(in_char* filename) {
STREAMFILE *tagFile = NULL;
in_char filename_clean[PATH_LIMIT];
char filename_utf8[PATH_LIMIT];
char tagfile_path_utf8[PATH_LIMIT];
in_char tagfile_path_i[PATH_LIMIT];
in_char filename_clean[WINAMP_PATH_LIMIT];
char filename_utf8[WINAMP_PATH_LIMIT];
char tagfile_path_utf8[WINAMP_PATH_LIMIT];
in_char tagfile_path_i[WINAMP_PATH_LIMIT];
char *path;
@ -823,7 +823,7 @@ static void load_tagfile_info(in_char* filename) {
}
/* clean extra part for subsong tags */
parse_fn_string(filename, NULL, filename_clean,PATH_LIMIT);
parse_fn_string(filename, NULL, filename_clean,WINAMP_PATH_LIMIT);
if (wa_strcmp(last_tags.filename, filename_clean) == 0) {
return; /* not changed, tags still apply */
@ -832,18 +832,18 @@ static void load_tagfile_info(in_char* filename) {
last_tags.loaded = 0;
/* tags are now for this filename, find tagfile path */
wa_ichar_to_char(filename_utf8, PATH_LIMIT, filename_clean);
wa_ichar_to_char(filename_utf8, WINAMP_PATH_LIMIT, filename_clean);
strcpy(tagfile_path_utf8,filename_utf8);
path = strrchr(tagfile_path_utf8,'\\');
if (path != NULL) {
path[1] = '\0'; /* includes "\", remove after that from tagfile_path */
strcat(tagfile_path_utf8,tagfile_name);
strcat(tagfile_path_utf8, tagfile_name);
}
else { /* ??? */
strcpy(tagfile_path_utf8,tagfile_name);
strcpy(tagfile_path_utf8, tagfile_name);
}
wa_char_to_ichar(tagfile_path_i, PATH_LIMIT, tagfile_path_utf8);
wa_char_to_ichar(tagfile_path_i, WINAMP_PATH_LIMIT, tagfile_path_utf8);
wa_strcpy(last_tags.filename, filename_clean);
last_tags.tag_count = 0;
@ -895,7 +895,7 @@ static int winampGetExtendedFileInfo_common(in_char* filename, char *metadata, c
int i, tag_found;
int max_len;
//;{ char f8[PATH_LIMIT]; wa_ichar_to_char(f8,PATH_LIMIT,filename); vgm_logi("winampGetExtendedFileInfo_common: file %s\n", f8); }
//;{ char f8[WINAMP_PATH_LIMIT]; wa_ichar_to_char(f8,WINAMP_PATH_LIMIT,filename); vgm_logi("winampGetExtendedFileInfo_common: file %s\n", f8); }
/* load list current tags, if necessary */
load_tagfile_info(filename);
@ -952,13 +952,13 @@ fail:
/* for Winamp 5.24 */
__declspec (dllexport) int winampGetExtendedFileInfo(char *filename, char *metadata, char *ret, int retlen) {
in_char filename_wchar[PATH_LIMIT];
in_char filename_wchar[WINAMP_PATH_LIMIT];
int ok;
if (settings.tagfile_disable)
return 0;
wa_char_to_ichar(filename_wchar,PATH_LIMIT, filename);
wa_char_to_ichar(filename_wchar,WINAMP_PATH_LIMIT, filename);
//;{ vgm_logi("winampGetExtendedFileInfo: file %s\n", filename); }
@ -971,16 +971,16 @@ __declspec (dllexport) int winampGetExtendedFileInfo(char *filename, char *metad
/* for Winamp 5.3+ */
__declspec (dllexport) int winampGetExtendedFileInfoW(wchar_t *filename, char *metadata, wchar_t *ret, int retlen) {
in_char filename_ichar[PATH_LIMIT];
in_char filename_ichar[WINAMP_PATH_LIMIT];
char ret_utf8[2048];
int ok;
if (settings.tagfile_disable)
return 0;
wa_wchar_to_ichar(filename_ichar,PATH_LIMIT, filename);
wa_wchar_to_ichar(filename_ichar,WINAMP_PATH_LIMIT, filename);
//;{ char f8[PATH_LIMIT]; wa_ichar_to_char(f8,PATH_LIMIT,filename); vgm_logi("winampGetExtendedFileInfoW: file %s\n", f8); }
//;{ char f8[WINAMP_PATH_LIMIT]; wa_ichar_to_char(f8,WINAMP_PATH_LIMIT,filename); vgm_logi("winampGetExtendedFileInfoW: file %s\n", f8); }
ok = winampGetExtendedFileInfo_common(filename_ichar, metadata, ret_utf8,2048);
if (ok == 0)
@ -1019,7 +1019,7 @@ short xsample_buffer[SAMPLE_BUFFER_SIZE*2 * VGMSTREAM_MAX_CHANNELS];
static void* winampGetExtendedRead_open_common(in_char *fn, int *size, int *bps, int *nch, int *srate) {
VGMSTREAM* xvgmstream = NULL;
//;{ char f8[PATH_LIMIT]; wa_ichar_to_char(f8,PATH_LIMIT,fn); vgm_logi("winampGetExtendedRead_open_common: open common file %s\n", f8); }
//;{ char f8[WINAMP_PATH_LIMIT]; wa_ichar_to_char(f8,WINAMP_PATH_LIMIT,fn); vgm_logi("winampGetExtendedRead_open_common: open common file %s\n", f8); }
/* open the stream */
xvgmstream = init_vgmstream_winamp_fileinfo(fn);
@ -1056,17 +1056,17 @@ static void* winampGetExtendedRead_open_common(in_char *fn, int *size, int *bps,
}
__declspec(dllexport) void* winampGetExtendedRead_open(const char *fn, int *size, int *bps, int *nch, int *srate) {
in_char filename_wchar[PATH_LIMIT];
in_char filename_wchar[WINAMP_PATH_LIMIT];
wa_char_to_ichar(filename_wchar, PATH_LIMIT, fn);
wa_char_to_ichar(filename_wchar, WINAMP_PATH_LIMIT, fn);
return winampGetExtendedRead_open_common(filename_wchar, size, bps, nch, srate);
}
__declspec(dllexport) void* winampGetExtendedRead_openW(const wchar_t *fn, int *size, int *bps, int *nch, int *srate) {
in_char filename_ichar[PATH_LIMIT];
in_char filename_ichar[WINAMP_PATH_LIMIT];
wa_wchar_to_ichar(filename_ichar, PATH_LIMIT, fn);
wa_wchar_to_ichar(filename_ichar, WINAMP_PATH_LIMIT, fn);
return winampGetExtendedRead_open_common(filename_ichar, size, bps, nch, srate);
}

View File

@ -33,6 +33,8 @@
/* IN_CONFIG */
/* ************************************* */
#define WINAMP_PATH_LIMIT 4096
extern In_Module input_module;
extern const int priority_values[7];