Merge pull request #769 from bnnm/txtp-3do

txtp 3do
This commit is contained in:
bnnm 2020-11-29 20:22:34 +01:00 committed by GitHub
commit 77cc431be7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 286 additions and 223 deletions

View File

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

View File

@ -25,6 +25,7 @@ static const char* extension_list[] = {
"208",
"2dx9",
"2pfs",
"3do",
"4", //for Game.com audio
"8", //txth/reserved [Gungage (PS1)]
"800",
@ -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"},

View File

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

View File

@ -4,45 +4,49 @@
/* .str - 3DO format with CTRL/SNDS/SHDR blocks [Icebreaker (3DO), Battle Pinball (3DO)] */
VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) {
VGMSTREAM* init_vgmstream_str_snds(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, shdr_offset = -1;
int loop_flag, channel_count, found_shdr = 0;
int loop_flag, channels, found_shdr = 0;
size_t file_size, ctrl_size = -1;
/* checks */
if (!check_extensions(streamFile, "str"))
/* .str: standard
* .3do: Aqua World - Umimi Monogatari (3DO) movies */
if (!check_extensions(sf, "str,3do"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4354524c && /* "CTRL" */
read_32bitBE(0x00,streamFile) != 0x534e4453 && /* "SNDS" */
read_32bitBE(0x00,streamFile) != 0x53484452) /* "SHDR" */
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(streamFile);
file_size = get_streamfile_size(sf);
start_offset = 0x00;
/* scan chunks until we find a SNDS containing a SHDR */
{
off_t current_chunk = 0;
off_t offset = 0;
uint32_t size;
while (!found_shdr && current_chunk < file_size) {
if (current_chunk < 0) goto fail;
while (!found_shdr && offset < file_size) {
if (offset < 0) goto fail;
if (current_chunk+read_32bitBE(current_chunk+0x04,streamFile) >= file_size)
size = read_u32be(offset + 0x04,sf);
if (offset + size >= file_size)
goto fail;
switch (read_32bitBE(current_chunk,streamFile)) {
switch (read_u32be(offset + 0x00,sf)) {
case 0x4354524C: /* "CTRL" */
ctrl_size = read_32bitBE(current_chunk+4,streamFile);
ctrl_size = read_u32be(offset + 0x04,sf);
break;
case 0x534e4453: /* "SNDS" */
switch (read_32bitBE(current_chunk+16,streamFile)) {
switch (read_u32be(offset + 0x10,sf)) {
case 0x53484452: /* SHDR */
found_shdr = 1;
shdr_offset = current_chunk+16;
shdr_offset = offset + 0x10;
break;
default:
break;
@ -50,10 +54,10 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) {
break;
case 0x53484452: /* "SHDR" */
switch (read_32bitBE(current_chunk+0x7C, streamFile)) {
switch (read_u32be(offset + 0x7C, sf)) {
case 0x4354524C: /* "CTRL" */
/* to distinguish between styles */
ctrl_size = read_32bitBE(current_chunk + 0x80, streamFile);
ctrl_size = read_u32be(offset + 0x80, sf);
break;
default:
@ -61,50 +65,51 @@ VGMSTREAM * init_vgmstream_str_snds(STREAMFILE *streamFile) {
}
break;
default:
/* ignore others for now */
default: /* ignore others */
break;
}
current_chunk += read_32bitBE(current_chunk+0x04,streamFile);
offset += size;
}
}
if (!found_shdr) goto fail;
if (!found_shdr)
goto fail;
channel_count = read_32bitBE(shdr_offset+0x20,streamFile);
channels = read_u32be(shdr_offset+0x20,sf);
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_STR_SNDS;
vgmstream->sample_rate = read_32bitBE(shdr_offset+0x1c,streamFile);
vgmstream->sample_rate = read_u32be(shdr_offset+0x1c,sf);
if (ctrl_size == 0x1C || ctrl_size == 0x0B || ctrl_size == -1) {
vgmstream->num_samples = read_32bitBE(shdr_offset+0x2c,streamFile) - 1; /* sample count? */
vgmstream->num_samples = read_u32be(shdr_offset+0x2c,sf) - 1; /* sample count? */
}
else {
vgmstream->num_samples = read_32bitBE(shdr_offset+0x2c,streamFile) * 0x10; /* frame count? */
vgmstream->num_samples = read_u32be(shdr_offset+0x2c,sf) * 0x10; /* frame count? */
}
vgmstream->num_samples /= vgmstream->channels;
switch (read_32bitBE(shdr_offset+0x24,streamFile)) {
switch (read_u32be(shdr_offset + 0x24,sf)) {
case 0x53445832: /* "SDX2" */
if (channel_count > 1) {
if (channels > 1) {
vgmstream->coding_type = coding_SDX2_int;
vgmstream->interleave_block_size = 1;
} else
} 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))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View File

@ -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,10 +544,17 @@ 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;
}
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;
@ -552,6 +564,23 @@ static int make_group_random(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;
}
/* 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)

View File

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

View File

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