mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 16:30:54 +01:00
commit
77cc431be7
24
doc/TXTP.md
24
doc/TXTP.md
@ -332,6 +332,7 @@ music_Home.ps3.scd#C1~3
|
||||
- **`#b(time)`**: set target time (even without loops) for the body part (modified by other values)
|
||||
- **`#f(fade period)`**: set (in seconds) how long the fade out lasts after target number of loops (if file loops)
|
||||
- **`#d(fade delay)`**: set (in seconds) delay before fade out kicks in (if file loops)
|
||||
- **`#B(time)`**: same as `#b`, but implies no `#f`/`#d` (for easier exact times).
|
||||
- **`#p(time-begin)`**: pad song beginning (not between loops)
|
||||
- **`#P(time-end)`**: pad song song end (not between loops)
|
||||
- **`#r(time-begin)`**: remove/trim song beginning (not between loops)
|
||||
@ -505,9 +506,25 @@ bgm03.adx
|
||||
bgm04.adx #A ##defines loop end
|
||||
bgm05.adx
|
||||
```
|
||||
You can also use `#@loop` to set loop start.
|
||||
You can also use `#@loop` and `#@loop-end` aliases.
|
||||
|
||||
This setting also works in groups, which allows loops when using multiple segmented groups (not possible with `loop_start/end_segment`).
|
||||
Anchors can be applied to groups too.
|
||||
```
|
||||
bgm01a.adx
|
||||
bgm01b.adx
|
||||
group -L2
|
||||
bgm02a.adx
|
||||
bgm02b.adx
|
||||
group -L2 #a ##loops here
|
||||
group -S2
|
||||
```
|
||||
```
|
||||
bgm01.adx
|
||||
bgm02.adx
|
||||
group = -L2 #a ##similar to loop_start_segment=1 or #E
|
||||
```
|
||||
|
||||
This setting also works inside groups, which allows internal loops when using multiple segmented layouts (not possible with `loop_start/end_segment`).
|
||||
```
|
||||
bgm01.adx
|
||||
bgm02.adx #a
|
||||
@ -517,10 +534,9 @@ This setting also works in groups, which allows loops when using multiple segmen
|
||||
bgm03.adx
|
||||
group -S2 #l 3.0
|
||||
group -S2
|
||||
#could use R groups to select one sub-groups that loops
|
||||
# could even use R group to select one sub-groups that loops
|
||||
# (loop_start_segment doesn't make sense for both segments)
|
||||
```
|
||||
Loop anchors have priority over `loop_start_segment`, and are ignored in layered layouts.
|
||||
|
||||
|
||||
### Force sample rate
|
||||
|
@ -25,7 +25,8 @@ static const char* extension_list[] = {
|
||||
"208",
|
||||
"2dx9",
|
||||
"2pfs",
|
||||
"4", // for Game.com audio
|
||||
"3do",
|
||||
"4", //for Game.com audio
|
||||
"8", //txth/reserved [Gungage (PS1)]
|
||||
"800",
|
||||
"9tav",
|
||||
@ -943,7 +944,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_DSP_WSI, "Alone in the Dark .WSI header"},
|
||||
{meta_AIFC, "Apple AIFF-C (Audio Interchange File Format) header"},
|
||||
{meta_AIFF, "Apple AIFF (Audio Interchange File Format) header"},
|
||||
{meta_STR_SNDS, "3DO .str header"},
|
||||
{meta_STR_SNDS, "3DO SNDS header"},
|
||||
{meta_WS_AUD, "Westwood Studios .aud header"},
|
||||
{meta_WS_AUD_old, "Westwood Studios .aud (old) header"},
|
||||
{meta_PS2_IVB, "IVB/BVII header"},
|
||||
|
@ -376,6 +376,12 @@ static const hcakey_info hcakey_list[] = {
|
||||
/* D4DJ Groovy Mix (Android) [base files] */
|
||||
{393410674916959300}, // 0575ACECA945A444
|
||||
|
||||
/* Toji no Miko: Kizamishi Issen no Tomoshibi (Android) */
|
||||
{62057514034227932}, // 00DC78FAEFA76ADC
|
||||
|
||||
/* Readyyy! (Android) */
|
||||
{1234567890987654321}, // 112210F4B16C1CB1
|
||||
|
||||
/* Dragalia Lost (iOS/Android) */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
|
@ -1,114 +1,119 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
|
||||
/* .str - 3DO format with CTRL/SNDS/SHDR blocks [Icebreaker (3DO), Battle Pinball (3DO)] */
|
||||
VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, shdr_offset = -1;
|
||||
int loop_flag, channel_count, found_shdr = 0;
|
||||
size_t file_size, ctrl_size = -1;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "str"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4354524c && /* "CTRL" */
|
||||
read_32bitBE(0x00,streamFile) != 0x534e4453 && /* "SNDS" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53484452) /* "SHDR" */
|
||||
goto fail;
|
||||
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
start_offset = 0x00;
|
||||
|
||||
/* scan chunks until we find a SNDS containing a SHDR */
|
||||
{
|
||||
off_t current_chunk = 0;
|
||||
|
||||
while (!found_shdr && current_chunk < file_size) {
|
||||
if (current_chunk < 0) goto fail;
|
||||
|
||||
if (current_chunk+read_32bitBE(current_chunk+0x04,streamFile) >= file_size)
|
||||
goto fail;
|
||||
|
||||
switch (read_32bitBE(current_chunk,streamFile)) {
|
||||
case 0x4354524C: /* "CTRL" */
|
||||
ctrl_size = read_32bitBE(current_chunk+4,streamFile);
|
||||
break;
|
||||
|
||||
case 0x534e4453: /* "SNDS" */
|
||||
switch (read_32bitBE(current_chunk+16,streamFile)) {
|
||||
case 0x53484452: /* SHDR */
|
||||
found_shdr = 1;
|
||||
shdr_offset = current_chunk+16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x53484452: /* "SHDR" */
|
||||
switch (read_32bitBE(current_chunk+0x7C, streamFile)) {
|
||||
case 0x4354524C: /* "CTRL" */
|
||||
/* to distinguish between styles */
|
||||
ctrl_size = read_32bitBE(current_chunk + 0x80, streamFile);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
/* ignore others for now */
|
||||
break;
|
||||
}
|
||||
|
||||
current_chunk += read_32bitBE(current_chunk+0x04,streamFile);
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_shdr) goto fail;
|
||||
|
||||
channel_count = read_32bitBE(shdr_offset+0x20,streamFile);
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_STR_SNDS;
|
||||
vgmstream->sample_rate = read_32bitBE(shdr_offset+0x1c,streamFile);
|
||||
|
||||
if (ctrl_size == 0x1C || ctrl_size == 0x0B || ctrl_size == -1) {
|
||||
vgmstream->num_samples = read_32bitBE(shdr_offset+0x2c,streamFile) - 1; /* sample count? */
|
||||
}
|
||||
else {
|
||||
vgmstream->num_samples = read_32bitBE(shdr_offset+0x2c,streamFile) * 0x10; /* frame count? */
|
||||
}
|
||||
vgmstream->num_samples /= vgmstream->channels;
|
||||
|
||||
switch (read_32bitBE(shdr_offset+0x24,streamFile)) {
|
||||
case 0x53445832: /* "SDX2" */
|
||||
if (channel_count > 1) {
|
||||
vgmstream->coding_type = coding_SDX2_int;
|
||||
vgmstream->interleave_block_size = 1;
|
||||
} else
|
||||
vgmstream->coding_type = coding_SDX2;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_blocked_str_snds;
|
||||
|
||||
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"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
|
||||
/* .str - 3DO format with CTRL/SNDS/SHDR blocks [Icebreaker (3DO), Battle Pinball (3DO)] */
|
||||
VGMSTREAM* init_vgmstream_str_snds(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, shdr_offset = -1;
|
||||
int loop_flag, channels, found_shdr = 0;
|
||||
size_t file_size, ctrl_size = -1;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .str: standard
|
||||
* .3do: Aqua World - Umimi Monogatari (3DO) movies */
|
||||
if (!check_extensions(sf, "str,3do"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00,sf) != 0x4354524c && /* "CTRL" */
|
||||
read_u32be(0x00,sf) != 0x534e4453 && /* "SNDS" */
|
||||
read_u32be(0x00,sf) != 0x53484452) /* "SHDR" */
|
||||
goto fail;
|
||||
|
||||
file_size = get_streamfile_size(sf);
|
||||
start_offset = 0x00;
|
||||
|
||||
/* scan chunks until we find a SNDS containing a SHDR */
|
||||
{
|
||||
off_t offset = 0;
|
||||
uint32_t size;
|
||||
|
||||
while (!found_shdr && offset < file_size) {
|
||||
if (offset < 0) goto fail;
|
||||
|
||||
size = read_u32be(offset + 0x04,sf);
|
||||
if (offset + size >= file_size)
|
||||
goto fail;
|
||||
|
||||
switch (read_u32be(offset + 0x00,sf)) {
|
||||
case 0x4354524C: /* "CTRL" */
|
||||
ctrl_size = read_u32be(offset + 0x04,sf);
|
||||
break;
|
||||
|
||||
case 0x534e4453: /* "SNDS" */
|
||||
switch (read_u32be(offset + 0x10,sf)) {
|
||||
case 0x53484452: /* SHDR */
|
||||
found_shdr = 1;
|
||||
shdr_offset = offset + 0x10;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x53484452: /* "SHDR" */
|
||||
switch (read_u32be(offset + 0x7C, sf)) {
|
||||
case 0x4354524C: /* "CTRL" */
|
||||
/* to distinguish between styles */
|
||||
ctrl_size = read_u32be(offset + 0x80, sf);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* ignore others */
|
||||
break;
|
||||
}
|
||||
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_shdr)
|
||||
goto fail;
|
||||
|
||||
channels = read_u32be(shdr_offset+0x20,sf);
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_STR_SNDS;
|
||||
vgmstream->sample_rate = read_u32be(shdr_offset+0x1c,sf);
|
||||
|
||||
if (ctrl_size == 0x1C || ctrl_size == 0x0B || ctrl_size == -1) {
|
||||
vgmstream->num_samples = read_u32be(shdr_offset+0x2c,sf) - 1; /* sample count? */
|
||||
}
|
||||
else {
|
||||
vgmstream->num_samples = read_u32be(shdr_offset+0x2c,sf) * 0x10; /* frame count? */
|
||||
}
|
||||
vgmstream->num_samples /= vgmstream->channels;
|
||||
|
||||
switch (read_u32be(shdr_offset + 0x24,sf)) {
|
||||
case 0x53445832: /* "SDX2" */
|
||||
if (channels > 1) {
|
||||
vgmstream->coding_type = coding_SDX2_int;
|
||||
vgmstream->interleave_block_size = 1;
|
||||
} else {
|
||||
vgmstream->coding_type = coding_SDX2;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
vgmstream->layout_type = layout_blocked_str_snds;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
120
src/meta/txtp.c
120
src/meta/txtp.c
@ -107,7 +107,7 @@ typedef struct {
|
||||
char repeat;
|
||||
int selected;
|
||||
|
||||
txtp_entry group_settings;
|
||||
txtp_entry entry;
|
||||
|
||||
} txtp_group;
|
||||
|
||||
@ -334,10 +334,11 @@ static int find_loop_anchors(txtp_header* txtp, int position, int count, int* p_
|
||||
//;VGM_LOG("TXTP: find loop anchors from %i to %i\n", position, count);
|
||||
|
||||
for (i = position, j = 0; i < position + count; i++, j++) {
|
||||
if (txtp->entry[i].loop_anchor_start) {
|
||||
loop_start = j + 1; /* logic elsewhere also uses +1 */
|
||||
/* catch first time anchors appear only, also logic elsewhere also uses +1 */
|
||||
if (txtp->entry[i].loop_anchor_start && !loop_start) {
|
||||
loop_start = j + 1;
|
||||
}
|
||||
if (txtp->entry[i].loop_anchor_end) {
|
||||
if (txtp->entry[i].loop_anchor_end && !loop_end) {
|
||||
loop_end = j + 1;
|
||||
}
|
||||
}
|
||||
@ -354,7 +355,8 @@ static int find_loop_anchors(txtp_header* txtp, int position, int count, int* p_
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_group_segment(txtp_header* txtp, int is_group, int position, int count) {
|
||||
|
||||
static int make_group_segment(txtp_header* txtp, txtp_group* grp, int position, int count) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
segmented_layout_data *data_s = NULL;
|
||||
int i, loop_flag = 0;
|
||||
@ -362,7 +364,7 @@ static int make_group_segment(txtp_header* txtp, int is_group, int position, int
|
||||
|
||||
|
||||
/* allowed for actual groups (not final "mode"), otherwise skip to optimize */
|
||||
if (!is_group && count == 1) {
|
||||
if (!grp && count == 1) {
|
||||
//;VGM_LOG("TXTP: ignored single group\n");
|
||||
return 1;
|
||||
}
|
||||
@ -440,6 +442,13 @@ static int make_group_segment(txtp_header* txtp, int is_group, int position, int
|
||||
/* set new vgmstream and reorder positions */
|
||||
update_vgmstream_list(vgmstream, txtp, position, count);
|
||||
|
||||
|
||||
/* special "whole loop" settings */
|
||||
if (grp->entry.loop_anchor_start == 1) {
|
||||
grp->entry.config.config_set = 1;
|
||||
grp->entry.config.really_force_loop = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
@ -448,14 +457,14 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_group_layer(txtp_header* txtp, int is_group, int position, int count) {
|
||||
static int make_group_layer(txtp_header* txtp, txtp_group* grp, int position, int count) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
layered_layout_data* data_l = NULL;
|
||||
int i;
|
||||
|
||||
|
||||
/* allowed for actual groups (not final mode), otherwise skip to optimize */
|
||||
if (!is_group && count == 1) {
|
||||
if (!grp && count == 1) {
|
||||
//;VGM_LOG("TXTP: ignored single group\n");
|
||||
return 1;
|
||||
}
|
||||
@ -492,16 +501,17 @@ static int make_group_layer(txtp_header* txtp, int is_group, int position, int c
|
||||
}
|
||||
}
|
||||
|
||||
/* loop settings only make sense if this group becomes final vgmstream */
|
||||
if (position == 0 && txtp->vgmstream_count == count) {
|
||||
if (txtp->is_loop_auto && !vgmstream->loop_flag) {
|
||||
vgmstream_force_loop(vgmstream, 1, 0, vgmstream->num_samples);
|
||||
}
|
||||
}
|
||||
|
||||
/* set new vgmstream and reorder positions */
|
||||
update_vgmstream_list(vgmstream, txtp, position, count);
|
||||
|
||||
|
||||
/* special "whole loop" settings (also loop if this group becomes final vgmstream) */
|
||||
if (grp->entry.loop_anchor_start == 1
|
||||
|| (position == 0 && txtp->vgmstream_count == count && txtp->is_loop_auto)) {
|
||||
grp->entry.config.config_set = 1;
|
||||
grp->entry.config.really_force_loop = 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
@ -510,12 +520,12 @@ fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int make_group_random(txtp_header* txtp, int is_group, int position, int count, int selected) {
|
||||
static int make_group_random(txtp_header* txtp, txtp_group* grp, int position, int count, int selected) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
int i;
|
||||
|
||||
/* allowed for actual groups (not final mode), otherwise skip to optimize */
|
||||
if (!is_group && count == 1) {
|
||||
if (!grp && count == 1) {
|
||||
//;VGM_LOG("TXTP: ignored single group\n");
|
||||
return 1;
|
||||
}
|
||||
@ -525,11 +535,6 @@ static int make_group_random(txtp_header* txtp, int is_group, int position, int
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* special case meaning "play all", basically for quick testing */
|
||||
if (selected == count) {
|
||||
return make_group_segment(txtp, is_group, position, count);
|
||||
}
|
||||
|
||||
/* 0=actually random for fun and testing, but undocumented since random music is kinda weird, may change anytime
|
||||
* (plus foobar caches song duration unless .txtp is modifies, so it can get strange if randoms are too different) */
|
||||
if (selected < 0) {
|
||||
@ -539,19 +544,43 @@ static int make_group_random(txtp_header* txtp, int is_group, int position, int
|
||||
//;VGM_LOG("TXTP: autoselected random %i\n", selected);
|
||||
}
|
||||
|
||||
if (selected < 0 || selected >= count) {
|
||||
if (selected < 0 || selected > count) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* get selected and remove non-selected */
|
||||
vgmstream = txtp->vgmstream[position + selected];
|
||||
txtp->vgmstream[position + selected] = NULL;
|
||||
for (i = 0; i < count; i++) {
|
||||
close_vgmstream(txtp->vgmstream[i + position]);
|
||||
if (selected == count) {
|
||||
/* special case meaning "select all", basically for quick testing and clearer Wwise */
|
||||
if (!make_group_segment(txtp, grp, position, count))
|
||||
goto fail;
|
||||
vgmstream = txtp->vgmstream[position];
|
||||
}
|
||||
else {
|
||||
/* get selected and remove non-selected */
|
||||
vgmstream = txtp->vgmstream[position + selected];
|
||||
txtp->vgmstream[position + selected] = NULL;
|
||||
for (i = 0; i < count; i++) {
|
||||
close_vgmstream(txtp->vgmstream[i + position]);
|
||||
}
|
||||
|
||||
/* set new vgmstream and reorder positions */
|
||||
update_vgmstream_list(vgmstream, txtp, position, count);
|
||||
}
|
||||
|
||||
/* set new vgmstream and reorder positions */
|
||||
update_vgmstream_list(vgmstream, txtp, position, count);
|
||||
|
||||
/* special "whole loop" settings */
|
||||
if (grp->entry.loop_anchor_start == 1) {
|
||||
grp->entry.config.config_set = 1;
|
||||
grp->entry.config.really_force_loop = 1;
|
||||
}
|
||||
|
||||
/* force selected vgmstream to be a segment when not a group already, and
|
||||
* group + vgmstream has config (AKA must loop/modify over the result) */
|
||||
//todo could optimize to not generate segment in some cases?
|
||||
if (!(vgmstream->layout_type == layout_layered || vgmstream->layout_type == layout_segmented) &&
|
||||
(grp->entry.config.config_set && vgmstream->config.config_set) ) {
|
||||
if (!make_group_segment(txtp, grp, position, 1))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
@ -595,15 +624,15 @@ static int parse_groups(txtp_header* txtp) {
|
||||
//;VGM_LOG("TXTP: group=%i, count=%i, groups=%i\n", pos, grp->count, groups);
|
||||
switch(grp->type) {
|
||||
case TXTP_GROUP_MODE_LAYERED:
|
||||
if (!make_group_layer(txtp, 1, pos, grp->count))
|
||||
if (!make_group_layer(txtp, grp, pos, grp->count))
|
||||
goto fail;
|
||||
break;
|
||||
case TXTP_GROUP_MODE_SEGMENTED:
|
||||
if (!make_group_segment(txtp, 1, pos, grp->count))
|
||||
if (!make_group_segment(txtp, grp, pos, grp->count))
|
||||
goto fail;
|
||||
break;
|
||||
case TXTP_GROUP_MODE_RANDOM:
|
||||
if (!make_group_random(txtp, 1, pos, grp->count, grp->selected))
|
||||
if (!make_group_random(txtp, grp, pos, grp->count, grp->selected))
|
||||
goto fail;
|
||||
break;
|
||||
default:
|
||||
@ -611,24 +640,28 @@ static int parse_groups(txtp_header* txtp) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* group may also have settings (like downmixing) */
|
||||
apply_settings(txtp->vgmstream[grp->position], &grp->group_settings);
|
||||
txtp->entry[grp->position] = grp->group_settings; /* memcpy old settings for subgroups */
|
||||
apply_settings(txtp->vgmstream[grp->position], &grp->entry);
|
||||
txtp->entry[grp->position] = grp->entry; /* memcpy old settings for subgroups */
|
||||
}
|
||||
|
||||
/* final tweaks (should be integrated with the above?) */
|
||||
if (txtp->is_layered) {
|
||||
if (!make_group_layer(txtp, 0, 0, txtp->vgmstream_count))
|
||||
if (!make_group_layer(txtp, NULL, 0, txtp->vgmstream_count))
|
||||
goto fail;
|
||||
}
|
||||
if (txtp->is_segmented) {
|
||||
if (!make_group_segment(txtp, 0, 0, txtp->vgmstream_count))
|
||||
if (!make_group_segment(txtp, NULL, 0, txtp->vgmstream_count))
|
||||
goto fail;
|
||||
}
|
||||
if (txtp->is_single) {
|
||||
/* special case of setting start_segment to force/overwrite looping
|
||||
* (better to use #E but left for compatibility with older TXTPs) */
|
||||
if (txtp->loop_start_segment == 1 && !txtp->loop_end_segment) {
|
||||
//todo try look settings
|
||||
//txtp->default_entry.config.config_set = 1;
|
||||
//txtp->default_entry.config.really_force_loop = 1;
|
||||
vgmstream_force_loop(txtp->vgmstream[0], 1, txtp->vgmstream[0]->loop_start_sample, txtp->vgmstream[0]->num_samples);
|
||||
}
|
||||
}
|
||||
@ -1426,6 +1459,15 @@ static void parse_params(txtp_entry* entry, char* params) {
|
||||
params += get_time_f(params, &tcfg->body_time_s, &tcfg->body_time, &tcfg->body_time_set);
|
||||
tcfg->config_set = 1;
|
||||
}
|
||||
else if (strcmp(command,"B") == 0) {
|
||||
params += get_time_f(params, &tcfg->body_time_s, &tcfg->body_time, &tcfg->body_time_set);
|
||||
tcfg->config_set = 1;
|
||||
/* similar to 'b' but implies no fades */
|
||||
tcfg->fade_time_set = 1;
|
||||
tcfg->fade_time = 0;
|
||||
tcfg->fade_delay_set = 1;
|
||||
tcfg->fade_delay = 0;
|
||||
}
|
||||
|
||||
/* other settings */
|
||||
else if (strcmp(command,"h") == 0) {
|
||||
@ -1458,7 +1500,7 @@ static void parse_params(txtp_entry* entry, char* params) {
|
||||
entry->loop_anchor_start = 1;
|
||||
//;VGM_LOG("TXTP: anchor start set\n");
|
||||
}
|
||||
else if (is_match(command,"A") || is_match(command,"@LOOP")) {
|
||||
else if (is_match(command,"A") || is_match(command,"@loop-end")) {
|
||||
entry->loop_anchor_end = 1;
|
||||
//;VGM_LOG("TXTP: anchor end set\n");
|
||||
}
|
||||
@ -1598,7 +1640,7 @@ static int add_group(txtp_header* txtp, char* line) {
|
||||
}
|
||||
}
|
||||
|
||||
parse_params(&cfg.group_settings, line);
|
||||
parse_params(&cfg.entry, line);
|
||||
|
||||
/* Groups can use "auto" position of last N files, so we need a counter that changes like this:
|
||||
* #layer of 2 (pos = 0)
|
||||
|
@ -67,7 +67,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
|
||||
/* checks */
|
||||
/* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011)
|
||||
* .wav: older ADPCM files [Punch Out!! (Wii)]
|
||||
* .wav: older PCM/ADPCM files [Spider-Man: Web of Shadows (PC), Punch Out!! (Wii)]
|
||||
* .xma: older XMA files [Too Human (X360), Tron Evolution (X360)]
|
||||
* .ogg: older Vorbis files [The King of Fighters XII (X360)]
|
||||
* .bnk: Wwise banks for memory .wem detection */
|
||||
@ -498,6 +498,7 @@ VGMSTREAM* init_vgmstream_wwise(STREAMFILE* sf) {
|
||||
|
||||
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail;
|
||||
if (!ww.seek_offset) goto fail;
|
||||
if (ww.channels > 8) goto fail; /* mapping not defined */
|
||||
|
||||
cfg.channels = ww.channels;
|
||||
cfg.table_offset = ww.seek_offset;
|
||||
@ -827,7 +828,7 @@ static int parse_wwise(STREAMFILE* sf, wwise_header* ww) {
|
||||
/* format to codec */
|
||||
switch(ww->format) {
|
||||
case 0x0001: ww->codec = PCM; break; /* older Wwise */
|
||||
case 0x0002: ww->codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
|
||||
case 0x0002: ww->codec = IMA; break; /* newer Wwise (variable, probably means "platform's ADPCM") */
|
||||
case 0x0069: ww->codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */
|
||||
case 0x0161: ww->codec = XWMA; break; /* WMAv2 */
|
||||
case 0x0162: ww->codec = XWMA; break; /* WMAPro */
|
||||
@ -893,61 +894,54 @@ fail:
|
||||
/*
|
||||
- old format
|
||||
"fmt" size 0x28, extra size 0x16 / size 0x18, extra size 0x06
|
||||
0x12 (2): flag? (00,10,18): not related to seek table, codebook type, chunk count, looping
|
||||
0x14 (4): channel config
|
||||
0x18-24 (16): ? (fixed: 0x01000000 00001000 800000AA 00389B71) [removed when extra size is 0x06]
|
||||
|
||||
"vorb" size 0x34
|
||||
0x00 (4): num_samples
|
||||
0x00 (4): dwTotalPCMFrames
|
||||
0x04 (4): skip samples?
|
||||
0x08 (4): ? (small if loop, 0 otherwise)
|
||||
0x0c (4): data start offset after seek table+setup, or loop start when "smpl" is present
|
||||
0x10 (4): ? (small, 0..~0x400)
|
||||
0x14 (4): approximate data size without seek table? (almost setup+packets)
|
||||
0x18 (4): setup_offset within data (0 = no seek table)
|
||||
0x1c (4): audio_offset within data
|
||||
0x20 (2): biggest packet size (not including header)?
|
||||
0x22 (2): ? (small, N..~0x100) uLastGranuleExtra?
|
||||
0x24 (4): ? (mid, 0~0x5000) dwDecodeAllocSize?
|
||||
0x28 (4): ? (mid, 0~0x5000) dwDecodeX64AllocSize?
|
||||
0x2c (4): parent bank/event id? uHashCodebook? (shared by several .wem a game, but not all need to share it)
|
||||
0x30 (1): blocksize_1_exp (small)
|
||||
0x31 (1): blocksize_0_exp (large)
|
||||
0x08 (4): LoopInfo.uLoopBeginExtra? (present if loop)
|
||||
0x0c (4): LoopInfo.dwLoopStartPacketOffset (data start, or loop start when "smpl" is present)
|
||||
0x10 (4): LoopInfo.uLoopEndExtra? (0..~0x400)
|
||||
0x14 (4): LoopInfo.dwLoopEndPacketOffset?
|
||||
0x18 (4): dwSeekTableSize (0 = no seek table)
|
||||
0x1c (4): dwVorbisDataOffset (offset within data)
|
||||
0x20 (2): uMaxPacketSize (not including header)
|
||||
0x22 (2): uLastGranuleExtra (0..~0x100)
|
||||
0x24 (4): dwDecodeAllocSize (0~0x5000)
|
||||
0x28 (4): dwDecodeX64AllocSize (mid, 0~0x5000)
|
||||
0x2c (4): uHashCodebook? (shared by several .wem a game, but not all need to share it)
|
||||
0x30 (1): uBlockSizes[0] (blocksize_1_exp, small)
|
||||
0x31 (1): uBlockSizes[1] (blocksize_0_exp, large)
|
||||
0x32 (2): empty
|
||||
|
||||
"vorb" size 0x28 / 0x2c / 0x2a
|
||||
0x00 (4): num_samples
|
||||
0x04 (4): data start offset after seek table+setup, or loop start when "smpl" is present
|
||||
0x08 (4): data end offset after seek table (setup+packets), or loop end when "smpl" is present
|
||||
0x00 (4): dwTotalPCMFrames
|
||||
0x04 (4): LoopInfo.dwLoopStartPacketOffset (data start, or loop start when "smpl" is present)
|
||||
0x08 (4): LoopInfo.dwLoopEndPacketOffset (data end, or loop end when "smpl" is present)
|
||||
0x0c (2): ? (small, 0..~0x400) [(4) when size is 0x2C]
|
||||
0x10 (4): setup_offset within data (0 = no seek table)
|
||||
0x14 (4): audio_offset within data
|
||||
0x18 (2): biggest packet size (not including header)?
|
||||
0x1a (2): ? (small, N..~0x100) uLastGranuleExtra? [(4) when size is 0x2C]
|
||||
0x1c (4): ? (mid, 0~0x5000) dwDecodeAllocSize?
|
||||
0x20 (4): ? (mid, 0~0x5000) dwDecodeX64AllocSize?
|
||||
0x24 (4): parent bank/event id? uHashCodebook? (shared by several .wem a game, but not all need to share it)
|
||||
0x28 (1): blocksize_1_exp (small) [removed when size is 0x28]
|
||||
0x29 (1): blocksize_0_exp (large) [removed when size is 0x28]
|
||||
0x10 (4): dwSeekTableSize (0 = no seek table)
|
||||
0x14 (4): dwVorbisDataOffset (offset within data)
|
||||
0x18 (2): uMaxPacketSize (not including header)
|
||||
0x1a (2): uLastGranuleExtra (0..~0x100) [(4) when size is 0x2C]
|
||||
0x1c (4): dwDecodeAllocSize (0~0x5000)
|
||||
0x20 (4): dwDecodeX64AllocSize (0~0x5000)
|
||||
0x24 (4): uHashCodebook? (shared by several .wem a game, but not all need to share it)
|
||||
0x28 (1): uBlockSizes[0] (blocksize_1_exp, small) [removed when size is 0x28]
|
||||
0x29 (1): uBlockSizes[1] (blocksize_0_exp, large) [removed when size is 0x28]
|
||||
|
||||
- new format:
|
||||
"fmt" size 0x42, extra size 0x30
|
||||
0x12 (2): flag? (00,10,18): not related to seek table, codebook type, chunk count, looping, etc
|
||||
0x14 (4): channel config
|
||||
0x18 (4): num_samples
|
||||
0x1c (4): data start offset after seek table+setup, or loop start when "smpl" is present
|
||||
0x20 (4): data end offset after seek table (setup+packets), or loop end when "smpl" is present
|
||||
0x24 (2): ?1 (small, 0..~0x400)
|
||||
0x26 (2): ?2 (small, N..~0x100): not related to seek table, codebook type, chunk count, looping, packet size, samples, etc
|
||||
0x28 (4): setup offset within data (0 = no seek table)
|
||||
0x2c (4): audio offset within data
|
||||
0x30 (2): biggest packet size (not including header)
|
||||
0x32 (2): (small, 0..~0x100) uLastGranuleExtra?
|
||||
0x34 (4): ? (mid, 0~0x5000) dwDecodeAllocSize?
|
||||
0x38 (4): ? (mid, 0~0x5000) dwDecodeX64AllocSize?
|
||||
0x40 (1): blocksize_1_exp (small)
|
||||
0x41 (1): blocksize_0_exp (large)
|
||||
|
||||
Wwise encoder options, unknown fields above may be reflect these:
|
||||
https://www.audiokinetic.com/library/edge/?source=Help&id=vorbis_encoder_parameters
|
||||
0x18 (4): dwTotalPCMFrames
|
||||
0x1c (4): LoopInfo.dwLoopStartPacketOffset (data start, or loop start when "smpl" is present)
|
||||
0x20 (4): LoopInfo.dwLoopEndPacketOffset (data end, or loop end when "smpl" is present)
|
||||
0x24 (2): LoopInfo.uLoopBeginExtra (small, 0..~0x400)
|
||||
0x26 (2): LoopInfo.uLoopEndExtra (extra samples after seek?)
|
||||
0x28 (4): dwSeekTableSize (0 = no seek table)
|
||||
0x2c (4): dwVorbisDataOffset (offset within data)
|
||||
0x30 (2): uMaxPacketSize (not including header)
|
||||
0x32 (2): uLastGranuleExtra (small, 0..~0x100)
|
||||
0x34 (4): dwDecodeAllocSize (mid, 0~0x5000)
|
||||
0x38 (4): dwDecodeX64AllocSize (mid, 0~0x5000)
|
||||
0x40 (1): uBlockSizes[0] (blocksize_1_exp, small)
|
||||
0x41 (1): uBlockSizes[1] (blocksize_0_exp, large)
|
||||
*/
|
||||
|
@ -877,12 +877,20 @@ void vgmstream_set_loop_target(VGMSTREAM* vgmstream, int loop_target) {
|
||||
/* MISC */
|
||||
/*******************************************************************************/
|
||||
|
||||
static void describe_get_time(int32_t samples, int sample_rate, double* p_time_mm, double* p_time_ss) {
|
||||
double seconds = (double)samples / sample_rate;
|
||||
*p_time_mm = (int)(seconds / 60.0);
|
||||
*p_time_ss = seconds - *p_time_mm * 60.0f;
|
||||
if (*p_time_ss >= 59.999) /* avoid round up to 60.0 when printing to %06.3f */
|
||||
*p_time_ss = 59.999;
|
||||
}
|
||||
|
||||
/* 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, seconds;
|
||||
double time_mm, time_ss;
|
||||
|
||||
if (!vgmstream) {
|
||||
snprintf(temp,TEMPSIZE, "NULL VGMSTREAM");
|
||||
@ -935,27 +943,22 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) {
|
||||
concatn(length,desc,"\n");
|
||||
}
|
||||
|
||||
/* times mod sounds avoid round up to 60.0 */
|
||||
if (vgmstream->loop_start_sample >= 0 && vgmstream->loop_end_sample > vgmstream->loop_start_sample) {
|
||||
if (!vgmstream->loop_flag) {
|
||||
concatn(length,desc,"looping: disabled\n");
|
||||
}
|
||||
|
||||
seconds = (double)vgmstream->loop_start_sample / vgmstream->sample_rate;
|
||||
time_mm = (int)(seconds / 60.0);
|
||||
time_ss = seconds - time_mm * 60.0f;
|
||||
describe_get_time(vgmstream->loop_start_sample, vgmstream->sample_rate, &time_mm, &time_ss);
|
||||
snprintf(temp,TEMPSIZE, "loop start: %d samples (%1.0f:%06.3f seconds)\n", vgmstream->loop_start_sample, time_mm, time_ss);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
seconds = (double)vgmstream->loop_end_sample / vgmstream->sample_rate;
|
||||
time_mm = (int)(seconds / 60.0);
|
||||
time_ss = seconds - time_mm * 60.0f;
|
||||
describe_get_time(vgmstream->loop_end_sample, vgmstream->sample_rate, &time_mm, &time_ss);
|
||||
snprintf(temp,TEMPSIZE, "loop end: %d samples (%1.0f:%06.3f seconds)\n", vgmstream->loop_end_sample, time_mm, time_ss);
|
||||
concatn(length,desc,temp);
|
||||
}
|
||||
|
||||
seconds = (double)vgmstream->num_samples / vgmstream->sample_rate;
|
||||
time_mm = (int)(seconds / 60.0);
|
||||
time_ss = seconds - time_mm * 60.0;
|
||||
describe_get_time(vgmstream->num_samples, vgmstream->sample_rate, &time_mm, &time_ss);
|
||||
snprintf(temp,TEMPSIZE, "stream total samples: %d (%1.0f:%06.3f seconds)\n", vgmstream->num_samples, time_mm, time_ss);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
@ -1012,7 +1015,7 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) {
|
||||
concatn(length,desc,temp);
|
||||
concatn(length,desc,"\n");
|
||||
|
||||
snprintf(temp,TEMPSIZE, "bitrate: %d kbps\n", get_vgmstream_average_bitrate(vgmstream) / 1000); //todo \n?
|
||||
snprintf(temp,TEMPSIZE, "bitrate: %d kbps\n", get_vgmstream_average_bitrate(vgmstream) / 1000);
|
||||
concatn(length,desc,temp);
|
||||
|
||||
/* only interesting if more than one */
|
||||
@ -1032,13 +1035,9 @@ void describe_vgmstream(VGMSTREAM* vgmstream, char* desc, int length) {
|
||||
}
|
||||
|
||||
if (vgmstream->config_enabled) {
|
||||
double time_mm, time_ss, seconds;
|
||||
int32_t samples = vgmstream->pstate.play_duration;
|
||||
|
||||
seconds = (double)samples / vgmstream->sample_rate;
|
||||
time_mm = (int)(seconds / 60.0);
|
||||
time_ss = seconds - time_mm * 60.0f;
|
||||
|
||||
describe_get_time(samples, vgmstream->sample_rate, &time_mm, &time_ss);
|
||||
snprintf(temp,TEMPSIZE, "play duration: %d samples (%1.0f:%06.3f seconds)\n", samples, time_mm, time_ss);
|
||||
concatn(length,desc,temp);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user