Add TXTP e/L options to alter loop config, tweak priority/consistency

This commit is contained in:
bnnm 2020-06-21 00:33:21 +02:00
parent 75f7115e28
commit cb38467380
8 changed files with 327 additions and 216 deletions

View File

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

View File

@ -12,17 +12,21 @@ 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
```
### Segments mode
Some games clumsily loop audio by using multiple full file "segments", so you can play separate intro + loop files together as a single track.
@ -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:

View File

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

View File

@ -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;

View File

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

View File

@ -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

View File

@ -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 */

View File

@ -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) {