mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 01:30:49 +01:00
Add TXTP e/L options to alter loop config, tweak priority/consistency
This commit is contained in:
parent
75f7115e28
commit
cb38467380
@ -72,12 +72,9 @@ typedef struct {
|
||||
char * outfilename;
|
||||
char * tag_filename;
|
||||
int decode_only;
|
||||
int ignore_loop;
|
||||
int force_loop;
|
||||
int really_force_loop;
|
||||
int play_forever;
|
||||
int play_sdtout;
|
||||
int play_wreckless;
|
||||
int play_forever;
|
||||
int print_metaonly;
|
||||
int print_adxencd;
|
||||
int print_oggenc;
|
||||
@ -86,10 +83,15 @@ typedef struct {
|
||||
int write_lwav;
|
||||
int only_stereo;
|
||||
int stream_index;
|
||||
|
||||
double loop_count;
|
||||
double fade_time;
|
||||
double fade_delay;
|
||||
int ignore_fade;
|
||||
int ignore_loop;
|
||||
int force_loop;
|
||||
int really_force_loop;
|
||||
|
||||
int seek_samples;
|
||||
|
||||
/* not quite config but eh */
|
||||
@ -279,36 +281,46 @@ static void print_info(VGMSTREAM * vgmstream, cli_config *cfg) {
|
||||
}
|
||||
}
|
||||
|
||||
static void apply_config(VGMSTREAM * vgmstream, cli_config *cfg) {
|
||||
static void apply_config(VGMSTREAM* vgmstream, cli_config* cfg) {
|
||||
|
||||
/* honor suggested config, if any (defined order matters)
|
||||
* note that ignore_fade and play_forever should take priority */
|
||||
if (vgmstream->config_loop_count > 0.0) {
|
||||
cfg->loop_count = vgmstream->config_loop_count;
|
||||
/* honor suggested config (order matters, and config mixes with/overwrites player defaults) */
|
||||
//if (vgmstream->config.play_forever) { /* not really suited for CLI */
|
||||
// cfg->play_forever = 1;
|
||||
// cfg->ignore_loop = 0;
|
||||
//}
|
||||
if (vgmstream->config.loop_count_set) {
|
||||
cfg->loop_count = vgmstream->config.loop_count;
|
||||
cfg->play_forever = 0;
|
||||
cfg->ignore_loop = 0;
|
||||
}
|
||||
if (vgmstream->config_fade_delay > 0.0) {
|
||||
cfg->fade_delay = vgmstream->config_fade_delay;
|
||||
if (vgmstream->config.fade_delay_set) {
|
||||
cfg->fade_delay = vgmstream->config.fade_delay;
|
||||
}
|
||||
if (vgmstream->config_fade_time > 0.0) {
|
||||
cfg->fade_time = vgmstream->config_fade_time;
|
||||
if (vgmstream->config.fade_time_set) {
|
||||
cfg->fade_time = vgmstream->config.fade_time;
|
||||
}
|
||||
if (vgmstream->config_force_loop) {
|
||||
cfg->really_force_loop = 1;
|
||||
}
|
||||
if (vgmstream->config_ignore_loop) {
|
||||
cfg->ignore_loop = 1;
|
||||
}
|
||||
if (vgmstream->config_ignore_fade) {
|
||||
if (vgmstream->config.ignore_fade) {
|
||||
cfg->ignore_fade = 1;
|
||||
}
|
||||
|
||||
/* remove non-compatible options */
|
||||
if (cfg->play_forever) {
|
||||
cfg->ignore_fade = 0;
|
||||
if (vgmstream->config.force_loop) {
|
||||
cfg->ignore_loop = 0;
|
||||
cfg->force_loop = 1;
|
||||
cfg->really_force_loop = 0;
|
||||
}
|
||||
if (vgmstream->config.really_force_loop) {
|
||||
cfg->ignore_loop = 0;
|
||||
cfg->force_loop = 0;
|
||||
cfg->really_force_loop = 1;
|
||||
}
|
||||
if (vgmstream->config.ignore_loop) {
|
||||
cfg->ignore_loop = 1;
|
||||
cfg->force_loop = 0;
|
||||
cfg->really_force_loop = 0;
|
||||
}
|
||||
|
||||
/* change vgmstream's loop stuff (ignore loop goes last) */
|
||||
|
||||
/* apply config */
|
||||
if (cfg->force_loop && !vgmstream->loop_flag) {
|
||||
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
|
||||
}
|
||||
@ -319,6 +331,14 @@ static void apply_config(VGMSTREAM * vgmstream, cli_config *cfg) {
|
||||
vgmstream_force_loop(vgmstream, 0, 0,0);
|
||||
}
|
||||
|
||||
/* remove non-compatible options */
|
||||
if (!vgmstream->loop_flag) {
|
||||
cfg->play_forever = 0;
|
||||
}
|
||||
if (cfg->play_forever) {
|
||||
cfg->ignore_fade = 0;
|
||||
}
|
||||
|
||||
/* loop N times, but also play stream end instead of fading out */
|
||||
if (cfg->loop_count > 0 && cfg->ignore_fade) {
|
||||
vgmstream_set_loop_target(vgmstream, (int)cfg->loop_count);
|
||||
|
128
doc/TXTP.md
128
doc/TXTP.md
@ -12,15 +12,19 @@ file1
|
||||
...
|
||||
fileN
|
||||
|
||||
mode = (mode) # "segments" is the default if not set
|
||||
mode = (segments|layers|mixed) # "segments" is the default if not set
|
||||
```
|
||||
You can set commands to alter how files play (described later). Having a single file is ok too, so are subdirs:
|
||||
```
|
||||
# set "subsong" command for single file inside subdir
|
||||
sounds/file#12
|
||||
|
||||
# will be ignored as none make sense here and is treated as "single" mode
|
||||
#mode = layers/segments/mixed
|
||||
You can set commands to alter how files play (described later), and having a single file is ok too:
|
||||
```
|
||||
file#12 # set "subsong" command for single file
|
||||
|
||||
#mode is ignored here as there is only one file
|
||||
```
|
||||
|
||||
*files* may be anything accepted by the file system (including spaces and symbols), and you can use subdirs. Separators can be `\` or `/`, but stick to `/` for consistency. Commands may be chained and use spaces as needed (also see "TXTP parsing" section).
|
||||
```
|
||||
sounds/bgm.fsb #s2 #i #for file inside subdir: play subsong 2 + disable looping
|
||||
```
|
||||
|
||||
|
||||
@ -38,7 +42,7 @@ loop_start_segment = 2 # 2nd file start
|
||||
loop_end_segment = 2 # optional, default is last
|
||||
mode = segments # optional, default is segments
|
||||
```
|
||||
You can also set looping to the last segment like this:
|
||||
You can (should) set looping to the last segment like this:
|
||||
```
|
||||
BGM01_BEGIN.VAG
|
||||
BGM01_LOOPED.VAG
|
||||
@ -181,7 +185,7 @@ You can set file commands by adding multiple `#(command)` after the name. `#(spa
|
||||
### Subsong selection for bank formats
|
||||
**`#(number)` or `#s(number)`**: set subsong (number)
|
||||
|
||||
**Super Robot Taisen OG Saga - Masou Kishin III - Pride of Justice (Vita)**: *bgm_12.txtp*
|
||||
**Super Robot Taisen OG Saga: Masou Kishin III - Pride of Justice (Vita)**: *bgm_12.txtp*
|
||||
```
|
||||
# select subsong 12
|
||||
bgm.sxd2#12
|
||||
@ -192,7 +196,7 @@ bgm.sxd2#12
|
||||
```
|
||||
|
||||
### Play segmented subsong ranges as one
|
||||
**`#(number)~(number)` or `#s(number)~(number)`**: set multiple subsong segments at a time, to avoid so much C&P
|
||||
**`#(number)~(number)` or `#s(number)~(number)`**: set multiple subsong segments at a time, to avoid so much C&P.
|
||||
|
||||
**Prince of Persia Sands of Time**: *song_01.txtp*
|
||||
```
|
||||
@ -211,71 +215,73 @@ song#3#h22050
|
||||
```
|
||||
|
||||
|
||||
### Channel mask for channel subsongs/layers
|
||||
**`#c(number)`** (single) or **`#c(number)~(number)`** (range): set number of channels to play. You can add multiple comma-separated numbers, or use ` ` space or `-` as separator and combine multiple ranges with single channels too.
|
||||
### Channel removing/masking for channel subsongs/layers
|
||||
**`C(number)`** (single) or **`#C(number)~(number)`** (range), **`#c(number)`**: set number of channels to play. You can add multiple comma-separated numbers, or use ` ` space or `-` as separator and combine multiple ranges with single channels too.
|
||||
|
||||
If you use **`C(number)`** (uppercase) it will remove non-selected channels. This just a shortcut for macro `#@track` (described later):
|
||||
|
||||
If you use **`c(number)`** (lowercase) it doesn't change the final number of channels, just mutes non-selected channels (for backwards compatibility).
|
||||
|
||||
**Final Fantasy XIII-2**: *music_Home_01.ps3.txtp*
|
||||
```
|
||||
#plays channels 1 and 2 = 1st subsong
|
||||
music_Home.ps3.scd#c1,2
|
||||
music_Home.ps3.scd#C1,2
|
||||
```
|
||||
|
||||
```
|
||||
#plays channels 3 and 4 = 2nd subsong
|
||||
#plays channels 3 and 4 = 2nd subsong (muting 1 and 2)
|
||||
music_Home.ps3.scd#c3 4
|
||||
|
||||
#plays 1 to 3
|
||||
music_Home.ps3.scd#c1~3
|
||||
```
|
||||
Doesn't change the final number of channels though, just mutes non-selected channels.
|
||||
|
||||
If you use **`C(number)`** it will remove non-selected channels (not done directly for backwards compatibility). This just a shortcut for macro `#@track` (described later):
|
||||
```
|
||||
#plays channels 3 and 4 = 2nd subsong and removes other channels
|
||||
music_Home.ps3.scd#C3 4
|
||||
music_Home.ps3.scd#C1~3
|
||||
```
|
||||
|
||||
|
||||
### Play settings
|
||||
**`#l(loops)`**, **`#f(fade)`**, **`#d(fade-delay)`**, **`#i(ignore loop)`**, **`#F(ignore fade)`**, **`#E(end-to-end loop)`**
|
||||
**`#L`**: play forever (if loops are set and player supports it)
|
||||
**`#l(loops)`**: set target number of loops (if file loops)
|
||||
**`#f(fade time)`**: set (in seconds) how long the fade out lasts (if file loops)
|
||||
**`#d(fade delay)`**: set (in seconds) the delay before fade kicks in after last loop (if file loops)
|
||||
**`#F`**: don't fade out after N loops but continue playing the song's original end (if file loops)
|
||||
**`#e`**: set full looping (end-to-end) but only if file doesn't have loop points (mainly for autogenerated .txtp)
|
||||
**`#E`**: force full looping (end-to-end), overriding original loop points
|
||||
**`#i`**: ignore and disable loop points, simply stopping after song's sample count (if file loops)
|
||||
|
||||
They are equivalent to some `test.exe/vgmstream_cli` options. Settings should mix with or override player's defaults. If player has "play forever" setting loops disables it (while other options don't quite apply), while forcing full loops would allow to play forever, or setting ignore looping would disable it.
|
||||
|
||||
Those setting should override player's defaults if set. They are equivalent to some test.exe options.
|
||||
|
||||
**God Hand (PS2)**: *boss2_3ningumi_ver6.txtp*
|
||||
```
|
||||
# set number of loops
|
||||
boss2_3ningumi_ver6.adx#l3
|
||||
boss2_3ningumi_ver6.adx#l3 #default is usually 2.0
|
||||
```
|
||||
```
|
||||
# set fade time (in seconds)
|
||||
boss2_3ningumi_ver6.adx#f10.5
|
||||
boss2_3ningumi_ver6.adx#f11.5 #default is usually 10.0
|
||||
```
|
||||
```
|
||||
# set fade delay (in seconds)
|
||||
boss2_3ningumi_ver6.adx#d0.5
|
||||
boss2_3ningumi_ver6.adx#d0.5 #default is usually 0.0
|
||||
```
|
||||
```
|
||||
# ignore and disable loops
|
||||
boss2_3ningumi_ver6.adx#i
|
||||
boss2_3ningumi_ver6.adx#i #this song has a nice stop
|
||||
```
|
||||
```
|
||||
# don't fade out and instead play the song ending after
|
||||
boss2_3ningumi_ver6.adx#F # this song has a nice stop
|
||||
boss2_3ningumi_ver6.adx#F #loop some then hear that stop
|
||||
```
|
||||
```
|
||||
# force full loops from end-to-end
|
||||
boss2_3ningumi_ver6.adx#E
|
||||
boss2_3ningumi_ver6.adx#e #song has loops, so ignored here
|
||||
```
|
||||
```
|
||||
# settings can be combined
|
||||
boss2_3ningumi_ver6.adx#l2#F # 2 loops + ending
|
||||
boss2_3ningumi_ver6.adx#E #force full loops
|
||||
```
|
||||
```
|
||||
# settings can be combined
|
||||
boss2_3ningumi_ver6.adx#l1.5#d1#f5
|
||||
boss2_3ningumi_ver6.adx#L #keep on loopin'
|
||||
```
|
||||
```
|
||||
# boss2_3ningumi_ver6.adx#l1.0#F # this is equivalent to #i
|
||||
boss2_3ningumi_ver6.adx #l 3 #F #combined: 3 loops + ending
|
||||
```
|
||||
```
|
||||
boss2_3ningumi_ver6.adx #l1.5#d1#f5 #combined: partial loops + some delay + smaller fade
|
||||
```
|
||||
```
|
||||
# boss2_3ningumi_ver6.adx #l1.0 #F #combined: equivalent to #i
|
||||
```
|
||||
|
||||
|
||||
@ -314,9 +320,9 @@ ptp_btl_bgm_voice.sgd#s1#h11050
|
||||
|
||||
|
||||
### Install loops
|
||||
**`#I(loop start time) (loop end time)`**: force/override looping values, same as .pos but nicer. Loop end is optional and defaults to total samples.
|
||||
**`#I(loop start time) [loop end time]`**: force/override looping values, same as .pos but nicer. Loop end is optional and defaults to total samples.
|
||||
|
||||
Time values can be `M:S(.n)` (minutes and seconds), `S.n` (seconds with dot), `0xN` (samples in hex format) or `N` (samples). Beware of the subtle difference between 10.0 (ten seconds) and 10 (ten samples). Wrong loop values (for example loop end being much larger than file's samples) will be ignored, but there is some leeway when using seconds for the loop end.
|
||||
Time values can be `M:S(.n)` (minutes and seconds), `S.n` (seconds, with dot), `0xN` (samples in hex format) or `N` (samples). Beware of the subtle difference between 10.0 (ten seconds) and 10 (ten samples). Wrong loop values (for example loop end being much larger than file's samples) will be ignored, but there is some leeway when using seconds for the loop end.
|
||||
|
||||
**Jewels Ocean (PC)**
|
||||
```
|
||||
@ -470,7 +476,7 @@ BGM_0_012_07.wem
|
||||
mode = layers
|
||||
|
||||
# plays R of BGM_0_012_04 and L of BGM_0_012_07
|
||||
commands = #c2,3
|
||||
commands = #C2,3
|
||||
```
|
||||
|
||||
As it applies at the end, some options with ambiguous or technically hard to handle meanings may be ignored:
|
||||
@ -493,18 +499,21 @@ TXTP may even reference other TXTP, or files that require TXTH, for extra comple
|
||||
|
||||
|
||||
### TXTP parsing
|
||||
*Filenames* may be anything accepted by the file system, including spaces and symbols, and multiple *commands* can be chained:
|
||||
Here is a rough look of how TXTP parses files, so you get a better idea of what's going on if some command fails.
|
||||
|
||||
*Filenames* should accept be anything accepted by the file system, and multiple *commands* can be chained:
|
||||
```
|
||||
bgm bank#s2#c1,2
|
||||
subdir name/bgm bank.fsb#s2#C1,2
|
||||
subdir name/bgm bank.fsb #s2 #C1,2 #comment
|
||||
```
|
||||
|
||||
You may add spaces as needed (but try to keep it simple and don't go overboard), though commands *must* start with `#(command)` (`#(space)(anything)` is a comment). Commands without corresponding file are ignored too (seen as comments), while incorrect commands are ignored and skip to next, though the parser may try to make something usable of them (this may be change anytime without warning):
|
||||
Commands may add spaces as needed, but try to keep it simple and don't go overboard). They *must* start with `#(command)`, as `#(space)(anything)` is a comment. Commands without corresponding file are ignored too (seen as comments), while incorrect commands are ignored and skip to next, though the parser may try to make something usable of them (this may be change anytime without warning):
|
||||
```
|
||||
# those are all equivalent
|
||||
song#s2#c1,2
|
||||
song #s2#c1,2 # comment
|
||||
song #s 2 #c1,2# comment
|
||||
song #s 2 #c 1 , 2# comment
|
||||
song#s2#C1,2
|
||||
song #s2#C1,2 # comment
|
||||
song #s 2 #C1,2# comment
|
||||
song #s 2 #C 1 , 2# comment
|
||||
|
||||
#s2 #ignores rogue commands/comments
|
||||
|
||||
@ -514,17 +523,18 @@ song #E enable
|
||||
song #E 1
|
||||
song #Enable
|
||||
song #h -48000
|
||||
song #l -2.0
|
||||
|
||||
# accepted
|
||||
song #E # comment
|
||||
song #c1, 2, 3
|
||||
song #c 1 2 3
|
||||
song #C1, 2, 3
|
||||
song #C 1 2 3
|
||||
|
||||
# ignores first and reads second
|
||||
song #s TWO#c1,2
|
||||
song #s TWO#C1,2
|
||||
|
||||
# seen as #s1#c1,2
|
||||
song #s 1,2 #c1,2
|
||||
# seen as #s1#C1,2
|
||||
song #s 1,2 #C1,2
|
||||
|
||||
# all seen as #h48000
|
||||
song #h48000
|
||||
@ -535,7 +545,7 @@ song #h 48000mhz
|
||||
song #h hz48000
|
||||
|
||||
# ignored as channels don't go that high (may be modified on request)
|
||||
song #c32,41
|
||||
song #C32,41
|
||||
|
||||
# swaps 1 with 2
|
||||
song #m1-2
|
||||
@ -552,7 +562,7 @@ loop_start_segment = 1 #s2# #commands here are ignored
|
||||
|
||||
song
|
||||
commands=#s2 # commands here are allowed
|
||||
commands= #c1,2
|
||||
commands= #C1,2
|
||||
```
|
||||
|
||||
Repeated commands overwrite previous setting, except comma-separated commands that are additive:
|
||||
|
@ -318,7 +318,6 @@ void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
|
||||
seek_pos_samples = (int) audio_math::time_to_samples(p_seconds, vgmstream->sample_rate);
|
||||
int max_buffer_samples = SAMPLE_BUFFER_SIZE;
|
||||
bool loop_okay = config.song_play_forever && vgmstream->loop_flag && !config.song_ignore_loop && !force_ignore_loop;
|
||||
int loop_skips = 0;
|
||||
|
||||
// possible when disabling looping without refreshing foobar's cached song length
|
||||
// (with infinite looping on p_seconds can't go over seek bar though)
|
||||
@ -326,13 +325,13 @@ void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
|
||||
seek_pos_samples = stream_length_samples;
|
||||
|
||||
int corrected_pos_samples = seek_pos_samples;
|
||||
int loop_length = (vgmstream->loop_end_sample - vgmstream->loop_start_sample);
|
||||
int loop_count = 0;
|
||||
|
||||
// optimize seeks withing loops
|
||||
if (vgmstream->loop_flag && (vgmstream->loop_end_sample - vgmstream->loop_start_sample) && seek_pos_samples >= vgmstream->loop_end_sample) {
|
||||
int loop_length = (vgmstream->loop_end_sample - vgmstream->loop_start_sample);
|
||||
|
||||
if (vgmstream->loop_flag && loop_length > 0 && seek_pos_samples >= vgmstream->loop_end_sample) {
|
||||
corrected_pos_samples -= vgmstream->loop_start_sample;
|
||||
loop_skips = corrected_pos_samples / loop_length;
|
||||
loop_count = corrected_pos_samples / loop_length;
|
||||
corrected_pos_samples %= loop_length;
|
||||
corrected_pos_samples += vgmstream->loop_start_sample;
|
||||
}
|
||||
@ -356,9 +355,9 @@ void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
|
||||
|
||||
while(decode_pos_samples < corrected_pos_samples) {
|
||||
int seek_samples = max_buffer_samples;
|
||||
if((decode_pos_samples + max_buffer_samples >= stream_length_samples) && !loop_okay)
|
||||
if ((decode_pos_samples + max_buffer_samples >= stream_length_samples) && !loop_okay)
|
||||
seek_samples = stream_length_samples - seek_pos_samples;
|
||||
if(decode_pos_samples + max_buffer_samples > seek_pos_samples)
|
||||
if (decode_pos_samples + max_buffer_samples > seek_pos_samples)
|
||||
seek_samples = seek_pos_samples - decode_pos_samples;
|
||||
|
||||
decode_pos_samples += seek_samples;
|
||||
@ -366,7 +365,7 @@ void input_vgmstream::decode_seek(double p_seconds,abort_callback & p_abort) {
|
||||
}
|
||||
|
||||
// seek may have been clamped to skip unneeded loops, adjust as some internals need this value
|
||||
vgmstream->loop_count += loop_skips; //todo evil, make seek_vgmstream
|
||||
vgmstream->loop_count = loop_count; //todo make seek_vgmstream, not ok if seeking in fade section with ignore_fade
|
||||
|
||||
// remove seek loop correction from counter so file ends correctly
|
||||
decode_pos_samples = seek_pos_samples;
|
||||
@ -552,52 +551,74 @@ void input_vgmstream::set_config_defaults(foobar_song_config *current) {
|
||||
current->song_loop_count = loop_count;
|
||||
current->song_fade_time = fade_seconds;
|
||||
current->song_fade_delay = fade_delay_seconds;
|
||||
current->song_ignore_loop = ignore_loop;
|
||||
current->song_really_force_loop = 0;
|
||||
current->song_ignore_fade = 0;
|
||||
current->song_force_loop = 0;
|
||||
current->song_really_force_loop = 0;
|
||||
current->song_ignore_loop = ignore_loop;
|
||||
}
|
||||
|
||||
void input_vgmstream::apply_config(VGMSTREAM * vgmstream, foobar_song_config *current) {
|
||||
void input_vgmstream::apply_config(VGMSTREAM* vgmstream, foobar_song_config* cfg) {
|
||||
|
||||
/* honor suggested config, if any (defined order matters)
|
||||
* note that ignore_fade and play_forever should take priority */
|
||||
if (vgmstream->config_loop_count) {
|
||||
current->song_loop_count = vgmstream->config_loop_count;
|
||||
/* honor suggested config (order matters, and config mixes with/overwrites player defaults) */
|
||||
if (vgmstream->config.play_forever) {
|
||||
cfg->song_play_forever = 1;
|
||||
cfg->song_ignore_loop = 0;
|
||||
}
|
||||
if (vgmstream->config_fade_delay) {
|
||||
current->song_fade_delay = vgmstream->config_fade_delay;
|
||||
if (vgmstream->config.loop_count_set) {
|
||||
cfg->song_loop_count = vgmstream->config.loop_count;
|
||||
cfg->song_play_forever = 0;
|
||||
cfg->song_ignore_loop = 0;
|
||||
}
|
||||
if (vgmstream->config_fade_time) {
|
||||
current->song_fade_time = vgmstream->config_fade_time;
|
||||
if (vgmstream->config.fade_delay_set) {
|
||||
cfg->song_fade_delay = vgmstream->config.fade_delay;
|
||||
}
|
||||
if (vgmstream->config_force_loop) {
|
||||
current->song_really_force_loop = 1;
|
||||
if (vgmstream->config.fade_time_set) {
|
||||
cfg->song_fade_time = vgmstream->config.fade_time;
|
||||
}
|
||||
if (vgmstream->config_ignore_loop) {
|
||||
current->song_ignore_loop = 1;
|
||||
}
|
||||
if (vgmstream->config_ignore_fade) {
|
||||
current->song_ignore_fade = 1;
|
||||
if (vgmstream->config.ignore_fade) {
|
||||
cfg->song_ignore_fade = 1;
|
||||
}
|
||||
|
||||
/* remove non-compatible options */
|
||||
if (current->song_play_forever) {
|
||||
current->song_ignore_fade = 0;
|
||||
current->song_ignore_loop = 0;
|
||||
if (vgmstream->config.force_loop) {
|
||||
cfg->song_ignore_loop = 0;
|
||||
cfg->song_force_loop = 1;
|
||||
cfg->song_really_force_loop = 0;
|
||||
}
|
||||
if (vgmstream->config.really_force_loop) {
|
||||
cfg->song_ignore_loop = 0;
|
||||
cfg->song_force_loop = 0;
|
||||
cfg->song_really_force_loop = 1;
|
||||
}
|
||||
if (vgmstream->config.ignore_loop) {
|
||||
cfg->song_ignore_loop = 1;
|
||||
cfg->song_force_loop = 0;
|
||||
cfg->song_really_force_loop = 0;
|
||||
}
|
||||
|
||||
/* change loop stuff, in no particular order */
|
||||
if (current->song_really_force_loop) {
|
||||
|
||||
/* apply config */
|
||||
if (cfg->song_force_loop && !vgmstream->loop_flag) {
|
||||
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
|
||||
}
|
||||
if (current->song_ignore_loop) {
|
||||
if (cfg->song_really_force_loop) {
|
||||
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
|
||||
}
|
||||
if (cfg->song_ignore_loop) {
|
||||
vgmstream_force_loop(vgmstream, 0, 0,0);
|
||||
}
|
||||
|
||||
/* remove non-compatible options */
|
||||
if (!vgmstream->loop_flag) {
|
||||
cfg->song_play_forever = 0;
|
||||
}
|
||||
if (cfg->song_play_forever) {
|
||||
cfg->song_ignore_fade = 0;
|
||||
}
|
||||
|
||||
/* loop N times, but also play stream end instead of fading out */
|
||||
if (current->song_loop_count > 0 && current->song_ignore_fade) {
|
||||
vgmstream_set_loop_target(vgmstream, (int)current->song_loop_count);
|
||||
current->song_fade_time = 0; /* force no fade */
|
||||
if (cfg->song_loop_count > 0 && cfg->song_ignore_fade) {
|
||||
vgmstream_set_loop_target(vgmstream, (int)cfg->song_loop_count);
|
||||
cfg->song_fade_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ typedef struct {
|
||||
double song_fade_time;
|
||||
double song_fade_delay;
|
||||
int song_ignore_loop;
|
||||
int song_force_loop;
|
||||
int song_really_force_loop;
|
||||
int song_ignore_fade;
|
||||
} foobar_song_config;
|
||||
|
126
src/meta/txtp.c
126
src/meta/txtp.c
@ -72,15 +72,7 @@ typedef struct {
|
||||
int mixing_count;
|
||||
txtp_mix_data mixing[TXTP_MIXING_MAX];
|
||||
|
||||
int config_loop_count_set;
|
||||
double config_loop_count;
|
||||
int config_fade_time_set;
|
||||
double config_fade_time;
|
||||
int config_fade_delay_set;
|
||||
double config_fade_delay;
|
||||
int config_ignore_loop;
|
||||
int config_force_loop;
|
||||
int config_ignore_fade;
|
||||
play_config_t config;
|
||||
|
||||
int sample_rate;
|
||||
|
||||
@ -437,21 +429,37 @@ fail:
|
||||
|
||||
static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
|
||||
|
||||
if (current->config_loop_count_set)
|
||||
vgmstream->config_loop_count = current->config_loop_count;
|
||||
if (current->config_fade_time_set)
|
||||
vgmstream->config_fade_time = current->config_fade_time;
|
||||
if (current->config_fade_delay_set)
|
||||
vgmstream->config_fade_delay = current->config_fade_delay;
|
||||
if (current->config_ignore_loop)
|
||||
vgmstream->config_ignore_loop = current->config_ignore_loop;
|
||||
if (current->config_force_loop)
|
||||
vgmstream->config_force_loop = current->config_force_loop;
|
||||
if (current->config_ignore_fade)
|
||||
vgmstream->config_ignore_fade = current->config_ignore_fade;
|
||||
if (current->config.play_forever) {
|
||||
vgmstream->config.play_forever = current->config.play_forever;
|
||||
}
|
||||
if (current->config.loop_count_set) {
|
||||
vgmstream->config.loop_count_set = 1;
|
||||
vgmstream->config.loop_count = current->config.loop_count;
|
||||
}
|
||||
if (current->config.fade_time_set) {
|
||||
vgmstream->config.fade_time_set = 1;
|
||||
vgmstream->config.fade_time = current->config.fade_time;
|
||||
}
|
||||
if (current->config.fade_delay_set) {
|
||||
vgmstream->config.fade_delay_set = 1;
|
||||
vgmstream->config.fade_delay = current->config.fade_delay;
|
||||
}
|
||||
if (current->config.ignore_fade) {
|
||||
vgmstream->config.ignore_fade = current->config.ignore_fade;
|
||||
}
|
||||
if (current->config.force_loop) {
|
||||
vgmstream->config.force_loop = current->config.force_loop;
|
||||
}
|
||||
if (current->config.really_force_loop) {
|
||||
vgmstream->config.really_force_loop = current->config.really_force_loop;
|
||||
}
|
||||
if (current->config.ignore_loop) {
|
||||
vgmstream->config.ignore_loop = current->config.ignore_loop;
|
||||
}
|
||||
|
||||
if (current->sample_rate > 0)
|
||||
if (current->sample_rate > 0) {
|
||||
vgmstream->sample_rate = current->sample_rate;
|
||||
}
|
||||
|
||||
if (current->loop_install_set) {
|
||||
if (current->loop_start_second > 0 || current->loop_end_second > 0) {
|
||||
@ -899,26 +907,32 @@ static void add_config(txtp_entry* current, txtp_entry* cfg, const char* filenam
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg->config_loop_count_set) {
|
||||
current->config_loop_count_set = cfg->config_loop_count_set;
|
||||
current->config_loop_count = cfg->config_loop_count;
|
||||
if (cfg->config.play_forever) {
|
||||
current->config.play_forever = cfg->config.play_forever;
|
||||
}
|
||||
if (cfg->config_fade_time_set) {
|
||||
current->config_fade_time_set = cfg->config_fade_time_set;
|
||||
current->config_fade_time = cfg->config_fade_time;
|
||||
if (cfg->config.loop_count_set) {
|
||||
current->config.loop_count_set = 1;
|
||||
current->config.loop_count = cfg->config.loop_count;
|
||||
}
|
||||
if (cfg->config_fade_delay_set) {
|
||||
current->config_fade_delay_set = cfg->config_fade_delay_set;
|
||||
current->config_fade_delay = cfg->config_fade_delay;
|
||||
if (cfg->config.fade_time_set) {
|
||||
current->config.fade_time_set = 1;
|
||||
current->config.fade_time = cfg->config.fade_time;
|
||||
}
|
||||
if (cfg->config_ignore_loop) {
|
||||
current->config_ignore_loop = cfg->config_ignore_loop;
|
||||
if (cfg->config.fade_delay_set) {
|
||||
current->config.fade_delay_set = 1;
|
||||
current->config.fade_delay = cfg->config.fade_delay;
|
||||
}
|
||||
if (cfg->config_force_loop) {
|
||||
current->config_force_loop = cfg->config_force_loop;
|
||||
if (cfg->config.ignore_fade) {
|
||||
current->config.ignore_fade = cfg->config.ignore_fade;
|
||||
}
|
||||
if (cfg->config_ignore_fade) {
|
||||
current->config_ignore_fade = cfg->config_ignore_fade;
|
||||
if (cfg->config.force_loop) {
|
||||
current->config.force_loop = cfg->config.force_loop;
|
||||
}
|
||||
if (cfg->config.really_force_loop) {
|
||||
current->config.really_force_loop = cfg->config.really_force_loop;
|
||||
}
|
||||
if (cfg->config.ignore_loop) {
|
||||
current->config.ignore_loop = cfg->config.ignore_loop;
|
||||
}
|
||||
|
||||
if (cfg->sample_rate > 0) {
|
||||
@ -1081,28 +1095,42 @@ static void parse_config(txtp_entry *cfg, char *config) {
|
||||
}
|
||||
}
|
||||
else if (strcmp(command,"i") == 0) {
|
||||
config += get_bool(config, &cfg->config_ignore_loop);
|
||||
//;VGM_LOG("TXTP: ignore_loop=%i\n", cfg->config_ignore_loop);
|
||||
config += get_bool(config, &cfg->config.ignore_loop);
|
||||
//;VGM_LOG("TXTP: ignore_loop=%i\n", cfg->config.ignore_loop);
|
||||
}
|
||||
else if (strcmp(command,"e") == 0) {
|
||||
config += get_bool(config, &cfg->config.force_loop);
|
||||
//;VGM_LOG("TXTP: force_loop=%i\n", cfg->config.force_loop);
|
||||
}
|
||||
else if (strcmp(command,"E") == 0) {
|
||||
config += get_bool(config, &cfg->config_force_loop);
|
||||
//;VGM_LOG("TXTP: force_loop=%i\n", cfg->config_force_loop);
|
||||
config += get_bool(config, &cfg->config.really_force_loop);
|
||||
//;VGM_LOG("TXTP: really_force_loop=%i\n", cfg->config.really_force_loop);
|
||||
}
|
||||
else if (strcmp(command,"F") == 0) {
|
||||
config += get_bool(config, &cfg->config_ignore_fade);
|
||||
//;VGM_LOG("TXTP: ignore_fade=%i\n", cfg->config_ignore_fade);
|
||||
config += get_bool(config, &cfg->config.ignore_fade);
|
||||
//;VGM_LOG("TXTP: ignore_fade=%i\n", cfg->config.ignore_fade);
|
||||
}
|
||||
else if (strcmp(command,"L") == 0) {
|
||||
config += get_bool(config, &cfg->config.play_forever);
|
||||
//;VGM_LOG("TXTP: play_forever=%i\n", cfg->config.play_forever);
|
||||
}
|
||||
else if (strcmp(command,"l") == 0) {
|
||||
config += get_double(config, &cfg->config_loop_count, &cfg->config_loop_count_set);
|
||||
//;VGM_LOG("TXTP: loop_count=%f\n", cfg->config_loop_count);
|
||||
config += get_double(config, &cfg->config.loop_count, &cfg->config.loop_count_set);
|
||||
if (cfg->config.loop_count < 0)
|
||||
cfg->config.loop_count_set = 0;
|
||||
//;VGM_LOG("TXTP: loop_count=%f\n", cfg->config.loop_count);
|
||||
}
|
||||
else if (strcmp(command,"f") == 0) {
|
||||
config += get_double(config, &cfg->config_fade_time, &cfg->config_fade_time_set);
|
||||
//;VGM_LOG("TXTP: fade_time=%f\n", cfg->config_fade_time);
|
||||
config += get_double(config, &cfg->config.fade_time, &cfg->config.fade_time_set);
|
||||
if (cfg->config.fade_time < 0)
|
||||
cfg->config.fade_time_set = 0;
|
||||
//;VGM_LOG("TXTP: fade_time=%f\n", cfg->config.fade_time);
|
||||
}
|
||||
else if (strcmp(command,"d") == 0) {
|
||||
config += get_double(config, &cfg->config_fade_delay, &cfg->config_fade_delay_set);
|
||||
//;VGM_LOG("TXTP: fade_delay %f\n", cfg->config_fade_delay);
|
||||
config += get_double(config, &cfg->config.fade_delay, &cfg->config.fade_delay_set);
|
||||
if (cfg->config.fade_delay < 0)
|
||||
cfg->config.fade_delay_set = 0;
|
||||
//;VGM_LOG("TXTP: fade_delay %f\n", cfg->config.fade_delay);
|
||||
}
|
||||
else if (strcmp(command,"h") == 0) {
|
||||
config += get_int(config, &cfg->sample_rate);
|
||||
|
@ -807,8 +807,8 @@ void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max) {
|
||||
|
||||
/* set loops to hear all track changes */
|
||||
track_num = output_channels / max;
|
||||
if (vgmstream->config_loop_count < track_num)
|
||||
vgmstream->config_loop_count = track_num;
|
||||
if (vgmstream->config.loop_count < track_num)
|
||||
vgmstream->config.loop_count = track_num;
|
||||
|
||||
ch = 0;
|
||||
for (track = 0; track < track_num; track++) {
|
||||
@ -868,8 +868,8 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) {
|
||||
|
||||
/* set loops to hear all track changes */
|
||||
layer_num = output_channels / max;
|
||||
if (vgmstream->config_loop_count < layer_num)
|
||||
vgmstream->config_loop_count = layer_num;
|
||||
if (vgmstream->config.loop_count < layer_num)
|
||||
vgmstream->config.loop_count = layer_num;
|
||||
|
||||
/* mode 'v': constant volume
|
||||
* mode 'e': sets fades to successively lower/equalize volume per loop for each layer
|
||||
|
@ -777,6 +777,19 @@ typedef enum {
|
||||
mapping_7POINT1_surround = speaker_FL | speaker_FR | speaker_FC | speaker_LFE | speaker_BL | speaker_BR | speaker_SL | speaker_SR,
|
||||
} mapping_t;
|
||||
|
||||
typedef struct {
|
||||
int play_forever;
|
||||
int loop_count_set;
|
||||
double loop_count;
|
||||
int fade_time_set;
|
||||
double fade_time;
|
||||
int fade_delay_set;
|
||||
double fade_delay;
|
||||
int ignore_fade;
|
||||
int force_loop;
|
||||
int really_force_loop;
|
||||
int ignore_loop;
|
||||
} play_config_t;
|
||||
|
||||
/* info for a single vgmstream channel */
|
||||
typedef struct {
|
||||
@ -861,14 +874,9 @@ typedef struct {
|
||||
/* other config */
|
||||
int allow_dual_stereo; /* search for dual stereo (file_L.ext + file_R.ext = single stereo file) */
|
||||
|
||||
/* config requests, players must read and honor these values */
|
||||
/* (ideally internally would work as a player, but for now player must do it manually) */
|
||||
double config_loop_count;
|
||||
double config_fade_time;
|
||||
double config_fade_delay;
|
||||
int config_ignore_loop;
|
||||
int config_force_loop;
|
||||
int config_ignore_fade;
|
||||
/* config requests, players must read and honor these values
|
||||
* (ideally internally would work as a player, but for now player must do it manually) */
|
||||
play_config_t config;
|
||||
|
||||
|
||||
/* layout/block state */
|
||||
|
@ -87,9 +87,10 @@ typedef struct {
|
||||
double song_loop_count;
|
||||
double song_fade_time;
|
||||
double song_fade_delay;
|
||||
int song_ignore_loop;
|
||||
int song_really_force_loop;
|
||||
int song_ignore_fade;
|
||||
int song_ignore_loop;
|
||||
int song_force_loop;
|
||||
int song_really_force_loop;
|
||||
} winamp_song_config;
|
||||
|
||||
/* current play state */
|
||||
@ -949,52 +950,74 @@ static void set_config_defaults(winamp_song_config *current) {
|
||||
current->song_loop_count = settings.loop_count;
|
||||
current->song_fade_time = settings.fade_time;
|
||||
current->song_fade_delay = settings.fade_delay;
|
||||
current->song_ignore_loop = settings.ignore_loop;
|
||||
current->song_really_force_loop = 0;
|
||||
current->song_ignore_fade = 0;
|
||||
current->song_force_loop = 0;
|
||||
current->song_really_force_loop = 0;
|
||||
current->song_ignore_loop = settings.ignore_loop;
|
||||
}
|
||||
|
||||
static void apply_config(VGMSTREAM * vgmstream, winamp_song_config *current) {
|
||||
static void apply_config(VGMSTREAM* vgmstream, winamp_song_config* cfg) {
|
||||
|
||||
/* honor suggested config, if any (defined order matters)
|
||||
* note that ignore_fade and play_forever should take priority */
|
||||
if (vgmstream->config_loop_count) {
|
||||
current->song_loop_count = vgmstream->config_loop_count;
|
||||
/* honor suggested config (order matters, and config mixes with/overwrites player defaults) */
|
||||
if (vgmstream->config.play_forever) {
|
||||
cfg->song_play_forever = 1;
|
||||
cfg->song_ignore_loop = 0;
|
||||
}
|
||||
if (vgmstream->config_fade_delay) {
|
||||
current->song_fade_delay = vgmstream->config_fade_delay;
|
||||
if (vgmstream->config.loop_count_set) {
|
||||
cfg->song_loop_count = vgmstream->config.loop_count;
|
||||
cfg->song_play_forever = 0;
|
||||
cfg->song_ignore_loop = 0;
|
||||
}
|
||||
if (vgmstream->config_fade_time) {
|
||||
current->song_fade_time = vgmstream->config_fade_time;
|
||||
if (vgmstream->config.fade_delay_set) {
|
||||
cfg->song_fade_delay = vgmstream->config.fade_delay;
|
||||
}
|
||||
if (vgmstream->config_force_loop) {
|
||||
current->song_really_force_loop = 1;
|
||||
if (vgmstream->config.fade_time_set) {
|
||||
cfg->song_fade_time = vgmstream->config.fade_time;
|
||||
}
|
||||
if (vgmstream->config_ignore_loop) {
|
||||
current->song_ignore_loop = 1;
|
||||
}
|
||||
if (vgmstream->config_ignore_fade) {
|
||||
current->song_ignore_fade = 1;
|
||||
if (vgmstream->config.ignore_fade) {
|
||||
cfg->song_ignore_fade = 1;
|
||||
}
|
||||
|
||||
/* remove non-compatible options */
|
||||
if (current->song_play_forever) {
|
||||
current->song_ignore_fade = 0;
|
||||
current->song_ignore_loop = 0;
|
||||
if (vgmstream->config.force_loop) {
|
||||
cfg->song_ignore_loop = 0;
|
||||
cfg->song_force_loop = 1;
|
||||
cfg->song_really_force_loop = 0;
|
||||
}
|
||||
if (vgmstream->config.really_force_loop) {
|
||||
cfg->song_ignore_loop = 0;
|
||||
cfg->song_force_loop = 0;
|
||||
cfg->song_really_force_loop = 1;
|
||||
}
|
||||
if (vgmstream->config.ignore_loop) {
|
||||
cfg->song_ignore_loop = 1;
|
||||
cfg->song_force_loop = 0;
|
||||
cfg->song_really_force_loop = 0;
|
||||
}
|
||||
|
||||
/* change loop stuff, in no particular order */
|
||||
if (current->song_really_force_loop) {
|
||||
|
||||
/* apply config */
|
||||
if (cfg->song_force_loop && !vgmstream->loop_flag) {
|
||||
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
|
||||
}
|
||||
if (current->song_ignore_loop) {
|
||||
if (cfg->song_really_force_loop) {
|
||||
vgmstream_force_loop(vgmstream, 1, 0,vgmstream->num_samples);
|
||||
}
|
||||
if (cfg->song_ignore_loop) {
|
||||
vgmstream_force_loop(vgmstream, 0, 0,0);
|
||||
}
|
||||
|
||||
/* remove non-compatible options */
|
||||
if (!vgmstream->loop_flag) {
|
||||
cfg->song_play_forever = 0;
|
||||
}
|
||||
if (cfg->song_play_forever) {
|
||||
cfg->song_ignore_fade = 0;
|
||||
}
|
||||
|
||||
/* loop N times, but also play stream end instead of fading out */
|
||||
if (current->song_loop_count > 0 && current->song_ignore_fade) {
|
||||
vgmstream_set_loop_target(vgmstream, (int)current->song_loop_count);
|
||||
current->song_fade_time = 0; /* force no fade */
|
||||
if (cfg->song_loop_count > 0 && cfg->song_ignore_fade) {
|
||||
vgmstream_set_loop_target(vgmstream, (int)cfg->song_loop_count);
|
||||
cfg->song_fade_time = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1378,7 +1401,7 @@ DWORD WINAPI __stdcall decode(void *arg) {
|
||||
int output_bytes;
|
||||
|
||||
if (state.decode_pos_samples + max_buffer_samples > state.stream_length_samples
|
||||
&& (!settings.loop_forever || !vgmstream->loop_flag))
|
||||
&& (!config.song_play_forever || !vgmstream->loop_flag))
|
||||
samples_to_do = state.stream_length_samples - state.decode_pos_samples;
|
||||
else
|
||||
samples_to_do = max_buffer_samples;
|
||||
@ -1396,7 +1419,7 @@ DWORD WINAPI __stdcall decode(void *arg) {
|
||||
|
||||
/* adjust seeking past file, can happen using the right (->) key
|
||||
* (should be done here and not in SetOutputTime due to threads/race conditions) */
|
||||
if (state.seek_needed_samples > max_samples && !settings.loop_forever) {
|
||||
if (state.seek_needed_samples > max_samples && !config.song_play_forever) {
|
||||
state.seek_needed_samples = max_samples;
|
||||
}
|
||||
|
||||
@ -1448,7 +1471,7 @@ DWORD WINAPI __stdcall decode(void *arg) {
|
||||
}
|
||||
|
||||
/* fade near the end */
|
||||
if (vgmstream->loop_flag && state.fade_samples > 0 && !settings.loop_forever) {
|
||||
if (vgmstream->loop_flag && state.fade_samples > 0 && !config.song_play_forever) {
|
||||
int fade_channels = state.output_channels;
|
||||
int samples_into_fade = state.decode_pos_samples - (state.stream_length_samples - state.fade_samples);
|
||||
if (samples_into_fade + samples_to_do > 0) {
|
||||
@ -1836,7 +1859,7 @@ __declspec(dllexport) size_t winampGetExtendedRead_getData(void *handle, char *d
|
||||
int samples_to_do;
|
||||
|
||||
if (xstate.decode_pos_samples + max_buffer_samples > xstate.stream_length_samples
|
||||
&& (!settings.loop_forever || !xvgmstream->loop_flag))
|
||||
&& (!config.song_play_forever || !xvgmstream->loop_flag))
|
||||
samples_to_do = xstate.stream_length_samples - xstate.decode_pos_samples;
|
||||
else
|
||||
samples_to_do = max_buffer_samples;
|
||||
@ -1853,7 +1876,7 @@ __declspec(dllexport) size_t winampGetExtendedRead_getData(void *handle, char *d
|
||||
|
||||
/* adjust seeking past file, can happen using the right (->) key
|
||||
* (should be done here and not in SetOutputTime due to threads/race conditions) */
|
||||
if (xstate.seek_needed_samples > max_samples && !settings.loop_forever) {
|
||||
if (xstate.seek_needed_samples > max_samples && !config.song_play_forever) {
|
||||
xstate.seek_needed_samples = max_samples;
|
||||
}
|
||||
|
||||
@ -1881,7 +1904,7 @@ __declspec(dllexport) size_t winampGetExtendedRead_getData(void *handle, char *d
|
||||
render_vgmstream(xsample_buffer, samples_to_do, xvgmstream);
|
||||
|
||||
/* fade near the end */
|
||||
if (xvgmstream->loop_flag && xstate.fade_samples > 0 && !settings.loop_forever) {
|
||||
if (xvgmstream->loop_flag && xstate.fade_samples > 0 && !config.song_play_forever) {
|
||||
int fade_channels = xstate.output_channels;
|
||||
int samples_into_fade = xstate.decode_pos_samples - (xstate.stream_length_samples - xstate.fade_samples);
|
||||
if (samples_into_fade + xstate.decode_pos_samples > 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user