diff --git a/src/coding/libs/utkdec.c b/src/coding/libs/utkdec.c index f93f9dbd..b08ffb45 100644 --- a/src/coding/libs/utkdec.c +++ b/src/coding/libs/utkdec.c @@ -44,23 +44,23 @@ static const uint8_t mask_table[8] = { * note this table is mirrored: for (i = 1 .. 32) t[64 - i] = -t[i]) */ static const float utk_rc_table[64] = { /* 6b index start */ - +0.000000, -0.996776, -0.990327, -0.983879, - -0.977431, -0.970982, -0.964534, -0.958085, - -0.951637, -0.930754, -0.904960, -0.879167, - -0.853373, -0.827579, -0.801786, -0.775992, + +0.000000f, -0.996776f, -0.990327f, -0.983879f, + -0.977431f, -0.970982f, -0.964534f, -0.958085f, + -0.951637f, -0.930754f, -0.904960f, -0.879167f, + -0.853373f, -0.827579f, -0.801786f, -0.775992f, /* 5b index start */ - -0.750198, -0.724405, -0.698611, -0.670635, - -0.619048, -0.567460, -0.515873, -0.464286, - -0.412698, -0.361111, -0.309524, -0.257937, - -0.206349, -0.154762, -0.103175, -0.051587, - +0.000000, +0.051587, +0.103175, +0.154762, - +0.206349, +0.257937, +0.309524, +0.361111, - +0.412698, +0.464286, +0.515873, +0.567460, - +0.619048, +0.670635, +0.698611, +0.724405, - +0.750198, +0.775992, +0.801786, +0.827579, - +0.853373, +0.879167, +0.904960, +0.930754, - +0.951637, +0.958085, +0.964534, +0.970982, - +0.977431, +0.983879, +0.990327, +0.996776, + -0.750198f, -0.724405f, -0.698611f, -0.670635f, + -0.619048f, -0.567460f, -0.515873f, -0.464286f, + -0.412698f, -0.361111f, -0.309524f, -0.257937f, + -0.206349f, -0.154762f, -0.103175f, -0.051587f, + +0.000000f, +0.051587f, +0.103175f, +0.154762f, + +0.206349f, +0.257937f, +0.309524f, +0.361111f, + +0.412698f, +0.464286f, +0.515873f, +0.567460f, + +0.619048f, +0.670635f, +0.698611f, +0.724405f, + +0.750198f, +0.775992f, +0.801786f, +0.827579f, + +0.853373f, +0.879167f, +0.904960f, +0.930754f, + +0.951637f, +0.958085f, +0.964534f, +0.970982f, + +0.977431f, +0.983879f, +0.990327f, +0.996776f, }; static const uint8_t utk_codebooks[2][256] = { @@ -145,23 +145,25 @@ static const struct { {MDL_LARGEPULSE, 7, +6.0f} }; +/* In Lego Batman 2 gain[0] = 1.068 while other games (Lego Marvel, Lego SW) is 64.0f. + * The latter makes more sense and the former is audibly worse so it was probably a bug. */ static const float cbx_fixed_gains[64] = { - 1.068, 68.351997, 72.999931, 77.963921, - 83.265465, 88.927513, 94.974579, 101.43285, - 108.33028, 115.69673, 123.5641, 131.96646, - 140.94017, 150.52409, 160.75972, 171.69138, - 183.36638, 195.83528, 209.15207, 223.3744, - 238.56386, 254.78619, 272.11163, 290.6152, - 310.37701, 331.48264, 354.02344, 378.09702, - 403.80759, 431.26648, 460.59259, 491.91287, - 525.36292, 561.08759, 599.24152, 639.98993, - 683.50922, 729.98779, 779.62695, 832.64154, - 889.26111, 949.73083, 1014.3125, 1083.2858, - 1156.9491, 1235.6216, 1319.6438, 1409.3795, - 1505.2173, 1607.572, 1716.8868, 1833.6351, - 1958.3223, 2091.488, 2233.7092, 2385.6013, - 2547.822, 2721.0737, 2906.1067, 3103.7219, - 3314.7749, 3540.1794, 3780.9116, 4038.0134, + 64.0f, 68.351997f, 72.999931f, 77.963921f, + 83.265465f, 88.927513f, 94.974579f, 101.43285f, + 108.33028f, 115.69673f, 123.5641f, 131.96646f, + 140.94017f, 150.52409f, 160.75972f, 171.69138f, + 183.36638f, 195.83528f, 209.15207f, 223.3744f, + 238.56386f, 254.78619f, 272.11163f, 290.6152f, + 310.37701f, 331.48264f, 354.02344f, 378.09702f, + 403.80759f, 431.26648f, 460.59259f, 491.91287f, + 525.36292f, 561.08759f, 599.24152f, 639.98993f, + 683.50922f, 729.98779f, 779.62695f, 832.64154f, + 889.26111f, 949.73083f, 1014.3125f, 1083.2858f, + 1156.9491f, 1235.6216f, 1319.6438f, 1409.3795f, + 1505.2173f, 1607.572f, 1716.8868f, 1833.6351f, + 1958.3223f, 2091.488f, 2233.7092f, 2385.6013f, + 2547.822f, 2721.0737f, 2906.1067f, 3103.7219f, + 3314.7749f, 3540.1794f, 3780.9116f, 4038.0134f, }; /* Bitreader in OG code can only read from set ptr; doesn't seem to check bounds though. @@ -223,14 +225,15 @@ static void consume_bits(struct bitreader_t* br, int count) { static void parse_header(utk_context_t* ctx) { if (ctx->type == UTK_CBX) { - /* CBX uses fixed parameters unlike EA-MT, probably encoder defaults for MT10:1 */ + /* CBX uses fixed parameters unlike EA-MT, probably encoder defaults for MT10:1 + * equivalent to EA-MT with base_thre = 8, base_gain = 7, base_mult = 28 (plus rounding diffs). + * OG CBX code uses values/tables directly rather than config though */ ctx->reduced_bandwidth = true; + ctx->multipulse_threshold = 32 - 8; - /* equivalent to EA-MT with base_gain = 8, base_mult = 28 (plus rounding diffs) - * then fixed_gain[0] is set to 1.068 afterwards. - * OG CBX code uses config/tables directly rather than copying though */ - for (int i = 0; i < 64; i++) { + ctx->fixed_gains[0] = cbx_fixed_gains[0]; + for (int i = 1; i < 64; i++) { ctx->fixed_gains[i] = cbx_fixed_gains[i]; } } diff --git a/src/formats.c b/src/formats.c index f132d1c0..758b6ada 100644 --- a/src/formats.c +++ b/src/formats.c @@ -132,6 +132,7 @@ static const char* extension_list[] = { "caf", "cat", "cbd2", + "cbx", "cd", "cfn", //fake extension for CAF (renamed, to be removed?) "chd", //txth/reserved [Donkey Konga (GC), Star Fox Assault (GC)] @@ -1427,6 +1428,7 @@ static const meta_info meta_info_list[] = { {meta_SNDS, "Sony SNDS header"}, {meta_NXOF, "Nihon Falcom FDK header"}, {meta_GWB_GWD, "Ubisoft GWB+GWD header"}, + {meta_CBX, "Traveller's Tales CBX header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/src/meta/cbx.c b/src/meta/cbx.c new file mode 100644 index 00000000..09a919ce --- /dev/null +++ b/src/meta/cbx.c @@ -0,0 +1,46 @@ +#include "meta.h" +#include "../coding/coding.h" + + +/* !B0X - Traveller's Tales speech files [Lego Batman 2 (PC), Lego Dimensions (PS3)] */ +VGMSTREAM* init_vgmstream_cbx(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t start_offset, pcm_size; + int loop_flag, channels, sample_rate; + + + /* checks */ + if (!is_id32be(0x00,sf, "!B0X")) + return NULL; + if (!check_extensions(sf, "cbx")) + return NULL; + + /* debug strings identify this as "Chatterbox"/"CBOX"/"CBX", while sound lib seems called "NuSound" + * (probably based on .utk) */ + + pcm_size = read_u32le(0x04, sf); + sample_rate = read_s32le(0x08, sf); + start_offset = 0x0c; + channels = 1; + loop_flag = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_CBX; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = pcm_size / 2; + vgmstream->coding_type = coding_EA_MT; + vgmstream->layout_type = layout_none; + vgmstream->codec_data = init_ea_mt_cbx(vgmstream->channels); + if (!vgmstream->codec_data) goto fail; + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/meta.h b/src/meta/meta.h index d59e75a7..b3680058 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -1000,4 +1000,6 @@ VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf); VGMSTREAM* init_vgmstream_gwb_gwd(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_cbx(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/src/vgmstream.c b/src/vgmstream.c index 5d0753fd..dc1051ff 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -523,6 +523,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_nxof, init_vgmstream_gwb_gwd, init_vgmstream_s_pack, + init_vgmstream_cbx, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_scd_pcm, diff --git a/src/vgmstream_types.h b/src/vgmstream_types.h index 0fe63960..be943507 100644 --- a/src/vgmstream_types.h +++ b/src/vgmstream_types.h @@ -703,6 +703,7 @@ typedef enum { meta_SNDS, meta_NXOF, meta_GWB_GWD, + meta_CBX, } meta_t;