Updated VGMStream to r1050-3581-g8fd25a33
parent
1be15d73e3
commit
0b09d1567b
|
@ -133,6 +133,16 @@
|
|||
833A7A2E1ED11961003EC53E /* xau.c in Sources */ = {isa = PBXBuildFile; fileRef = 833A7A2D1ED11961003EC53E /* xau.c */; };
|
||||
8342469420C4D23000926E48 /* h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469020C4D22F00926E48 /* h4m.c */; };
|
||||
8342469620C4D23D00926E48 /* blocked_h4m.c in Sources */ = {isa = PBXBuildFile; fileRef = 8342469520C4D23D00926E48 /* blocked_h4m.c */; };
|
||||
8346D97925BF838C00D1A8B0 /* idtech.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97425BF838C00D1A8B0 /* idtech.c */; };
|
||||
8346D97A25BF838C00D1A8B0 /* idtech_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */; };
|
||||
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97625BF838C00D1A8B0 /* ktac.c */; };
|
||||
8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97725BF838C00D1A8B0 /* mjb_mjh.c */; };
|
||||
8346D97D25BF838C00D1A8B0 /* compresswave.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97825BF838C00D1A8B0 /* compresswave.c */; };
|
||||
8346D98325BF83B300D1A8B0 /* speex_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D97E25BF83B200D1A8B0 /* speex_decoder.c */; };
|
||||
8346D98425BF83B300D1A8B0 /* coding_utils_samples.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */; };
|
||||
8346D98525BF83B300D1A8B0 /* compresswave_decoder_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */; };
|
||||
8346D98625BF83B300D1A8B0 /* compresswave_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */; };
|
||||
8346D98725BF83B300D1A8B0 /* compresswave_decoder_lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */; };
|
||||
8349A8DF1FE6251F00E26435 /* vorbis_custom_utils_vid1.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */; };
|
||||
8349A8E11FE6251F00E26435 /* ea_mt_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */; };
|
||||
8349A8E81FE6253900E26435 /* blocked_dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 8349A8E21FE6253800E26435 /* blocked_dec.c */; };
|
||||
|
@ -471,10 +481,8 @@
|
|||
837CEADB23487E8300E62A4A /* bgw_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAD723487E8300E62A4A /* bgw_streamfile.h */; };
|
||||
837CEAF123487F2C00E62A4A /* xvas.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADC23487F2900E62A4A /* xvas.c */; };
|
||||
837CEAF223487F2C00E62A4A /* raw_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADD23487F2A00E62A4A /* raw_pcm.c */; };
|
||||
837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */; };
|
||||
837CEAF423487F2C00E62A4A /* xa_xa30.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEADF23487F2A00E62A4A /* xa_xa30.c */; };
|
||||
837CEAF523487F2C00E62A4A /* ubi_hx.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE023487F2A00E62A4A /* ubi_hx.c */; };
|
||||
837CEAF623487F2C00E62A4A /* mzrt.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE123487F2A00E62A4A /* mzrt.c */; };
|
||||
837CEAF723487F2C00E62A4A /* nub.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE223487F2A00E62A4A /* nub.c */; };
|
||||
837CEAF823487F2C00E62A4A /* xmv_valve.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE323487F2A00E62A4A /* xmv_valve.c */; };
|
||||
837CEAF923487F2C00E62A4A /* xavs.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE423487F2A00E62A4A /* xavs.c */; };
|
||||
|
@ -877,6 +885,16 @@
|
|||
833A7A2D1ED11961003EC53E /* xau.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xau.c; sourceTree = "<group>"; };
|
||||
8342469020C4D22F00926E48 /* h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = h4m.c; sourceTree = "<group>"; };
|
||||
8342469520C4D23D00926E48 /* blocked_h4m.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_h4m.c; sourceTree = "<group>"; };
|
||||
8346D97425BF838C00D1A8B0 /* idtech.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = idtech.c; sourceTree = "<group>"; };
|
||||
8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = idtech_streamfile.h; sourceTree = "<group>"; };
|
||||
8346D97625BF838C00D1A8B0 /* ktac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ktac.c; sourceTree = "<group>"; };
|
||||
8346D97725BF838C00D1A8B0 /* mjb_mjh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mjb_mjh.c; sourceTree = "<group>"; };
|
||||
8346D97825BF838C00D1A8B0 /* compresswave.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave.c; sourceTree = "<group>"; };
|
||||
8346D97E25BF83B200D1A8B0 /* speex_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = speex_decoder.c; sourceTree = "<group>"; };
|
||||
8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coding_utils_samples.h; sourceTree = "<group>"; };
|
||||
8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave_decoder_lib.c; sourceTree = "<group>"; };
|
||||
8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compresswave_decoder.c; sourceTree = "<group>"; };
|
||||
8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = compresswave_decoder_lib.h; sourceTree = "<group>"; };
|
||||
8349A8DC1FE6251E00E26435 /* vorbis_custom_utils_vid1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_vid1.c; sourceTree = "<group>"; };
|
||||
8349A8DE1FE6251F00E26435 /* ea_mt_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_mt_decoder.c; sourceTree = "<group>"; };
|
||||
8349A8E21FE6253800E26435 /* blocked_dec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_dec.c; sourceTree = "<group>"; };
|
||||
|
@ -1215,10 +1233,8 @@
|
|||
837CEAD723487E8300E62A4A /* bgw_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bgw_streamfile.h; sourceTree = "<group>"; };
|
||||
837CEADC23487F2900E62A4A /* xvas.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xvas.c; sourceTree = "<group>"; };
|
||||
837CEADD23487F2A00E62A4A /* raw_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_pcm.c; sourceTree = "<group>"; };
|
||||
837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mzrt_streamfile.h; sourceTree = "<group>"; };
|
||||
837CEADF23487F2A00E62A4A /* xa_xa30.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xa_xa30.c; sourceTree = "<group>"; };
|
||||
837CEAE023487F2A00E62A4A /* ubi_hx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ubi_hx.c; sourceTree = "<group>"; };
|
||||
837CEAE123487F2A00E62A4A /* mzrt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mzrt.c; sourceTree = "<group>"; };
|
||||
837CEAE223487F2A00E62A4A /* nub.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = nub.c; sourceTree = "<group>"; };
|
||||
837CEAE323487F2A00E62A4A /* xmv_valve.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmv_valve.c; sourceTree = "<group>"; };
|
||||
837CEAE423487F2A00E62A4A /* xavs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xavs.c; sourceTree = "<group>"; };
|
||||
|
@ -1545,8 +1561,12 @@
|
|||
83031EC0243C50A800C3F3E0 /* circus_decoder_miniz.c */,
|
||||
83031EBE243C50A700C3F3E0 /* circus_decoder_miniz.h */,
|
||||
832BF7FC21E050B6006F50F1 /* circus_decoder.c */,
|
||||
8346D97F25BF83B200D1A8B0 /* coding_utils_samples.h */,
|
||||
831BA6221EAC61CB00CF89B0 /* coding_utils.c */,
|
||||
836F6DE518BDC2180095E648 /* coding.h */,
|
||||
8346D98025BF83B300D1A8B0 /* compresswave_decoder_lib.c */,
|
||||
8346D98225BF83B300D1A8B0 /* compresswave_decoder_lib.h */,
|
||||
8346D98125BF83B300D1A8B0 /* compresswave_decoder.c */,
|
||||
834FE0AD215C798B000A5D3D /* derf_decoder.c */,
|
||||
8351F3252212B53300A606E4 /* dsa_decoder.c */,
|
||||
834FE0B1215C798C000A5D3D /* ea_mt_decoder_utk.h */,
|
||||
|
@ -1600,6 +1620,7 @@
|
|||
8373341423F60C7B00DE14DC /* relic_decoder.c */,
|
||||
836F6DFB18BDC2180095E648 /* SASSC_decoder.c */,
|
||||
836F6DFC18BDC2180095E648 /* sdx2_decoder.c */,
|
||||
8346D97E25BF83B200D1A8B0 /* speex_decoder.c */,
|
||||
8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */,
|
||||
837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */,
|
||||
83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */,
|
||||
|
@ -1734,6 +1755,7 @@
|
|||
8306B0CF2098458F000302D4 /* caf.c */,
|
||||
836F6E3B18BDC2180095E648 /* capdsp.c */,
|
||||
834FE0E8215C79EC000A5D3D /* ck.c */,
|
||||
8346D97825BF838C00D1A8B0 /* compresswave.c */,
|
||||
83A8BAE425667AA7000F5F3F /* cpk.c */,
|
||||
83FC177023AC59A800E1025F /* cri_utf.c */,
|
||||
83FC176C23AC58D100E1025F /* cri_utf.h */,
|
||||
|
@ -1799,6 +1821,8 @@
|
|||
834FE0DF215C79EB000A5D3D /* hd3_bd3.c */,
|
||||
836F6E5318BDC2180095E648 /* his.c */,
|
||||
834FE0E0215C79EB000A5D3D /* idsp_ie.c */,
|
||||
8346D97525BF838C00D1A8B0 /* idtech_streamfile.h */,
|
||||
8346D97425BF838C00D1A8B0 /* idtech.c */,
|
||||
8399335C2591E8C0001855AF /* ifs.c */,
|
||||
83C7280922BC893C00678B4A /* ikm.c */,
|
||||
837CEAE623487F2B00E62A4A /* ima.c */,
|
||||
|
@ -1816,6 +1840,7 @@
|
|||
834FE0C3215C79E6000A5D3D /* kma9_streamfile.h */,
|
||||
83A21F83201D8981000F04B9 /* kma9.c */,
|
||||
836F6E5918BDC2180095E648 /* kraw.c */,
|
||||
8346D97625BF838C00D1A8B0 /* ktac.c */,
|
||||
83AA7F792519C042004C5298 /* ktsc.c */,
|
||||
83D20074248DDB760048BD24 /* ktsr.c */,
|
||||
830EBE122004656E0023AA10 /* ktss.c */,
|
||||
|
@ -1829,6 +1854,7 @@
|
|||
83EDE5D61A70951A005F5D84 /* mca.c */,
|
||||
836F6E5E18BDC2180095E648 /* meta.h */,
|
||||
834FE0DE215C79EB000A5D3D /* mib_mih.c */,
|
||||
8346D97725BF838C00D1A8B0 /* mjb_mjh.c */,
|
||||
836F6E5F18BDC2180095E648 /* mn_str.c */,
|
||||
8349A9031FE6258100E26435 /* mogg.c */,
|
||||
836F6E6018BDC2180095E648 /* mp4.c */,
|
||||
|
@ -1852,8 +1878,6 @@
|
|||
836F6E6318BDC2180095E648 /* musc.c */,
|
||||
836F6E6418BDC2180095E648 /* musx.c */,
|
||||
836F6E6518BDC2180095E648 /* myspd.c */,
|
||||
837CEADE23487F2A00E62A4A /* mzrt_streamfile.h */,
|
||||
837CEAE123487F2A00E62A4A /* mzrt.c */,
|
||||
8349A9061FE6258100E26435 /* naac.c */,
|
||||
836F6E6618BDC2180095E648 /* naomi_adpcm.c */,
|
||||
836F6E6718BDC2180095E648 /* naomi_spsd.c */,
|
||||
|
@ -2156,6 +2180,8 @@
|
|||
836F705518BDC2190095E648 /* streamtypes.h in Headers */,
|
||||
836F6F3518BDC2190095E648 /* nwa_decoder.h in Headers */,
|
||||
83031EC9243C50A800C3F3E0 /* circus_decoder_lzxpcm.h in Headers */,
|
||||
8346D97A25BF838C00D1A8B0 /* idtech_streamfile.h in Headers */,
|
||||
8346D98425BF83B300D1A8B0 /* coding_utils_samples.h in Headers */,
|
||||
83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */,
|
||||
83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */,
|
||||
83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */,
|
||||
|
@ -2201,6 +2227,7 @@
|
|||
834FE0B5215C798C000A5D3D /* acm_decoder_libacm.h in Headers */,
|
||||
839E21E61F2EDAF100EE54D7 /* vorbis_custom_data_wwise.h in Headers */,
|
||||
83AA7F8A2519C076004C5298 /* decode.h in Headers */,
|
||||
8346D98725BF83B300D1A8B0 /* compresswave_decoder_lib.h in Headers */,
|
||||
8373342D23F60CDC00DE14DC /* bnsf_keys.h in Headers */,
|
||||
834FE103215C79ED000A5D3D /* ea_schl_streamfile.h in Headers */,
|
||||
83F0AA6021E2028C004BBC04 /* vsv_streamfile.h in Headers */,
|
||||
|
@ -2223,7 +2250,6 @@
|
|||
83C7281822BC893D00678B4A /* sfh_streamfile.h in Headers */,
|
||||
834FE0EF215C79ED000A5D3D /* xvag_streamfile.h in Headers */,
|
||||
8349A90C1FE6258200E26435 /* sqex_scd_streamfile.h in Headers */,
|
||||
837CEAF323487F2C00E62A4A /* mzrt_streamfile.h in Headers */,
|
||||
83031EC4243C50A800C3F3E0 /* circus_decoder_miniz.h in Headers */,
|
||||
8349A91B1FE6258200E26435 /* adx_keys.h in Headers */,
|
||||
836F6F4D18BDC2190095E648 /* layout.h in Headers */,
|
||||
|
@ -2419,6 +2445,7 @@
|
|||
836F705318BDC2190095E648 /* streamfile.c in Sources */,
|
||||
836F6F7418BDC2190095E648 /* bgw.c in Sources */,
|
||||
83F1EE2D245D4FB20076E182 /* imuse_decoder.c in Sources */,
|
||||
8346D98325BF83B300D1A8B0 /* speex_decoder.c in Sources */,
|
||||
836F6F7218BDC2190095E648 /* baf.c in Sources */,
|
||||
83F5F8831908D0A400C8E65F /* fsb5.c in Sources */,
|
||||
836F6FD418BDC2190095E648 /* ps2_dxh.c in Sources */,
|
||||
|
@ -2447,6 +2474,7 @@
|
|||
836F6FA118BDC2190095E648 /* myspd.c in Sources */,
|
||||
837CEB0123487F2C00E62A4A /* xmu.c in Sources */,
|
||||
836F6FD718BDC2190095E648 /* ps2_filp.c in Sources */,
|
||||
8346D97925BF838C00D1A8B0 /* idtech.c in Sources */,
|
||||
83FF0EBC1E93282100C58054 /* wwise.c in Sources */,
|
||||
836F6F7018BDC2190095E648 /* apple_caff.c in Sources */,
|
||||
836F6FB618BDC2190095E648 /* ngc_sck_dsp.c in Sources */,
|
||||
|
@ -2459,6 +2487,7 @@
|
|||
836F702318BDC2190095E648 /* rsf.c in Sources */,
|
||||
834FE109215C79ED000A5D3D /* idsp_ie.c in Sources */,
|
||||
83299FD01E7660C7003A3242 /* bik.c in Sources */,
|
||||
8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */,
|
||||
8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */,
|
||||
836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */,
|
||||
83C7281322BC893D00678B4A /* mta2.c in Sources */,
|
||||
|
@ -2518,6 +2547,7 @@
|
|||
836F703618BDC2190095E648 /* thp.c in Sources */,
|
||||
836F6F7818BDC2190095E648 /* Cstr.c in Sources */,
|
||||
83F1EE30245D4FC10076E182 /* imuse.c in Sources */,
|
||||
8346D98625BF83B300D1A8B0 /* compresswave_decoder.c in Sources */,
|
||||
836F6F1E18BDC2190095E648 /* acm_decoder.c in Sources */,
|
||||
836F6FD818BDC2190095E648 /* ps2_gbts.c in Sources */,
|
||||
831BA61A1EAC61A500CF89B0 /* ps2_vds_vdm.c in Sources */,
|
||||
|
@ -2564,6 +2594,7 @@
|
|||
8306B0A720984552000302D4 /* blocked_xvas.c in Sources */,
|
||||
8349A9101FE6258200E26435 /* ea_eaac.c in Sources */,
|
||||
836F700F18BDC2190095E648 /* ps2_xa30.c in Sources */,
|
||||
8346D97B25BF838C00D1A8B0 /* ktac.c in Sources */,
|
||||
8349A8ED1FE6253900E26435 /* blocked_ea_sns.c in Sources */,
|
||||
836F6F6F18BDC2190095E648 /* akb.c in Sources */,
|
||||
83EED5D6203A8BD7008BEB45 /* blocked_ea_swvr.c in Sources */,
|
||||
|
@ -2821,7 +2852,6 @@
|
|||
836F6FC918BDC2190095E648 /* ps2_2pfs.c in Sources */,
|
||||
83AFABBC23795202002F3947 /* xssb.c in Sources */,
|
||||
836F704818BDC2190095E648 /* xbox_ims.c in Sources */,
|
||||
837CEAF623487F2C00E62A4A /* mzrt.c in Sources */,
|
||||
83AA7F832519C042004C5298 /* adp_konami.c in Sources */,
|
||||
836F6F7518BDC2190095E648 /* bnsf.c in Sources */,
|
||||
836F704318BDC2190095E648 /* wpd.c in Sources */,
|
||||
|
@ -2847,6 +2877,8 @@
|
|||
836F6F7618BDC2190095E648 /* brstm.c in Sources */,
|
||||
836F700718BDC2190095E648 /* ps2_vgv.c in Sources */,
|
||||
836F704F18BDC2190095E648 /* xwb.c in Sources */,
|
||||
8346D98525BF83B300D1A8B0 /* compresswave_decoder_lib.c in Sources */,
|
||||
8346D97D25BF838C00D1A8B0 /* compresswave.c in Sources */,
|
||||
8306B0AD20984552000302D4 /* blocked_caf.c in Sources */,
|
||||
8306B0AA20984552000302D4 /* blocked_sthd.c in Sources */,
|
||||
836F6FE918BDC2190095E648 /* ps2_mihb.c in Sources */,
|
||||
|
|
|
@ -270,6 +270,17 @@ void seek_imuse(imuse_codec_data* data, int32_t num_sample);
|
|||
void free_imuse(imuse_codec_data* data);
|
||||
|
||||
|
||||
/* compresswave_decoder */
|
||||
typedef struct compresswave_codec_data compresswave_codec_data;
|
||||
|
||||
compresswave_codec_data* init_compresswave(STREAMFILE* sf);
|
||||
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_compresswave(compresswave_codec_data* data);
|
||||
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample);
|
||||
void free_compresswave(compresswave_codec_data* data);
|
||||
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data);
|
||||
|
||||
|
||||
/* ea_mt_decoder*/
|
||||
typedef struct ea_mt_codec_data ea_mt_codec_data;
|
||||
|
||||
|
@ -512,6 +523,18 @@ void free_celt_fsb(celt_codec_data* data);
|
|||
#endif
|
||||
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
/* speex_decoder */
|
||||
typedef struct speex_codec_data speex_codec_data;
|
||||
|
||||
speex_codec_data* init_speex_ea(int channels);
|
||||
void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_speex(speex_codec_data* data);
|
||||
void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample);
|
||||
void free_speex(speex_codec_data* data);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* ffmpeg_decoder */
|
||||
ffmpeg_codec_data* init_ffmpeg_offset(STREAMFILE* sf, uint64_t start, uint64_t size);
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
#ifndef _CODING_UTILS_SAMPLES_
|
||||
#define _CODING_UTILS_SAMPLES_
|
||||
|
||||
/* sample helpers */
|
||||
//TODO maybe move to .c
|
||||
// (as .h can be inlined but these probably aren't called enough times that there is a notable boost)
|
||||
|
||||
typedef struct {
|
||||
int16_t* samples; /* current samples (pointer is moved once consumed) */
|
||||
int filled; /* samples left */
|
||||
int channels; /* max channels sample buf handles */
|
||||
//TODO may be more useful with filled+consumed and not moving *samples?
|
||||
} s16buf_t;
|
||||
|
||||
static void s16buf_silence(sample_t** p_outbuf, int32_t* p_samples_silence, int channels) {
|
||||
int samples_silence;
|
||||
|
||||
samples_silence = *p_samples_silence;
|
||||
|
||||
memset(*p_outbuf, 0, samples_silence * channels * sizeof(int16_t));
|
||||
|
||||
*p_outbuf += samples_silence * channels;
|
||||
*p_samples_silence -= samples_silence;
|
||||
}
|
||||
|
||||
static void s16buf_discard(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_discard) {
|
||||
int samples_discard;
|
||||
|
||||
samples_discard = *p_samples_discard;
|
||||
if (samples_discard > sbuf->filled)
|
||||
samples_discard = sbuf->filled;
|
||||
|
||||
/* just ignore part of samples */
|
||||
|
||||
sbuf->samples += samples_discard * sbuf->channels;
|
||||
sbuf->filled -= samples_discard;
|
||||
|
||||
*p_samples_discard -= samples_discard;
|
||||
}
|
||||
|
||||
/* copy, move and mark consumed samples */
|
||||
static void s16buf_consume(sample_t** p_outbuf, s16buf_t* sbuf, int32_t* p_samples_consume) {
|
||||
int samples_consume;
|
||||
|
||||
samples_consume = *p_samples_consume;
|
||||
if (samples_consume > sbuf->filled)
|
||||
samples_consume = sbuf->filled;
|
||||
|
||||
/* memcpy is safe when filled/samples_copy is 0 (but must pass non-NULL bufs) */
|
||||
memcpy(*p_outbuf, sbuf->samples, samples_consume * sbuf->channels * sizeof(int16_t));
|
||||
|
||||
sbuf->samples += samples_consume * sbuf->channels;
|
||||
sbuf->filled -= samples_consume;
|
||||
|
||||
*p_outbuf += samples_consume * sbuf->channels;
|
||||
*p_samples_consume -= samples_consume;
|
||||
}
|
||||
|
||||
|
||||
#endif /* _CODING_UTILS_SAMPLES_ */
|
|
@ -0,0 +1,138 @@
|
|||
#include "coding.h"
|
||||
#include "coding_utils_samples.h"
|
||||
#include "compresswave_decoder_lib.h"
|
||||
|
||||
|
||||
#define COMPRESSWAVE_MAX_FRAME_SAMPLES 0x1000 /* arbitrary but should be multiple of 2 for 22050 mode */
|
||||
|
||||
/* opaque struct */
|
||||
struct compresswave_codec_data {
|
||||
/* config */
|
||||
STREAMFILE* sf;
|
||||
TCompressWaveData* cw;
|
||||
|
||||
/* frame state */
|
||||
int16_t* samples;
|
||||
int frame_samples;
|
||||
|
||||
/* frame state */
|
||||
s16buf_t sbuf;
|
||||
int samples_discard;
|
||||
};
|
||||
|
||||
|
||||
compresswave_codec_data* init_compresswave(STREAMFILE* sf) {
|
||||
compresswave_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(compresswave_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->sf = reopen_streamfile(sf, 0);
|
||||
if (!data->sf) goto fail;
|
||||
|
||||
data->frame_samples = COMPRESSWAVE_MAX_FRAME_SAMPLES;
|
||||
data->samples = malloc(2 * data->frame_samples * sizeof(int16_t)); /* always stereo */
|
||||
if (!data->samples) goto fail;
|
||||
|
||||
|
||||
data->cw = TCompressWaveData_Create();
|
||||
if (!data->cw) goto fail;
|
||||
|
||||
TCompressWaveData_LoadFromStream(data->cw, data->sf);
|
||||
|
||||
reset_compresswave(data);
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_compresswave(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame(compresswave_codec_data* data, int32_t samples_to_do) {
|
||||
uint32_t Len;
|
||||
int ok;
|
||||
|
||||
data->sbuf.samples = data->samples;
|
||||
data->sbuf.channels = 2;
|
||||
data->sbuf.filled = 0;
|
||||
|
||||
if (samples_to_do > data->frame_samples)
|
||||
samples_to_do = data->frame_samples;
|
||||
if (samples_to_do % 2 && samples_to_do > 1)
|
||||
samples_to_do -= 1; /* 22khz does 2 samples at once */
|
||||
|
||||
Len = samples_to_do * sizeof(int16_t) * 2; /* forced stereo */
|
||||
|
||||
ok = TCompressWaveData_Rendering(data->cw, data->sbuf.samples, Len);
|
||||
if (!ok) goto fail;
|
||||
|
||||
data->sbuf.filled = samples_to_do;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void decode_compresswave(compresswave_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
|
||||
int ok;
|
||||
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
s16buf_t* sbuf = &data->sbuf;
|
||||
|
||||
if (sbuf->filled <= 0) {
|
||||
ok = decode_frame(data, samples_to_do);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
if (data->samples_discard)
|
||||
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
|
||||
else
|
||||
s16buf_consume(&outbuf, sbuf, &samples_to_do);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
VGM_LOG("COMPRESSWAVE: decode fail, missing %i samples\n", samples_to_do);
|
||||
s16buf_silence(&outbuf, &samples_to_do, 2);
|
||||
}
|
||||
|
||||
|
||||
void reset_compresswave(compresswave_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
/* actual way to reset internal flags */
|
||||
TCompressWaveData_Stop(data->cw);
|
||||
TCompressWaveData_Play(data->cw, 0);
|
||||
|
||||
data->sbuf.filled = 0;
|
||||
data->samples_discard = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void seek_compresswave(compresswave_codec_data* data, int32_t num_sample) {
|
||||
if (!data) return;
|
||||
|
||||
reset_compresswave(data);
|
||||
data->samples_discard += num_sample;
|
||||
}
|
||||
|
||||
void free_compresswave(compresswave_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
TCompressWaveData_Free(data->cw);
|
||||
|
||||
close_streamfile(data->sf);
|
||||
free(data->samples);
|
||||
free(data);
|
||||
}
|
||||
|
||||
STREAMFILE* compresswave_get_streamfile(compresswave_codec_data* data) {
|
||||
if (!data) return NULL;
|
||||
return data->sf;
|
||||
}
|
|
@ -0,0 +1,998 @@
|
|||
#include "compresswave_decoder_lib.h"
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* Decodes CWav (CompressWave) audio codec, based on original delphi/pascal source code by Ko-Ta:
|
||||
* - http://kota.dokkoisho.com/
|
||||
* - http://kota.dokkoisho.com/library/CompressWave.zip
|
||||
* - https://web.archive.org/web/20180819144937/http://d.hatena.ne.jp/Ko-Ta/20070318/p1
|
||||
* (no license given)
|
||||
* Apparently found in few Japanese (doujin?) games around 1995-2002, most notably RADIO ZONDE.
|
||||
*
|
||||
* This is mostly a simple re-implementation following original code, basically Pascal-classes-to-plain-C
|
||||
* because why not. Only decoder part is replicated (some writting/loading/etc stuff removed or cleaned up).
|
||||
* Results should be byte-exact (all is int math).
|
||||
* **some parts like internal looping weren't tested (no valid files)
|
||||
*
|
||||
* Codec is basically huffman-coded ADPCM, that includes diff table and huffman tree setup in
|
||||
* CWav header. Described by the author as being "big, heavy and with bad sound quality".
|
||||
* An oddity is that mono files are output as fake stereo (repeats L/R), this is correct and agrees
|
||||
* with PCM totals in header. Output sample rate is always 44100 and files marked as 22050 or mono just
|
||||
* decode slightly differently. Curiously PCM output size in header may be not be multiple of 4, meaning
|
||||
* files that end with garbage half-a-sample (L sample = 0, nothing for R).
|
||||
*/
|
||||
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* common */
|
||||
/* ************************************************************************* */
|
||||
// pascal reader simulated in C
|
||||
typedef struct {
|
||||
STREAMFILE* File;
|
||||
int64_t Position;
|
||||
int64_t Size;
|
||||
} TStream;
|
||||
|
||||
static void TStream_Read_Uint32(TStream* this, uint32_t* value) {
|
||||
uint8_t buf[0x4] = {0};
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += 0x4;
|
||||
|
||||
*value = get_u32le(buf);
|
||||
}
|
||||
|
||||
|
||||
/* ************************************************************************* */
|
||||
/* HuffLib.pas */
|
||||
/* ************************************************************************* */
|
||||
|
||||
#define CW_TRUE 1
|
||||
#define CW_FALSE 0
|
||||
|
||||
typedef enum { nsEmpty, nsBranch, nsLeaf, nsRoot } TNodeState;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//structure declaration
|
||||
|
||||
//node structure for huffman tree
|
||||
typedef struct {
|
||||
uint8_t Value; //value (0..255)
|
||||
int32_t Weight; //weight value used during huffman tree creation
|
||||
TNodeState State; //state
|
||||
int32_t Link[2]; //bidimensional tree L/R path (-1: unused, >=0: index)
|
||||
} THuffTreeNode;
|
||||
|
||||
//header info for file writting
|
||||
typedef struct {
|
||||
char HedChar[4]; //head info
|
||||
int32_t Version; //Version
|
||||
uint32_t HistGraph[256]; //appearance rate
|
||||
int64_t FileSize; //file size
|
||||
} THuffHedState;
|
||||
|
||||
//for jumping to arbitrary places (^^), various usages
|
||||
typedef struct {
|
||||
uint32_t BitBuf;
|
||||
int32_t BitCount;
|
||||
int64_t StreamPos;
|
||||
uint32_t CipherBuf;
|
||||
} THuffPositionData;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//huffman encoding class
|
||||
//handled values are 0~255 of 1 byte.
|
||||
//takes appearance rate and makes a huffman tree for encoding
|
||||
|
||||
//EXTRA: external lib part, but not really needed so all is static
|
||||
|
||||
typedef struct {
|
||||
//control
|
||||
TStream Buff; // target stream
|
||||
int64_t BeginPos; // encoded area
|
||||
int Mode; // (0=initial, 1=read, 2=write)
|
||||
|
||||
//bit IO
|
||||
int IoCount; // 0..initial state, 1..read, 2..write
|
||||
uint32_t BitBuf; // held buffer
|
||||
int BitCount; // processed bit count
|
||||
#if 0
|
||||
int64_t BitWriteLen; // written size
|
||||
#endif
|
||||
uint32_t CipherBuf;
|
||||
|
||||
//huffman
|
||||
THuffTreeNode Node[512]; //tree structure
|
||||
uint8_t Code[256][256]; //fork support
|
||||
int32_t Root; //root
|
||||
|
||||
//huffman cipher bits
|
||||
uint32_t CipherList[16];
|
||||
|
||||
//header info
|
||||
THuffHedState Hed;
|
||||
} THuff;
|
||||
|
||||
|
||||
//related to huffman encoding
|
||||
static void THuff_InitHuffTree(THuff* this); //initializes tree
|
||||
static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2); //add node to tree
|
||||
static void THuff_MakeHuffTree(THuff* this);
|
||||
|
||||
//related to single bit IO
|
||||
static void THuff_BeginBitIO(THuff* this);
|
||||
static void THuff_EndBitIO(THuff* this);
|
||||
static int THuff_ReadBit(THuff* this);
|
||||
static uint32_t THuff__ROR(uint32_t src, uint32_t shift);
|
||||
|
||||
static THuff* THuff_Create(TStream* buf); // creation
|
||||
static void THuff_Free(THuff* this); // release the power
|
||||
static void THuff_SetCipherCode(THuff* this, uint32_t msk); // encryption mask bits
|
||||
//functions for reading
|
||||
static void THuff_BeginRead(THuff* this);
|
||||
static int THuff_Read(THuff* this);
|
||||
|
||||
#if 0
|
||||
static int64_t THuff_GetFileSize(THuff* this); // get file size before encoding
|
||||
static int THuff_GetEOF(THuff* this); // EOF detection
|
||||
#endif
|
||||
static void THuff_MoveBeginPosition(THuff* this); // return to initial state
|
||||
static void THuff_GetPositionData(THuff* this, THuffPositionData* s); // secret
|
||||
static void THuff_SetPositionData(THuff* this, THuffPositionData* s);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//create
|
||||
static THuff* THuff_Create(TStream* buf) {
|
||||
THuff* this = malloc(sizeof(THuff));
|
||||
if (!this) return NULL;
|
||||
|
||||
//define stream
|
||||
this->Buff = *buf;
|
||||
|
||||
//initialization
|
||||
THuff_InitHuffTree(this);
|
||||
memcpy(this->Hed.HedChar, "HUF\0", 0x4);
|
||||
this->Hed.Version = 1;
|
||||
this->Hed.FileSize = 0;
|
||||
|
||||
//set cipher bits
|
||||
this->CipherBuf = 0;
|
||||
THuff_SetCipherCode(this, 0x0);
|
||||
|
||||
//mode
|
||||
this->Mode = 0;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//free
|
||||
static void THuff_Free(THuff* this) {
|
||||
if (this == NULL) return;
|
||||
if (this->Mode == 2)
|
||||
THuff_EndBitIO(this);
|
||||
free(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//init tree structure (unused state)
|
||||
static void THuff_InitHuffTree(THuff* this) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 512; i++) {
|
||||
this->Node[i].State = nsEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//add node to huffman tree
|
||||
static int THuff_InsertHuffNode(THuff* this, int v, int w, TNodeState s, int b1, int b2) {
|
||||
int result = 0;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while ((this->Node[i].State != nsEmpty) && (i < 512)) {
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == 512) {
|
||||
result = -1;
|
||||
return result; //exit;
|
||||
}
|
||||
|
||||
this->Node[i].Value = v & 0xFF; //BYTE(v);
|
||||
this->Node[i].Weight = w;
|
||||
this->Node[i].State = s;
|
||||
this->Node[i].Link[0] = b1;
|
||||
if (this->Node[i].Link[0] > 511) {
|
||||
return -1;//? //halt;
|
||||
}
|
||||
this->Node[i].Link[1] = b2;
|
||||
if (this->Node[i].Link[1] > 511) {
|
||||
return -1;//? //halt;
|
||||
}
|
||||
//return entry number
|
||||
result = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//reads and expands huffman-encoded data
|
||||
static int THuff_Read(THuff* this) {
|
||||
int i;
|
||||
|
||||
i = this->Root;
|
||||
while (this->Node[i].State != nsLeaf) {
|
||||
i = this->Node[i].Link[THuff_ReadBit(this)];
|
||||
}
|
||||
|
||||
return this->Node[i].Value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//creates fork code from tree
|
||||
|
||||
//finds node of lowest weight
|
||||
static int THuff_MakeHuffTree_SerchMinNode(THuff* this, int* tNode) {
|
||||
int ii, aaa1, aaa2;
|
||||
|
||||
aaa1 = 0xFFFFFFF;
|
||||
aaa2 = 0;
|
||||
for (ii = 0 ; ii < 256; ii++) {
|
||||
if (tNode[ii] != -1) {
|
||||
if (this->Node[tNode[ii]].Weight < aaa1) {
|
||||
aaa2 = ii;
|
||||
aaa1 = this->Node[tNode[ii]].Weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aaa2;
|
||||
}
|
||||
|
||||
//finds closest node
|
||||
static int THuff_MakeHuffTree_SerchNearNode(THuff* this, int* tNode, int pos) {
|
||||
int ii, aaa1, aaa2;
|
||||
|
||||
aaa1 = 0xFFFFFFF;
|
||||
aaa2 = 0;
|
||||
for (ii = 0 ; ii < 256; ii++) {
|
||||
if (tNode[ii] != -1) {
|
||||
if ((abs(this->Node[tNode[ii]].Weight - this->Node[tNode[pos]].Weight) < aaa1) && (pos != ii)) {
|
||||
aaa2 = ii;
|
||||
aaa1 = this->Node[tNode[ii]].Weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aaa2;
|
||||
}
|
||||
|
||||
static void THuff_MakeHuffTree_MakeHuffCodeFromTree(THuff* this, uint8_t* tCode1, int* tCodePos, int pos) {
|
||||
int ii, aaa1;
|
||||
|
||||
if (this->Node[pos].State == nsLeaf) { //found
|
||||
tCode1[*tCodePos] = 0xFF;
|
||||
aaa1 = this->Node[pos].Value;
|
||||
for (ii = 0; ii < 256; ii++) {
|
||||
this->Code[aaa1][ii] = tCode1[ii];
|
||||
}
|
||||
}
|
||||
else { //not
|
||||
if (this->Node[pos].Link[0] != -1) {
|
||||
tCode1[*tCodePos] = 0;
|
||||
(*tCodePos)++;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[0]);
|
||||
}
|
||||
|
||||
if (this->Node[pos].Link[1] != -1) {
|
||||
tCode1[*tCodePos] = 1;
|
||||
(*tCodePos)++;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, tCodePos, this->Node[pos].Link[1]);
|
||||
}
|
||||
}
|
||||
|
||||
(*tCodePos)--;
|
||||
}
|
||||
|
||||
// creates huffman tree/codes from apparance rate (0..255)
|
||||
static void THuff_MakeHuffTree(THuff* this) {
|
||||
int i, aa1, aa2, aa3;
|
||||
int tCodePos;
|
||||
uint8_t tCode1[257];
|
||||
#if 0
|
||||
uint8_t tCode2[257];
|
||||
#endif
|
||||
int tNode[257];
|
||||
|
||||
//initializes huffman tree
|
||||
THuff_InitHuffTree(this);
|
||||
for (i = 0; i < 256; i++) {
|
||||
tNode[i] = -1;
|
||||
tCode1[i] = 0;
|
||||
#if 0
|
||||
tCode2[i] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
//adds child nodes + comparison target nodes
|
||||
for (i = 0; i < 256; i++) {
|
||||
tNode[i] = THuff_InsertHuffNode(this, i, this->Hed.HistGraph[i], nsLeaf, -1, -1);
|
||||
}
|
||||
|
||||
//creates optimal tree
|
||||
for (i = 0; i < 256 - 1; i++) {
|
||||
//find smallest node
|
||||
aa1 = THuff_MakeHuffTree_SerchMinNode(this, tNode);
|
||||
//find value closest to smallest node
|
||||
aa2 = THuff_MakeHuffTree_SerchNearNode(this, tNode, aa1);
|
||||
//make new node joining both together
|
||||
aa3 = THuff_InsertHuffNode(this, -1, this->Node[tNode[aa1]].Weight + this->Node[tNode[aa2]].Weight, nsBranch, tNode[aa1], tNode[aa2]);
|
||||
//remove aa1/2 from comparison target nodes.
|
||||
tNode[aa1] = -1;
|
||||
tNode[aa2] = -1;
|
||||
//add created node to comparison target nodes
|
||||
tNode[aa1] = aa3;
|
||||
}
|
||||
|
||||
//finally make added node top of the tree
|
||||
this->Root = aa3;
|
||||
|
||||
//create stack for data expansion from tree info
|
||||
tCodePos = 0;
|
||||
THuff_MakeHuffTree_MakeHuffCodeFromTree(this, tCode1, &tCodePos, this->Root);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//bit IO start process
|
||||
static void THuff_BeginBitIO(THuff* this) {
|
||||
this->IoCount = 0;
|
||||
this->BitBuf = 0;
|
||||
this->BitCount = 32;
|
||||
#if 0
|
||||
this->BitWriteLen = 0;
|
||||
#endif
|
||||
this->CipherBuf = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//bit IO end process
|
||||
static void THuff_EndBitIO(THuff* this) {
|
||||
#if 0
|
||||
if (this->IoCount == 2 && this->BitCount > 0) {
|
||||
this->BitBuf = this->BitBuf ^ this->CipherBuf;
|
||||
TStream_Write(this->Buff, BitBuf,4);
|
||||
}
|
||||
#endif
|
||||
THuff_BeginBitIO(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//read 1 bit from file
|
||||
static int THuff_ReadBit(THuff* this) {
|
||||
int result;
|
||||
uint32_t aaa;
|
||||
|
||||
if (this->BitCount == 32) {
|
||||
this->IoCount = 1; //ReadMode
|
||||
if (this->Buff.Position < this->Buff.Size) {
|
||||
//read
|
||||
TStream_Read_Uint32(&this->Buff, &aaa); //Buff.Read(aaa,sizeof(DWORD));
|
||||
this->BitBuf = aaa ^ this->CipherBuf;
|
||||
|
||||
//decryption phase
|
||||
this->CipherBuf = THuff__ROR(this->CipherBuf, aaa & 7);
|
||||
this->CipherBuf = this->CipherBuf ^ this->CipherList[aaa & 7];
|
||||
}
|
||||
this->BitCount = 0;
|
||||
}
|
||||
|
||||
//return 1 bit
|
||||
result = this->BitBuf & 1;
|
||||
this->BitBuf = this->BitBuf >> 1;
|
||||
|
||||
//advance BitCount
|
||||
this->BitCount++;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//starts reading encoded data from stream
|
||||
|
||||
static void TStream_Read_THuffHedState(TStream* this, THuffHedState* Hed) {
|
||||
uint8_t buf[0x410];
|
||||
int i;
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += sizeof(buf);
|
||||
|
||||
/* 0x00: string size (always 3) */
|
||||
memcpy(Hed->HedChar, buf+0x01, 0x03);
|
||||
Hed->Version = get_u32le(buf+0x04);
|
||||
for (i = 0; i < 256; i++) {
|
||||
Hed->HistGraph[i] = get_u32le(buf+0x08 + i*0x04);
|
||||
}
|
||||
Hed->FileSize = get_u64le(buf+0x408); /* seems always 0 */
|
||||
}
|
||||
|
||||
static void THuff_BeginRead(THuff* this) {
|
||||
TStream_Read_THuffHedState(&this->Buff, &this->Hed); //Buff.Read(Hed,sizeof(THuffHedState));
|
||||
THuff_MakeHuffTree(this);
|
||||
this->BeginPos = this->Buff.Position;
|
||||
THuff_BeginBitIO(this);
|
||||
this->Mode = 1;
|
||||
}
|
||||
|
||||
#if 0
|
||||
//------------------------------------------------------------------------------
|
||||
//get file size before encoding
|
||||
static int64_t THuff_GetFileSize(THuff* this) {
|
||||
return this->Hed.FileSize;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//EOF detection
|
||||
static int THuff_GetEOF(THuff* this) {
|
||||
if (this->Buff.Position < this->Buff.Size)
|
||||
return CW_FALSE;
|
||||
else
|
||||
return CW_TRUE;
|
||||
}
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
//return to initial positon
|
||||
static void THuff_MoveBeginPosition(THuff* this) {
|
||||
THuff_EndBitIO(this);
|
||||
this->Buff.Position = this->BeginPos;
|
||||
THuff_BeginBitIO(this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_GetPositionData(THuff* this, THuffPositionData* s) {
|
||||
s->BitBuf = this->BitBuf;
|
||||
s->BitCount = this->BitCount;
|
||||
s->StreamPos = this->Buff.Position;
|
||||
s->CipherBuf = this->CipherBuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_SetPositionData(THuff* this, THuffPositionData* s) {
|
||||
this->BitBuf = s->BitBuf;
|
||||
this->BitCount = s->BitCount;
|
||||
this->Buff.Position = s->StreamPos;
|
||||
this->CipherBuf = s->CipherBuf;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static void THuff_SetCipherCode(THuff* this, uint32_t msk) {
|
||||
//creates mask list
|
||||
this->CipherList[0] = msk / 3;
|
||||
this->CipherList[1] = msk / 17;
|
||||
this->CipherList[2] = msk / 7;
|
||||
this->CipherList[3] = msk / 5;
|
||||
this->CipherList[4] = msk / 3;
|
||||
this->CipherList[5] = msk / 11;
|
||||
this->CipherList[6] = msk / 13;
|
||||
this->CipherList[7] = msk / 19;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
static uint32_t THuff__ROR(uint32_t src, uint32_t shift) {
|
||||
uint8_t num = shift % 0xFF;
|
||||
return ((uint32_t)src >> num) | ((uint32_t)src << (32 - num));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
// CompressWaveLib.pas
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
#define PW_MAXVOLUME 0xFFFFFFF //don't change
|
||||
|
||||
|
||||
//proprietary compression file header
|
||||
typedef struct {
|
||||
//RIFF chunk
|
||||
char HedChar[8]; // 'CmpWave'
|
||||
uint32_t Channel; // 2(STEREO) / 1(MONO)
|
||||
uint32_t Sample; // 44100Hz / 22050Hz
|
||||
uint32_t Bit; // 16bit
|
||||
int32_t Tbl[256]; // conversion table value
|
||||
int64_t UnPressSize; // decompressed data size
|
||||
int64_t LoopStart; // loop start/end position
|
||||
int64_t LoopEnd;
|
||||
uint8_t LoopCount; // loop times
|
||||
char MusicTitle[128*2]; // song name
|
||||
char MusicArtist[128*2]; // composer
|
||||
} PRESSWAVEDATAHED;
|
||||
|
||||
//for writting
|
||||
typedef struct {
|
||||
short RBuf;
|
||||
short LBuf;
|
||||
} TLRWRITEBUFFER;
|
||||
|
||||
|
||||
//compression data class
|
||||
struct TCompressWaveData {
|
||||
//rendering flag (sets during rendering)
|
||||
int NowRendering;
|
||||
//flag for playback
|
||||
int32_t Faa1;
|
||||
int32_t Faa2;
|
||||
int32_t Fvv1;
|
||||
int32_t Fvv2;
|
||||
|
||||
int32_t FVolume;
|
||||
int32_t Ffade;
|
||||
int32_t FSetVolume;
|
||||
|
||||
int FEndLoop;
|
||||
int32_t FLoop;
|
||||
int FPlay;
|
||||
int64_t FWavePosition;
|
||||
int64_t FWaveLength;
|
||||
//flag for 22050kHz
|
||||
int32_t LBackBuf;
|
||||
int32_t RBackBuf;
|
||||
//for restoration
|
||||
THuffPositionData PosData;
|
||||
int32_t LPFaa1;
|
||||
int32_t LPFaa2;
|
||||
int32_t LPFvv1;
|
||||
int32_t LPFvv2;
|
||||
//cipher code
|
||||
uint32_t CipherCode;
|
||||
//hafu-hafu-hafuman
|
||||
THuff* RH;
|
||||
|
||||
#if 0
|
||||
TMemoryStream Data;
|
||||
#endif
|
||||
PRESSWAVEDATAHED Hed;
|
||||
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//create
|
||||
TCompressWaveData* TCompressWaveData_Create() {
|
||||
TCompressWaveData* this = malloc(sizeof(TCompressWaveData));
|
||||
if (!this) return NULL;
|
||||
#if 0
|
||||
this->Data = NULL;
|
||||
#endif
|
||||
this->RH = NULL;
|
||||
this->FWavePosition = 0;
|
||||
this->FWaveLength = 0;
|
||||
this->FVolume = PW_MAXVOLUME;
|
||||
this->FSetVolume = PW_MAXVOLUME;
|
||||
this->Ffade = 0;
|
||||
this->FEndLoop = CW_FALSE;
|
||||
this->FPlay = CW_FALSE;
|
||||
this->NowRendering = CW_FALSE;
|
||||
TCompressWaveData_SetCipherCode(this, 0);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//free
|
||||
void TCompressWaveData_Free(TCompressWaveData* this) {
|
||||
if (!this)
|
||||
return;
|
||||
|
||||
//EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors
|
||||
#if 0
|
||||
//sync
|
||||
while (this->NowRendering) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
//free
|
||||
if (this->RH != NULL)
|
||||
THuff_Free(this->RH);
|
||||
#if 0
|
||||
if (this->Data != NULL)
|
||||
TMemoryStream_Free(this->Data);
|
||||
#endif
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//outpus 44100/16bit/stereo waveform to designed buffer
|
||||
|
||||
void TCompressWaveData_Rendering_ReadPress(TCompressWaveData* this, int32_t* RFlg, int32_t* LFlg) {
|
||||
if (this->Hed.Channel == 2) {
|
||||
*RFlg = THuff_Read(this->RH); //STEREO
|
||||
*LFlg = THuff_Read(this->RH);
|
||||
}
|
||||
else {
|
||||
*RFlg = THuff_Read(this->RH); //MONO
|
||||
*LFlg = *RFlg;
|
||||
}
|
||||
}
|
||||
|
||||
void TCompressWaveData_Rendering_WriteWave(TCompressWaveData* this, int16_t** buf1, int32_t RVol, int32_t LVol) {
|
||||
TLRWRITEBUFFER bbb = {0};
|
||||
|
||||
if (this->Hed.Sample == 44100) { //44100 STEREO/MONO
|
||||
bbb.RBuf = RVol;
|
||||
bbb.LBuf = LVol;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
}
|
||||
if (this->Hed.Sample == 22050) { //22050 STEREO/MONO
|
||||
bbb.RBuf = (this->RBackBuf + RVol) / 2;
|
||||
bbb.LBuf = (this->LBackBuf + LVol) / 2;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
|
||||
bbb.RBuf = RVol;
|
||||
bbb.LBuf = LVol;
|
||||
(*buf1)[0] = bbb.RBuf;
|
||||
(*buf1)[1] = bbb.LBuf;
|
||||
(*buf1) += 2;
|
||||
|
||||
this->RBackBuf = RVol;
|
||||
this->LBackBuf = LVol;
|
||||
}
|
||||
}
|
||||
|
||||
int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len) {
|
||||
int result;
|
||||
int32_t RFlg, LFlg, RVol, LVol;
|
||||
int i, aaa;
|
||||
int16_t* buf1;
|
||||
int32_t PressLength, WaveStep;
|
||||
|
||||
|
||||
this->NowRendering = CW_TRUE;
|
||||
result = CW_FALSE;
|
||||
#if 0
|
||||
if (this->Data == NULL) {
|
||||
this->NowRendering = CW_FALSE;
|
||||
return result; //exit;
|
||||
}
|
||||
#endif
|
||||
|
||||
//fadeout song stop
|
||||
if ((this->FVolume < 1) && (this->FSetVolume < 1)) {
|
||||
this->FPlay = CW_FALSE;
|
||||
}
|
||||
//if (abs(this->FSetVolume - this->FVolume) < this->Ffade) {
|
||||
// this->FPlay = CW_FALSE;
|
||||
//}
|
||||
|
||||
//stop if FPlay (play flag) wasn't set
|
||||
if (this->FPlay == CW_FALSE) {
|
||||
this->NowRendering = CW_FALSE;
|
||||
return result; //exit;
|
||||
}
|
||||
|
||||
//pre processing
|
||||
RVol = this->Fvv1;
|
||||
LVol = this->Fvv2;
|
||||
if (this->Hed.Sample == 44100)
|
||||
WaveStep = 4;
|
||||
else
|
||||
WaveStep = 8;
|
||||
|
||||
PressLength = (int32_t)Len / WaveStep;
|
||||
|
||||
//expansion processing
|
||||
buf1 = buf;
|
||||
for (i = 0; i < PressLength; i++) {
|
||||
|
||||
//crossed over?
|
||||
if (this->FWavePosition > this->FWaveLength) {
|
||||
if (this->FEndLoop == CW_TRUE) { //playback with loop?
|
||||
TCompressWaveData_Previous(this);
|
||||
}
|
||||
else { //in case of playback without loop
|
||||
this->FPlay = CW_FALSE;
|
||||
return result; //exit
|
||||
}
|
||||
}
|
||||
|
||||
//loop related
|
||||
if (this->Hed.LoopCount > this->FLoop) {
|
||||
//if position is loop start, hold current flag/state
|
||||
//shr 3 matches 8 bit aligment
|
||||
if ((this->Hed.LoopStart >> 3) == (this->FWavePosition >> 3)) {
|
||||
TCompressWaveData_GetLoopState(this);
|
||||
}
|
||||
//if reached loop end do loop.
|
||||
if ((this->Hed.LoopEnd >> 3) == (this->FWavePosition >> 3)) {
|
||||
if (this->Hed.LoopCount != 255)
|
||||
this->FLoop++;
|
||||
TCompressWaveData_SetLoopState(this);
|
||||
}
|
||||
}
|
||||
|
||||
//read
|
||||
TCompressWaveData_Rendering_ReadPress(this, &RFlg, &LFlg);
|
||||
this->Faa1 = this->Faa1 + this->Hed.Tbl[RFlg];
|
||||
this->Faa2 = this->Faa2 + this->Hed.Tbl[LFlg];
|
||||
this->Fvv1 = this->Fvv1 + this->Faa1;
|
||||
this->Fvv2 = this->Fvv2 + this->Faa2;
|
||||
|
||||
//volume adjustment
|
||||
aaa = this->FSetVolume - this->FVolume;
|
||||
if (abs(aaa) < this->Ffade) {
|
||||
this->FVolume = this->FSetVolume;
|
||||
}
|
||||
else {
|
||||
if (aaa > 0)
|
||||
this->FVolume = this->FVolume + this->Ffade;
|
||||
else
|
||||
this->FVolume = this->FVolume - this->Ffade;
|
||||
}
|
||||
|
||||
//threshold calcs (due to overflow)
|
||||
if (this->Fvv1 > +32760) {
|
||||
this->Fvv1 = +32760;
|
||||
this->Faa1 = 0;
|
||||
}
|
||||
if (this->Fvv1 < -32760) {
|
||||
this->Fvv1 = -32760;
|
||||
this->Faa1 = 0;
|
||||
}
|
||||
if (this->Fvv2 > +32760) {
|
||||
this->Fvv2 = +32760;
|
||||
this->Faa2 = 0;
|
||||
}
|
||||
if (this->Fvv2 < -32760) {
|
||||
this->Fvv2 = -32760;
|
||||
this->Faa2 = 0;
|
||||
}
|
||||
|
||||
aaa = (this->FVolume >> 20);
|
||||
RVol = this->Fvv1 * aaa / 256;
|
||||
LVol = this->Fvv2 * aaa / 256;
|
||||
|
||||
//expand to buffer
|
||||
TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol);
|
||||
//advance playback position
|
||||
this->FWavePosition += WaveStep;
|
||||
}
|
||||
|
||||
//remainder calcs
|
||||
//depending on buffer lenght remainder may happen
|
||||
//example: 44100 / 4 = 11025...OK 44100 / 8 = 5512.5...NG
|
||||
// in that case appear as noise
|
||||
if (Len % 8 == 4) {
|
||||
TCompressWaveData_Rendering_WriteWave(this, &buf1, RVol, LVol);
|
||||
}
|
||||
|
||||
this->NowRendering = CW_FALSE;
|
||||
result = CW_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//read compressed file from stream
|
||||
|
||||
static void TStream_Read_PRESSWAVEDATAHED(TStream* this, PRESSWAVEDATAHED* Hed) {
|
||||
uint8_t buf[0x538];
|
||||
int i, len;
|
||||
|
||||
read_streamfile(buf, this->Position, sizeof(buf), this->File);
|
||||
this->Position += sizeof(buf);
|
||||
|
||||
memcpy(Hed->HedChar, buf + 0x00, 8);
|
||||
Hed->Channel = get_u32le(buf + 0x08);
|
||||
Hed->Sample = get_u32le(buf + 0x0c);
|
||||
Hed->Bit = get_u32le(buf + 0x10);
|
||||
for (i = 0; i < 256; i++) {
|
||||
Hed->Tbl[i] = get_s32le(buf + 0x14 + i * 0x04);
|
||||
}
|
||||
Hed->UnPressSize = get_u64le(buf + 0x418);
|
||||
Hed->LoopStart = get_u64le(buf + 0x420);
|
||||
Hed->LoopEnd = get_u64le(buf + 0x428);
|
||||
Hed->LoopCount = get_u8 (buf + 0x430);
|
||||
|
||||
len = get_u8 (buf + 0x431);
|
||||
memcpy(Hed->MusicTitle, buf + 0x432, len);
|
||||
len = get_u8 (buf + 0x4B1);
|
||||
memcpy(Hed->MusicArtist, buf + 0x4B2, len);
|
||||
|
||||
/* 0x538: huffman table */
|
||||
/* 0x948: data start */
|
||||
}
|
||||
|
||||
int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss) {
|
||||
int result = CW_FALSE;
|
||||
TStream data = {0};
|
||||
|
||||
if (ss == NULL)
|
||||
return result;
|
||||
#if 0
|
||||
if (this->Data != NULL)
|
||||
TMemoryStream_Free(this->Data);
|
||||
#endif
|
||||
|
||||
data.File = ss; //data = TMemoryStream.Create;
|
||||
data.Size = get_streamfile_size(ss); //data.SetSize(ss.Size);
|
||||
//data.CopyFrom(ss,0);
|
||||
|
||||
//get header info
|
||||
data.Position = 0;
|
||||
|
||||
TStream_Read_PRESSWAVEDATAHED(&data, &this->Hed); //data.Read(Hed,sizeof(PRESSWAVEDATAHED));
|
||||
this->FWaveLength = this->Hed.UnPressSize;
|
||||
if (this->RH != NULL)
|
||||
THuff_Free(this->RH);
|
||||
this->RH = THuff_Create(&data);
|
||||
if (!this->RH) return result;
|
||||
|
||||
THuff_SetCipherCode(this->RH, 0x00);
|
||||
THuff_BeginRead(this->RH);
|
||||
|
||||
//initialize playback flag
|
||||
TCompressWaveData_Stop(this);
|
||||
TCompressWaveData_SetVolume(this, 1.0, 0.0);
|
||||
result = CW_TRUE;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//temp pause
|
||||
void TCompressWaveData_Pause(TCompressWaveData* this) {
|
||||
this->FPlay = CW_FALSE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//sets volume
|
||||
void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade) {
|
||||
float aaa;
|
||||
|
||||
//EXTRA: C float seemingly can't store PW_MAXVOLUME (268435455 becomes 268435456.0), so must cast to double
|
||||
// to get proper results. Otherwise volume gets slightly different vs original (no casting needed there).
|
||||
// vol=1.0, fade=0.0 is the same as default params.
|
||||
|
||||
aaa = vol;
|
||||
//set volume threshold
|
||||
if (aaa > 1.0) aaa = 1.0;
|
||||
if (aaa < 0.0) aaa = 0.0;
|
||||
//calc volume increse
|
||||
if (fade < 0.01) { //with fade value
|
||||
this->Ffade = 0;
|
||||
this->FVolume = round(aaa * (double)PW_MAXVOLUME);
|
||||
this->FSetVolume = this->FVolume;
|
||||
}
|
||||
else { //without fade value
|
||||
this->Ffade = round(PW_MAXVOLUME / fade / 44100);
|
||||
this->FSetVolume = round(aaa * PW_MAXVOLUME);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns fade value
|
||||
float TCompressWaveData_GetFade(TCompressWaveData* this) {
|
||||
if ((this->Ffade == 0) || (abs(this->FVolume - this->FSetVolume) == 0)) {
|
||||
return 0; //exit;
|
||||
}
|
||||
return (abs(this->FVolume - this->FSetVolume)/44100) / this->Ffade;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns volume value
|
||||
float TCompressWaveData_GetVolume(TCompressWaveData* this) {
|
||||
return this->FVolume / PW_MAXVOLUME;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//returns volume after fade
|
||||
float TCompressWaveData_GetSetVolume(TCompressWaveData* this) {
|
||||
return this->FSetVolume / PW_MAXVOLUME;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//returns play time (current position). unit is secs
|
||||
float TCompressWaveData_GetPlayTime(TCompressWaveData* this) {
|
||||
return this->FWavePosition / (44100*4);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns song length. unit is secs
|
||||
float TCompressWaveData_GetTotalTime(TCompressWaveData* this) {
|
||||
return this->FWaveLength / (44100*4);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//play stop command. returns song to beginning
|
||||
void TCompressWaveData_Stop(TCompressWaveData* this) {
|
||||
//play flags to initial state
|
||||
this->FWavePosition = 0;
|
||||
this->Fvv1 = 0;
|
||||
this->Faa1 = 0;
|
||||
this->Fvv2 = 0;
|
||||
this->Faa2 = 0;
|
||||
this->LBackBuf = 0;
|
||||
this->RBackBuf = 0;
|
||||
TCompressWaveData_SetVolume(this, 1.0, 0);
|
||||
this->FPlay = CW_FALSE;
|
||||
this->FLoop = 0;
|
||||
#if 0
|
||||
if (this->Data == NULL)
|
||||
return;
|
||||
#endif
|
||||
|
||||
//EXTRA: presumably for threading but OG lib doesn't properly set this to false on all errors
|
||||
#if 0
|
||||
//sync
|
||||
while (this->NowRendering) {
|
||||
;
|
||||
}
|
||||
#endif
|
||||
|
||||
//return to initial location
|
||||
THuff_MoveBeginPosition(this->RH);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//returns song to beginning. difference vs STOP is that fade isn't initialized
|
||||
void TCompressWaveData_Previous(TCompressWaveData* this) {
|
||||
//play flags to initial state
|
||||
this->FWavePosition = 0;
|
||||
this->Fvv1 = 0;
|
||||
this->Faa1 = 0;
|
||||
this->Fvv2 = 0;
|
||||
this->Faa2 = 0;
|
||||
this->LBackBuf = 0;
|
||||
this->RBackBuf = 0;
|
||||
this->FLoop = 0;
|
||||
|
||||
#if 0
|
||||
if (this->Data == NULL)
|
||||
return;
|
||||
#endif
|
||||
//return to initial location
|
||||
THuff_MoveBeginPosition(this->RH);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//starts song playback
|
||||
void TCompressWaveData_Play(TCompressWaveData* this, int loop) {
|
||||
this->FPlay = CW_TRUE;
|
||||
this->FEndLoop = loop;
|
||||
if ((this->FVolume == 0) && (this->FSetVolume == 0))
|
||||
TCompressWaveData_SetVolume(this, 1.0,0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
//set parameters for looping
|
||||
//--------------------------------------------------------------
|
||||
//record encoded file position
|
||||
//since it uses huffman needs to held those flags too
|
||||
void TCompressWaveData_GetLoopState(TCompressWaveData* this) {
|
||||
this->LPFaa1 = this->Faa1;
|
||||
this->LPFaa2 = this->Faa2;
|
||||
this->LPFvv1 = this->Fvv1;
|
||||
this->LPFvv2 = this->Fvv2;
|
||||
THuff_GetPositionData(this->RH, &this->PosData);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
//return to recorded encoded file position
|
||||
void TCompressWaveData_SetLoopState(TCompressWaveData* this) {
|
||||
this->Faa1 = this->LPFaa1;
|
||||
this->Faa2 = this->LPFaa2;
|
||||
this->Fvv1 = this->LPFvv1;
|
||||
this->Fvv2 = this->LPFvv2;
|
||||
THuff_SetPositionData(this->RH, &this->PosData);
|
||||
this->FWavePosition = this->Hed.LoopStart;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
//sets cipher code
|
||||
void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num) {
|
||||
this->CipherCode = Num;
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#ifndef _COMPRESSWAVE_DECODER_LIB_H
|
||||
#define _COMPRESSWAVE_DECODER_LIB_H
|
||||
#include "../streamfile.h"
|
||||
|
||||
typedef struct TCompressWaveData TCompressWaveData;
|
||||
|
||||
void TCompressWaveData_GetLoopState(TCompressWaveData* this);
|
||||
void TCompressWaveData_SetLoopState(TCompressWaveData* this);
|
||||
|
||||
TCompressWaveData* TCompressWaveData_Create();
|
||||
void TCompressWaveData_Free(TCompressWaveData* this);
|
||||
int TCompressWaveData_Rendering(TCompressWaveData* this, int16_t* buf, uint32_t Len);
|
||||
int TCompressWaveData_LoadFromStream(TCompressWaveData* this, STREAMFILE* ss);
|
||||
void TCompressWaveData_SetCipherCode(TCompressWaveData* this, uint32_t Num);
|
||||
|
||||
void TCompressWaveData_Play(TCompressWaveData* this, int loop);
|
||||
void TCompressWaveData_Stop(TCompressWaveData* this);
|
||||
void TCompressWaveData_Previous(TCompressWaveData* this);
|
||||
void TCompressWaveData_Pause(TCompressWaveData* this);
|
||||
void TCompressWaveData_SetVolume(TCompressWaveData* this, float vol, float fade);
|
||||
float TCompressWaveData_GetVolume(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetSetVolume(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetFade(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetPlayTime(TCompressWaveData* this);
|
||||
float TCompressWaveData_GetTotalTime(TCompressWaveData* this);
|
||||
|
||||
#endif /*_COMPRESSWAVE_DECODER_LIB_H */
|
|
@ -1,206 +1,205 @@
|
|||
#include "coding.h"
|
||||
|
||||
#include "ea_mt_decoder_utk.h"
|
||||
|
||||
/* Decodes EA MicroTalk (speech codec) using utkencode lib (slightly modified for vgmstream).
|
||||
* EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same
|
||||
* with different encoding parameters. Later revisions may have PCM blocks (rare).
|
||||
*
|
||||
* Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode
|
||||
* Info: http://wiki.niotso.org/UTK
|
||||
*/
|
||||
|
||||
|
||||
//#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||
#define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
|
||||
#define UTK_MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define UTK_MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max)
|
||||
|
||||
#define UTK_BUFFER_SIZE 0x1000
|
||||
|
||||
struct ea_mt_codec_data {
|
||||
STREAMFILE *streamfile;
|
||||
uint8_t buffer[UTK_BUFFER_SIZE];
|
||||
off_t offset;
|
||||
off_t loop_offset;
|
||||
int loop_sample;
|
||||
|
||||
int pcm_blocks;
|
||||
int samples_filled;
|
||||
int samples_used;
|
||||
int samples_done;
|
||||
int samples_discard;
|
||||
void* utk_context;
|
||||
};
|
||||
|
||||
static size_t ea_mt_read_callback(void *dest, int size, void *arg);
|
||||
|
||||
ea_mt_codec_data *init_ea_mt(int channels, int pcm_blocks) {
|
||||
return init_ea_mt_loops(channels, pcm_blocks, 0, NULL);
|
||||
}
|
||||
|
||||
ea_mt_codec_data *init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) {
|
||||
ea_mt_codec_data *data = NULL;
|
||||
int i;
|
||||
|
||||
data = calloc(channels, sizeof(ea_mt_codec_data)); /* one decoder per channel */
|
||||
if (!data) goto fail;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
data[i].utk_context = calloc(1, sizeof(UTKContext));
|
||||
if (!data[i].utk_context) goto fail;
|
||||
utk_init(data[i].utk_context);
|
||||
|
||||
data[i].pcm_blocks = pcm_blocks;
|
||||
data[i].loop_sample = loop_sample;
|
||||
if (loop_offsets)
|
||||
data[i].loop_offset = loop_offsets[i];
|
||||
|
||||
utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_ea_mt(data, channels);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel) {
|
||||
int i;
|
||||
ea_mt_codec_data *data = vgmstream->codec_data;
|
||||
ea_mt_codec_data *ch_data = &data[channel];
|
||||
UTKContext* ctx = ch_data->utk_context;
|
||||
int samples_done = 0;
|
||||
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
if (ch_data->samples_filled) {
|
||||
/* consume current frame */
|
||||
int samples_to_get = ch_data->samples_filled;
|
||||
|
||||
/* don't go past loop, to reset decoder */
|
||||
if (ch_data->loop_sample > 0 && ch_data->samples_done < ch_data->loop_sample &&
|
||||
ch_data->samples_done + samples_to_get > ch_data->loop_sample)
|
||||
samples_to_get = ch_data->loop_sample - ch_data->samples_done;
|
||||
|
||||
if (ch_data->samples_discard) {
|
||||
/* discard samples for looping */
|
||||
if (samples_to_get > ch_data->samples_discard)
|
||||
samples_to_get = ch_data->samples_discard;
|
||||
ch_data->samples_discard -= samples_to_get;
|
||||
}
|
||||
else {
|
||||
/* get max samples and copy */
|
||||
if (samples_to_get > samples_to_do - samples_done)
|
||||
samples_to_get = samples_to_do - samples_done;
|
||||
|
||||
for (i = ch_data->samples_used; i < ch_data->samples_used + samples_to_get; i++) {
|
||||
int pcm = UTK_ROUND(ctx->decompressed_frame[i]);
|
||||
outbuf[0] = (int16_t)UTK_CLAMP(pcm, -32768, 32767);
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
|
||||
samples_done += samples_to_get;
|
||||
}
|
||||
|
||||
/* mark consumed samples */
|
||||
ch_data->samples_used += samples_to_get;
|
||||
ch_data->samples_filled -= samples_to_get;
|
||||
ch_data->samples_done += samples_to_get;
|
||||
|
||||
/* Loops in EA-MT are done with fully separate intro/loop substreams. We must
|
||||
* notify the decoder when a new substream begins (even with looping disabled). */
|
||||
if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) {
|
||||
ch_data->samples_filled = 0;
|
||||
ch_data->samples_discard = 0;
|
||||
|
||||
/* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */
|
||||
ch_data->offset = ch_data->loop_offset;
|
||||
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
|
||||
utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* new frame */
|
||||
if (ch_data->pcm_blocks)
|
||||
utk_rev3_decode_frame(ctx);
|
||||
else
|
||||
utk_decode_frame(ctx);
|
||||
|
||||
ch_data->samples_used = 0;
|
||||
ch_data->samples_filled = 432;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_ea_mt_offsets(VGMSTREAM *vgmstream, int is_start, int samples_discard) {
|
||||
ea_mt_codec_data *data = vgmstream->codec_data;
|
||||
int i;
|
||||
|
||||
if (!data) return;
|
||||
|
||||
|
||||
/* EA-MT frames are VBR (not byte-aligned?), so utk_decoder reads new buffer data automatically.
|
||||
* When decoding starts or a SCHl block changes, flush_ea_mt must be called to reset the state.
|
||||
* A bit hacky but would need some restructuring otherwise. */
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
UTKContext* ctx = data[i].utk_context;
|
||||
|
||||
data[i].streamfile = vgmstream->ch[i].streamfile; /* maybe should keep its own STREAMFILE? */
|
||||
if (is_start)
|
||||
data[i].offset = vgmstream->ch[i].channel_start_offset;
|
||||
else
|
||||
data[i].offset = vgmstream->ch[i].offset;
|
||||
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
|
||||
|
||||
if (is_start) {
|
||||
utk_reset(ctx);
|
||||
ctx->parsed_header = 0;
|
||||
data[i].samples_done = 0;
|
||||
}
|
||||
|
||||
data[i].samples_filled = 0;
|
||||
data[i].samples_discard = samples_discard;
|
||||
}
|
||||
}
|
||||
|
||||
void flush_ea_mt(VGMSTREAM *vgmstream) {
|
||||
flush_ea_mt_offsets(vgmstream, 0, 0);
|
||||
}
|
||||
|
||||
void reset_ea_mt(VGMSTREAM *vgmstream) {
|
||||
flush_ea_mt_offsets(vgmstream, 1, 0);
|
||||
}
|
||||
|
||||
void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample) {
|
||||
flush_ea_mt_offsets(vgmstream, 1, num_sample);
|
||||
}
|
||||
|
||||
void free_ea_mt(ea_mt_codec_data *data, int channels) {
|
||||
int i;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
free(data[i].utk_context);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* ********************** */
|
||||
|
||||
static size_t ea_mt_read_callback(void *dest, int size, void *arg) {
|
||||
ea_mt_codec_data *ch_data = arg;
|
||||
int bytes_read;
|
||||
|
||||
bytes_read = read_streamfile(dest,ch_data->offset,size,ch_data->streamfile);
|
||||
ch_data->offset += bytes_read;
|
||||
|
||||
return bytes_read;
|
||||
|
||||
}
|
||||
#include "coding.h"
|
||||
|
||||
#include "ea_mt_decoder_utk.h"
|
||||
|
||||
/* Decodes EA MicroTalk (speech codec) using utkencode lib (slightly modified for vgmstream).
|
||||
* EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same
|
||||
* with different encoding parameters. Later revisions may have PCM blocks (rare).
|
||||
*
|
||||
* Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode
|
||||
* Info: http://wiki.niotso.org/UTK
|
||||
*/
|
||||
|
||||
|
||||
//#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
|
||||
#define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
|
||||
#define UTK_MIN(x,y) ((x)<(y)?(x):(y))
|
||||
#define UTK_MAX(x,y) ((x)>(y)?(x):(y))
|
||||
#define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max)
|
||||
|
||||
#define UTK_BUFFER_SIZE 0x1000
|
||||
|
||||
struct ea_mt_codec_data {
|
||||
STREAMFILE *streamfile;
|
||||
uint8_t buffer[UTK_BUFFER_SIZE];
|
||||
off_t offset;
|
||||
off_t loop_offset;
|
||||
int loop_sample;
|
||||
|
||||
int pcm_blocks;
|
||||
int samples_filled;
|
||||
int samples_used;
|
||||
int samples_done;
|
||||
int samples_discard;
|
||||
void* utk_context;
|
||||
};
|
||||
|
||||
static size_t ea_mt_read_callback(void *dest, int size, void *arg);
|
||||
|
||||
ea_mt_codec_data* init_ea_mt(int channels, int pcm_blocks) {
|
||||
return init_ea_mt_loops(channels, pcm_blocks, 0, NULL);
|
||||
}
|
||||
|
||||
ea_mt_codec_data* init_ea_mt_loops(int channels, int pcm_blocks, int loop_sample, off_t *loop_offsets) {
|
||||
ea_mt_codec_data* data = NULL;
|
||||
int i;
|
||||
|
||||
data = calloc(channels, sizeof(ea_mt_codec_data)); /* one decoder per channel */
|
||||
if (!data) goto fail;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
data[i].utk_context = calloc(1, sizeof(UTKContext));
|
||||
if (!data[i].utk_context) goto fail;
|
||||
utk_init(data[i].utk_context);
|
||||
|
||||
data[i].pcm_blocks = pcm_blocks;
|
||||
data[i].loop_sample = loop_sample;
|
||||
if (loop_offsets)
|
||||
data[i].loop_offset = loop_offsets[i];
|
||||
|
||||
utk_set_callback(data[i].utk_context, data[i].buffer, UTK_BUFFER_SIZE, &data[i], &ea_mt_read_callback);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_ea_mt(data, channels);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void decode_ea_mt(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t samples_to_do, int channel) {
|
||||
int i;
|
||||
ea_mt_codec_data* data = vgmstream->codec_data;
|
||||
ea_mt_codec_data* ch_data = &data[channel];
|
||||
UTKContext* ctx = ch_data->utk_context;
|
||||
int samples_done = 0;
|
||||
|
||||
|
||||
while (samples_done < samples_to_do) {
|
||||
|
||||
if (ch_data->samples_filled) {
|
||||
/* consume current frame */
|
||||
int samples_to_get = ch_data->samples_filled;
|
||||
|
||||
/* don't go past loop, to reset decoder */
|
||||
if (ch_data->loop_sample > 0 && ch_data->samples_done < ch_data->loop_sample &&
|
||||
ch_data->samples_done + samples_to_get > ch_data->loop_sample)
|
||||
samples_to_get = ch_data->loop_sample - ch_data->samples_done;
|
||||
|
||||
if (ch_data->samples_discard) {
|
||||
/* discard samples for looping */
|
||||
if (samples_to_get > ch_data->samples_discard)
|
||||
samples_to_get = ch_data->samples_discard;
|
||||
ch_data->samples_discard -= samples_to_get;
|
||||
}
|
||||
else {
|
||||
/* get max samples and copy */
|
||||
if (samples_to_get > samples_to_do - samples_done)
|
||||
samples_to_get = samples_to_do - samples_done;
|
||||
|
||||
for (i = ch_data->samples_used; i < ch_data->samples_used + samples_to_get; i++) {
|
||||
int pcm = UTK_ROUND(ctx->decompressed_frame[i]);
|
||||
outbuf[0] = (int16_t)UTK_CLAMP(pcm, -32768, 32767);
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
|
||||
samples_done += samples_to_get;
|
||||
}
|
||||
|
||||
/* mark consumed samples */
|
||||
ch_data->samples_used += samples_to_get;
|
||||
ch_data->samples_filled -= samples_to_get;
|
||||
ch_data->samples_done += samples_to_get;
|
||||
|
||||
/* Loops in EA-MT are done with fully separate intro/loop substreams. We must
|
||||
* notify the decoder when a new substream begins (even with looping disabled). */
|
||||
if (ch_data->loop_sample > 0 && ch_data->samples_done == ch_data->loop_sample) {
|
||||
ch_data->samples_filled = 0;
|
||||
ch_data->samples_discard = 0;
|
||||
|
||||
/* offset is usually at loop_offset here, but not always (ex. loop_sample < 432) */
|
||||
ch_data->offset = ch_data->loop_offset;
|
||||
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
|
||||
utk_reset(ctx); /* decoder init (all fields must be reset, for some edge cases) */
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* new frame */
|
||||
if (ch_data->pcm_blocks)
|
||||
utk_rev3_decode_frame(ctx);
|
||||
else
|
||||
utk_decode_frame(ctx);
|
||||
|
||||
ch_data->samples_used = 0;
|
||||
ch_data->samples_filled = 432;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_ea_mt_offsets(VGMSTREAM* vgmstream, int is_start, int samples_discard) {
|
||||
ea_mt_codec_data* data = vgmstream->codec_data;
|
||||
int i;
|
||||
|
||||
if (!data) return;
|
||||
|
||||
|
||||
/* EA-MT frames are VBR (not byte-aligned?), so utk_decoder reads new buffer data automatically.
|
||||
* When decoding starts or a SCHl block changes, flush_ea_mt must be called to reset the state.
|
||||
* A bit hacky but would need some restructuring otherwise. */
|
||||
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
UTKContext* ctx = data[i].utk_context;
|
||||
|
||||
data[i].streamfile = vgmstream->ch[i].streamfile; /* maybe should keep its own STREAMFILE? */
|
||||
if (is_start)
|
||||
data[i].offset = vgmstream->ch[i].channel_start_offset;
|
||||
else
|
||||
data[i].offset = vgmstream->ch[i].offset;
|
||||
utk_set_ptr(ctx, 0, 0); /* reset the buffer reader */
|
||||
|
||||
if (is_start) {
|
||||
utk_reset(ctx);
|
||||
ctx->parsed_header = 0;
|
||||
data[i].samples_done = 0;
|
||||
}
|
||||
|
||||
data[i].samples_filled = 0;
|
||||
data[i].samples_discard = samples_discard;
|
||||
}
|
||||
}
|
||||
|
||||
void flush_ea_mt(VGMSTREAM* vgmstream) {
|
||||
flush_ea_mt_offsets(vgmstream, 0, 0);
|
||||
}
|
||||
|
||||
void reset_ea_mt(VGMSTREAM* vgmstream) {
|
||||
flush_ea_mt_offsets(vgmstream, 1, 0);
|
||||
}
|
||||
|
||||
void seek_ea_mt(VGMSTREAM* vgmstream, int32_t num_sample) {
|
||||
flush_ea_mt_offsets(vgmstream, 1, num_sample);
|
||||
}
|
||||
|
||||
void free_ea_mt(ea_mt_codec_data* data, int channels) {
|
||||
int i;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
free(data[i].utk_context);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* ********************** */
|
||||
|
||||
static size_t ea_mt_read_callback(void *dest, int size, void *arg) {
|
||||
ea_mt_codec_data *ch_data = arg;
|
||||
int bytes_read;
|
||||
|
||||
bytes_read = read_streamfile(dest,ch_data->offset,size,ch_data->streamfile);
|
||||
ch_data->offset += bytes_read;
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
|
|
@ -1,279 +1,279 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* AKA "EA ADPCM", evolved from CDXA. Inconsistently called EA XA/EA-XA/EAXA.
|
||||
* Some variations contain ADPCM hist header per block, but it's handled in ea_block.c */
|
||||
|
||||
/*
|
||||
* Another way to get coefs in EAXA v2, with no diffs (no idea which table is actually used in games):
|
||||
* coef1 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 0];
|
||||
* coef2 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 1];
|
||||
*/
|
||||
/*
|
||||
static const int32_t EA_XA_TABLE2[28] = {
|
||||
0, 0, 240, 0,
|
||||
460, -208, 392, -220,
|
||||
0, 0, 240, 0,
|
||||
460, 0, 392, 0,
|
||||
0, 0, 0, 0,
|
||||
-208, -1, -220, -1,
|
||||
0, 0, 0, 0x3F70
|
||||
};
|
||||
*/
|
||||
|
||||
static const int EA_XA_TABLE[20] = {
|
||||
0, 240, 460, 392,
|
||||
0, 0, -208, -220,
|
||||
0, 1, 3, 4,
|
||||
7, 8, 10, 11,
|
||||
0, -1, -3, -4
|
||||
};
|
||||
|
||||
/* EA XA v2 (always mono); like v1 but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
|
||||
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
|
||||
int xa_frame_size = 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
frame_info = read_8bit(stream->offset,stream->streamfile);
|
||||
|
||||
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
|
||||
stream->adpcm_history1_32 = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
|
||||
stream->adpcm_history2_32 = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += pcm_frame_size;
|
||||
}
|
||||
else { /* ADPCM frame */
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += xa_frame_size;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* later PC games use float math, though in the end sounds basically the same (decompiled from various exes) */
|
||||
static const double XA_K0[16] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const double XA_K1[16] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
/* code uses look-up table but it's be equivalent to:
|
||||
* (double)((nibble << 28) >> (shift + 8) >> 8) or (double)(signed_nibble << (12 - shift)) */
|
||||
static const uint32_t FLOAT_TABLE_INT[256] = {
|
||||
0x00000000,0x45800000,0x46000000,0x46400000,0x46800000,0x46A00000,0x46C00000,0x46E00000,
|
||||
0xC7000000,0xC6E00000,0xC6C00000,0xC6A00000,0xC6800000,0xC6400000,0xC6000000,0xC5800000,
|
||||
0x00000000,0x45000000,0x45800000,0x45C00000,0x46000000,0x46200000,0x46400000,0x46600000,
|
||||
0xC6800000,0xC6600000,0xC6400000,0xC6200000,0xC6000000,0xC5C00000,0xC5800000,0xC5000000,
|
||||
0x00000000,0x44800000,0x45000000,0x45400000,0x45800000,0x45A00000,0x45C00000,0x45E00000,
|
||||
0xC6000000,0xC5E00000,0xC5C00000,0xC5A00000,0xC5800000,0xC5400000,0xC5000000,0xC4800000,
|
||||
0x00000000,0x44000000,0x44800000,0x44C00000,0x45000000,0x45200000,0x45400000,0x45600000,
|
||||
0xC5800000,0xC5600000,0xC5400000,0xC5200000,0xC5000000,0xC4C00000,0xC4800000,0xC4000000,
|
||||
0x00000000,0x43800000,0x44000000,0x44400000,0x44800000,0x44A00000,0x44C00000,0x44E00000,
|
||||
0xC5000000,0xC4E00000,0xC4C00000,0xC4A00000,0xC4800000,0xC4400000,0xC4000000,0xC3800000,
|
||||
0x00000000,0x43000000,0x43800000,0x43C00000,0x44000000,0x44200000,0x44400000,0x44600000,
|
||||
0xC4800000,0xC4600000,0xC4400000,0xC4200000,0xC4000000,0xC3C00000,0xC3800000,0xC3000000,
|
||||
0x00000000,0x42800000,0x43000000,0x43400000,0x43800000,0x43A00000,0x43C00000,0x43E00000,
|
||||
0xC4000000,0xC3E00000,0xC3C00000,0xC3A00000,0xC3800000,0xC3400000,0xC3000000,0xC2800000,
|
||||
0x00000000,0x42000000,0x42800000,0x42C00000,0x43000000,0x43200000,0x43400000,0x43600000,
|
||||
0xC3800000,0xC3600000,0xC3400000,0xC3200000,0xC3000000,0xC2C00000,0xC2800000,0xC2000000,
|
||||
0x00000000,0x41800000,0x42000000,0x42400000,0x42800000,0x42A00000,0x42C00000,0x42E00000,
|
||||
0xC3000000,0xC2E00000,0xC2C00000,0xC2A00000,0xC2800000,0xC2400000,0xC2000000,0xC1800000,
|
||||
0x00000000,0x41000000,0x41800000,0x41C00000,0x42000000,0x42200000,0x42400000,0x42600000,
|
||||
0xC2800000,0xC2600000,0xC2400000,0xC2200000,0xC2000000,0xC1C00000,0xC1800000,0xC1000000,
|
||||
0x00000000,0x40800000,0x41000000,0x41400000,0x41800000,0x41A00000,0x41C00000,0x41E00000,
|
||||
0xC2000000,0xC1E00000,0xC1C00000,0xC1A00000,0xC1800000,0xC1400000,0xC1000000,0xC0800000,
|
||||
0x00000000,0x40000000,0x40800000,0x40C00000,0x41000000,0x41200000,0x41400000,0x41600000,
|
||||
0xC1800000,0xC1600000,0xC1400000,0xC1200000,0xC1000000,0xC0C00000,0xC0800000,0xC0000000,
|
||||
0x00000000,0x3F800000,0x40000000,0x40400000,0x40800000,0x40A00000,0x40C00000,0x40E00000,
|
||||
0xC1000000,0xC0E00000,0xC0C00000,0xC0A00000,0xC0800000,0xC0400000,0xC0000000,0xBF800000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
};
|
||||
static const float *FLOAT_TABLE = (const float *)FLOAT_TABLE_INT;
|
||||
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int i, sample_count, shift;
|
||||
|
||||
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
|
||||
int xa_frame_size = 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
frame_info = read_8bit(stream->offset,stream->streamfile);
|
||||
|
||||
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
|
||||
stream->adpcm_history1_double = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
|
||||
stream->adpcm_history2_double = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += pcm_frame_size;
|
||||
}
|
||||
else { /* ADPCM frame */
|
||||
double coef1, coef2, hist1, hist2, new_sample;
|
||||
|
||||
coef1 = XA_K0[(frame_info >> 4)];
|
||||
coef2 = XA_K1[(frame_info >> 4)];
|
||||
shift = (frame_info & 0x0F) + 8;// << 4;
|
||||
hist1 = stream->adpcm_history1_double;
|
||||
hist2 = stream->adpcm_history2_double;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
off_t byte_offset = (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (double)FLOAT_TABLE[sample_nibble + shift];
|
||||
new_sample = new_sample + coef1 * hist1 + coef2 * hist2;
|
||||
|
||||
outbuf[sample_count] = clamp16((int)new_sample);
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_double = hist1;
|
||||
stream->adpcm_history2_double = hist2;
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += xa_frame_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* EA XA v1 (mono/stereo) */
|
||||
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */
|
||||
|
||||
int frame_size = is_stereo ? 0x0f*2 : 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
if (is_stereo) {
|
||||
/* coefs ch0+ch1 + shift ch0+ch1 */
|
||||
frame_info = read_8bit(stream->offset + 0x00, stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0];
|
||||
coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4];
|
||||
|
||||
frame_info = read_8bit(stream->offset + 0x01, stream->streamfile);
|
||||
shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8;
|
||||
} else {
|
||||
/* coefs + shift ch0 */
|
||||
frame_info = read_8bit(stream->offset + 0x00, stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
}
|
||||
|
||||
/* samples */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = is_stereo ? (stream->offset + 0x02 + i) : (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = is_stereo ? (hn ? 4 : 0) : ((!(i & 1)) ? 4 : 0); /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += frame_size;
|
||||
}
|
||||
|
||||
/* Maxis EA-XA v1 (mono/stereo) with byte-interleave layout in stereo mode */
|
||||
void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
|
||||
int frame_size = 0x0f * channelspacing; /* varies in mono/stereo */
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header (coefs+shift ch0 + coefs+shift ch1) */
|
||||
frame_info = read_8bit(stream->offset + channel,stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
|
||||
/* samples */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x01*channelspacing + (channelspacing == 2 ? i/2 + channel + (i/2)*0x01 : i/2));
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += frame_size;
|
||||
}
|
||||
|
||||
int32_t ea_xa_bytes_to_samples(size_t bytes, int channels) {
|
||||
if (channels <= 0) return 0;
|
||||
return bytes / channels / 0x0f * 28;
|
||||
}
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* AKA "EA ADPCM", evolved from CDXA. Inconsistently called EA XA/EA-XA/EAXA.
|
||||
* Some variations contain ADPCM hist header per block, but it's handled in ea_block.c */
|
||||
|
||||
/*
|
||||
* Another way to get coefs in EAXA v2, with no diffs (no idea which table is actually used in games):
|
||||
* coef1 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 0];
|
||||
* coef2 = EA_XA_TABLE2[(((frame_info >> 4) & 0x0F) << 1) + 1];
|
||||
*/
|
||||
/*
|
||||
static const int32_t EA_XA_TABLE2[28] = {
|
||||
0, 0, 240, 0,
|
||||
460, -208, 392, -220,
|
||||
0, 0, 240, 0,
|
||||
460, 0, 392, 0,
|
||||
0, 0, 0, 0,
|
||||
-208, -1, -220, -1,
|
||||
0, 0, 0, 0x3F70
|
||||
};
|
||||
*/
|
||||
|
||||
static const int EA_XA_TABLE[20] = {
|
||||
0, 240, 460, 392,
|
||||
0, 0, -208, -220,
|
||||
0, 1, 3, 4,
|
||||
7, 8, 10, 11,
|
||||
0, -1, -3, -4
|
||||
};
|
||||
|
||||
/* EA XA v2 (always mono); like v1 but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
|
||||
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
|
||||
int xa_frame_size = 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
frame_info = read_8bit(stream->offset,stream->streamfile);
|
||||
|
||||
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
|
||||
stream->adpcm_history1_32 = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
|
||||
stream->adpcm_history2_32 = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += pcm_frame_size;
|
||||
}
|
||||
else { /* ADPCM frame */
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += xa_frame_size;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* later PC games use float math, though in the end sounds basically the same (decompiled from various exes) */
|
||||
static const double XA_K0[16] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const double XA_K1[16] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
/* code uses look-up table but it's equivalent to:
|
||||
* (double)((nibble << 28) >> (shift + 8) >> 8) or (double)(signed_nibble << (12 - shift)) */
|
||||
static const uint32_t FLOAT_TABLE_INT[256] = {
|
||||
0x00000000,0x45800000,0x46000000,0x46400000,0x46800000,0x46A00000,0x46C00000,0x46E00000,
|
||||
0xC7000000,0xC6E00000,0xC6C00000,0xC6A00000,0xC6800000,0xC6400000,0xC6000000,0xC5800000,
|
||||
0x00000000,0x45000000,0x45800000,0x45C00000,0x46000000,0x46200000,0x46400000,0x46600000,
|
||||
0xC6800000,0xC6600000,0xC6400000,0xC6200000,0xC6000000,0xC5C00000,0xC5800000,0xC5000000,
|
||||
0x00000000,0x44800000,0x45000000,0x45400000,0x45800000,0x45A00000,0x45C00000,0x45E00000,
|
||||
0xC6000000,0xC5E00000,0xC5C00000,0xC5A00000,0xC5800000,0xC5400000,0xC5000000,0xC4800000,
|
||||
0x00000000,0x44000000,0x44800000,0x44C00000,0x45000000,0x45200000,0x45400000,0x45600000,
|
||||
0xC5800000,0xC5600000,0xC5400000,0xC5200000,0xC5000000,0xC4C00000,0xC4800000,0xC4000000,
|
||||
0x00000000,0x43800000,0x44000000,0x44400000,0x44800000,0x44A00000,0x44C00000,0x44E00000,
|
||||
0xC5000000,0xC4E00000,0xC4C00000,0xC4A00000,0xC4800000,0xC4400000,0xC4000000,0xC3800000,
|
||||
0x00000000,0x43000000,0x43800000,0x43C00000,0x44000000,0x44200000,0x44400000,0x44600000,
|
||||
0xC4800000,0xC4600000,0xC4400000,0xC4200000,0xC4000000,0xC3C00000,0xC3800000,0xC3000000,
|
||||
0x00000000,0x42800000,0x43000000,0x43400000,0x43800000,0x43A00000,0x43C00000,0x43E00000,
|
||||
0xC4000000,0xC3E00000,0xC3C00000,0xC3A00000,0xC3800000,0xC3400000,0xC3000000,0xC2800000,
|
||||
0x00000000,0x42000000,0x42800000,0x42C00000,0x43000000,0x43200000,0x43400000,0x43600000,
|
||||
0xC3800000,0xC3600000,0xC3400000,0xC3200000,0xC3000000,0xC2C00000,0xC2800000,0xC2000000,
|
||||
0x00000000,0x41800000,0x42000000,0x42400000,0x42800000,0x42A00000,0x42C00000,0x42E00000,
|
||||
0xC3000000,0xC2E00000,0xC2C00000,0xC2A00000,0xC2800000,0xC2400000,0xC2000000,0xC1800000,
|
||||
0x00000000,0x41000000,0x41800000,0x41C00000,0x42000000,0x42200000,0x42400000,0x42600000,
|
||||
0xC2800000,0xC2600000,0xC2400000,0xC2200000,0xC2000000,0xC1C00000,0xC1800000,0xC1000000,
|
||||
0x00000000,0x40800000,0x41000000,0x41400000,0x41800000,0x41A00000,0x41C00000,0x41E00000,
|
||||
0xC2000000,0xC1E00000,0xC1C00000,0xC1A00000,0xC1800000,0xC1400000,0xC1000000,0xC0800000,
|
||||
0x00000000,0x40000000,0x40800000,0x40C00000,0x41000000,0x41200000,0x41400000,0x41600000,
|
||||
0xC1800000,0xC1600000,0xC1400000,0xC1200000,0xC1000000,0xC0C00000,0xC0800000,0xC0000000,
|
||||
0x00000000,0x3F800000,0x40000000,0x40400000,0x40800000,0x40A00000,0x40C00000,0x40E00000,
|
||||
0xC1000000,0xC0E00000,0xC0C00000,0xC0A00000,0xC0800000,0xC0400000,0xC0000000,0xBF800000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||
};
|
||||
static const float* FLOAT_TABLE = (const float *)FLOAT_TABLE_INT;
|
||||
|
||||
void decode_ea_xa_v2(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int i, sample_count, shift;
|
||||
|
||||
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
|
||||
int xa_frame_size = 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
frame_info = read_8bit(stream->offset,stream->streamfile);
|
||||
|
||||
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
|
||||
stream->adpcm_history1_double = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
|
||||
stream->adpcm_history2_double = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += pcm_frame_size;
|
||||
}
|
||||
else { /* ADPCM frame */
|
||||
double coef1, coef2, hist1, hist2, new_sample;
|
||||
|
||||
coef1 = XA_K0[(frame_info >> 4)];
|
||||
coef2 = XA_K1[(frame_info >> 4)];
|
||||
shift = (frame_info & 0x0F) + 8;// << 4;
|
||||
hist1 = stream->adpcm_history1_double;
|
||||
hist2 = stream->adpcm_history2_double;
|
||||
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
off_t byte_offset = (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (double)FLOAT_TABLE[sample_nibble + shift];
|
||||
new_sample = new_sample + coef1 * hist1 + coef2 * hist2;
|
||||
|
||||
outbuf[sample_count] = clamp16((int)new_sample);
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_double = hist1;
|
||||
stream->adpcm_history2_double = hist2;
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += xa_frame_size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* EA XA v1 (mono/stereo) */
|
||||
void decode_ea_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */
|
||||
|
||||
int frame_size = is_stereo ? 0x0f*2 : 0x0f;
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header */
|
||||
if (is_stereo) {
|
||||
/* coefs ch0+ch1 + shift ch0+ch1 */
|
||||
frame_info = read_8bit(stream->offset + 0x00, stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0];
|
||||
coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4];
|
||||
|
||||
frame_info = read_8bit(stream->offset + 0x01, stream->streamfile);
|
||||
shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8;
|
||||
} else {
|
||||
/* coefs + shift ch0 */
|
||||
frame_info = read_8bit(stream->offset + 0x00, stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
}
|
||||
|
||||
/* samples */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = is_stereo ? (stream->offset + 0x02 + i) : (stream->offset + 0x01 + i/2);
|
||||
int nibble_shift = is_stereo ? (hn ? 4 : 0) : ((!(i & 1)) ? 4 : 0); /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += frame_size;
|
||||
}
|
||||
|
||||
/* Maxis EA-XA v1 (mono/stereo) with byte-interleave layout in stereo mode */
|
||||
void decode_maxis_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame_info;
|
||||
int32_t coef1, coef2;
|
||||
int i, sample_count, shift;
|
||||
|
||||
int frame_size = 0x0f * channelspacing; /* varies in mono/stereo */
|
||||
int frame_samples = 28;
|
||||
first_sample = first_sample % frame_samples;
|
||||
|
||||
/* header (coefs+shift ch0 + coefs+shift ch1) */
|
||||
frame_info = read_8bit(stream->offset + channel,stream->streamfile);
|
||||
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
|
||||
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
|
||||
shift = (frame_info & 0x0F) + 8;
|
||||
|
||||
/* samples */
|
||||
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
|
||||
uint8_t sample_byte, sample_nibble;
|
||||
int32_t new_sample;
|
||||
off_t byte_offset = (stream->offset + 0x01*channelspacing + (channelspacing == 2 ? i/2 + channel + (i/2)*0x01 : i/2));
|
||||
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
|
||||
|
||||
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
|
||||
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
|
||||
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
|
||||
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[sample_count] = new_sample;
|
||||
stream->adpcm_history2_32 = stream->adpcm_history1_32;
|
||||
stream->adpcm_history1_32 = new_sample;
|
||||
}
|
||||
|
||||
/* only increment offset on complete frame */
|
||||
if (i == frame_samples)
|
||||
stream->offset += frame_size;
|
||||
}
|
||||
|
||||
int32_t ea_xa_bytes_to_samples(size_t bytes, int channels) {
|
||||
if (channels <= 0) return 0;
|
||||
return bytes / channels / 0x0f * 28;
|
||||
}
|
||||
|
|
|
@ -1,183 +1,183 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
|
||||
static const int step_table[4] = {
|
||||
5, 1, -1, -3
|
||||
};
|
||||
|
||||
static const int mc3_table[4][4][64] = {
|
||||
{
|
||||
{
|
||||
2, 2, 3, 7, 15, 27, 45, 70, 104, 148, 202, 268, 347, 441, 551, 677,
|
||||
821, 984, 1168, 1374, 1602, 1854, 2131, 2435, 2767, 3127, 3517, 3938, 4392, 4880, 5402, 5960,
|
||||
6555, 7189, 7862, 8577, 9333, 10132, 10976, 11865, 12802, 13786, 14819, 15903, 17038, 18226, 19469, 20766,
|
||||
22120, 23531, 25001, 26531, 28123, 29776, 31494, 33276, 35124, 37039, 39023, 41076, 43201, 45397, 47666, 50010
|
||||
},
|
||||
{
|
||||
1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465,
|
||||
564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097,
|
||||
4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276,
|
||||
15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382
|
||||
},
|
||||
{
|
||||
0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211,
|
||||
256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862,
|
||||
2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489,
|
||||
6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 1, 3, 5, 8, 13, 18, 25, 33, 43, 55, 68, 84,
|
||||
102, 123, 146, 171, 200, 231, 266, 304, 345, 390, 439, 492, 549, 610, 675, 745,
|
||||
819, 898, 982, 1072, 1166, 1266, 1372, 1483, 1600, 1723, 1852, 1987, 2129, 2278, 2433, 2595,
|
||||
2765, 2941, 3125, 3316, 3515, 3722, 3936, 4159, 4390, 4629, 4877, 5134, 5400, 5674, 5958, 6251
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
1, 1, 2, 4, 9, 17, 28, 44, 65, 92, 126, 167, 217, 276, 344, 423,
|
||||
513, 615, 730, 858, 1001, 1159, 1332, 1522, 1729, 1954, 2198, 2461, 2745, 3050, 3376, 3725,
|
||||
4097, 4493, 4914, 5360, 5833, 6332, 6860, 7416, 8001, 8616, 9262, 9939, 10649, 11391, 12168, 12978,
|
||||
13825, 14707, 15626, 16582, 17576, 18610, 19683, 20797, 21952, 23149, 24389, 25673, 27000, 28373, 29791, 31256
|
||||
},
|
||||
{
|
||||
0, 0, 1, 2, 5, 10, 17, 26, 39, 55, 75, 100, 130, 165, 206, 254,
|
||||
308, 369, 438, 515, 600, 695, 799, 913, 1037, 1172, 1319, 1477, 1647, 1830, 2025, 2235,
|
||||
2458, 2696, 2948, 3216, 3499, 3799, 4116, 4449, 4800, 5169, 5557, 5963, 6389, 6835, 7300, 7787,
|
||||
8295, 8824, 9375, 9949, 10546, 11166, 11810, 12478, 13171, 13889, 14633, 15403, 16200, 17023, 17874, 18753
|
||||
},
|
||||
{
|
||||
0, 0, 0, 1, 3, 6, 11, 17, 26, 37, 50, 67, 86, 110, 137, 169,
|
||||
205, 246, 292, 343, 400, 463, 532, 608, 691, 781, 879, 984, 1098, 1220, 1350, 1490,
|
||||
1638, 1797, 1965, 2144, 2333, 2533, 2744, 2966, 3200, 3446, 3704, 3975, 4259, 4556, 4867, 5191,
|
||||
5530, 5882, 6250, 6632, 7030, 7444, 7873, 8319, 8781, 9259, 9755, 10269, 10800, 11349, 11916, 12502
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 0, 1, 2, 4, 6, 9, 12, 16, 21, 27, 34, 42,
|
||||
51, 61, 73, 85, 100, 115, 133, 152, 172, 195, 219, 246, 274, 305, 337, 372,
|
||||
409, 449, 491, 536, 583, 633, 686, 741, 800, 861, 926, 993, 1064, 1139, 1216, 1297,
|
||||
1382, 1470, 1562, 1658, 1757, 1861, 1968, 2079, 2195, 2314, 2438, 2567, 2700, 2837, 2979, 3125
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465,
|
||||
564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097,
|
||||
4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276,
|
||||
15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382
|
||||
},
|
||||
{
|
||||
1, 1, 1, 3, 7, 13, 22, 35, 52, 74, 101, 134, 173, 220, 275, 338,
|
||||
410, 492, 584, 687, 801, 927, 1065, 1217, 1383, 1563, 1758, 1969, 2196, 2440, 2701, 2980,
|
||||
3277, 3594, 3931, 4288, 4666, 5066, 5488, 5932, 6401, 6893, 7409, 7951, 8519, 9113, 9734, 10383,
|
||||
11060, 11765, 12500, 13265, 14061, 14888, 15747, 16638, 17562, 18519, 19511, 20538, 21600, 22698, 23833, 25005
|
||||
},
|
||||
{
|
||||
0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211,
|
||||
256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862,
|
||||
2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489,
|
||||
6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628
|
||||
},
|
||||
{
|
||||
0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127,
|
||||
154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117,
|
||||
1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893,
|
||||
4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
1, 1, 2, 5, 11, 20, 34, 53, 78, 111, 151, 201, 260, 331, 413, 508,
|
||||
616, 738, 876, 1030, 1201, 1390, 1598, 1826, 2075, 2345, 2638, 2954, 3294, 3660, 4051, 4470,
|
||||
4916, 5392, 5897, 6432, 6999, 7599, 8232, 8899, 9601, 10339, 11114, 11927, 12779, 13670, 14601, 15574,
|
||||
16590, 17648, 18751, 19898, 21092, 22332, 23620, 24957, 26343, 27779, 29267, 30807, 32400, 34047, 35749, 37507
|
||||
},
|
||||
{
|
||||
1, 1, 1, 3, 6, 11, 19, 31, 45, 64, 88, 117, 152, 193, 241, 296,
|
||||
359, 430, 511, 601, 701, 811, 932, 1065, 1210, 1368, 1538, 1723, 1921, 2135, 2363, 2607,
|
||||
2868, 3145, 3440, 3752, 4083, 4433, 4802, 5191, 5600, 6031, 6483, 6957, 7454, 7974, 8517, 9085,
|
||||
9677, 10295, 10938, 11607, 12303, 13027, 13778, 14558, 15366, 16204, 17072, 17971, 18900, 19861, 20854, 21879
|
||||
},
|
||||
{
|
||||
0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127,
|
||||
154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117,
|
||||
1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893,
|
||||
4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* MC3 3-bit ADPCM (Paradigm Entertainment games).
|
||||
*
|
||||
* Layout: blocks with 32b header + 32b ch1, 32b ch2, 32b ch1...
|
||||
* Each 32b is a sub-block with 10 samples (3b x10) sharing a 'mode' of sorts.
|
||||
* More than one block is rarely used though.
|
||||
*
|
||||
* Tables and original algorithm by daemon1
|
||||
*/
|
||||
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int i, sample_count = 0;
|
||||
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
int step_index = stream->adpcm_step_index;
|
||||
|
||||
/* internal interleave */
|
||||
int block_samples = (vgmstream->interleave_block_size - 4) / 4 / vgmstream->channels * 10;
|
||||
first_sample = first_sample % block_samples;
|
||||
|
||||
|
||||
/* block header */
|
||||
if (first_sample == 0) {
|
||||
uint32_t header = (uint32_t)read_32bitLE(stream->offset, stream->streamfile);
|
||||
header = (header >> channel*16); /* lower 16=ch1, upper 16b=ch2 */
|
||||
step_index = header & 0x3f; /* 6b */
|
||||
hist = header & 0xffc0; /* 16b sans 6b */
|
||||
if (hist > 0x7fff) hist -= 0x10000; /* sign extend */
|
||||
}
|
||||
|
||||
|
||||
/* block samples */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint32_t subblock, mode, samples, index, sign, diff;
|
||||
|
||||
/* header + ch shift + sub-block number (ex. ch0 i=10: sub-block 1, ch0 i=23: sub-block 2) */
|
||||
off_t subblock_offset = stream->offset + 4 + 4*channel + (i/10)*(4*vgmstream->channels);
|
||||
int sample_shift = (i%10)*3;
|
||||
|
||||
/* expand 3b */
|
||||
subblock = (uint32_t)read_32bitLE(subblock_offset, stream->streamfile);
|
||||
mode = (subblock >> 30) & 0x3; /* upper 2b */
|
||||
samples = (subblock) & 0x3FFFFFFF; /* lower 3b*10 */
|
||||
|
||||
index = (samples >> sample_shift) & 3; /* lower 2b */
|
||||
sign = (samples >> sample_shift) & 4; /* upper 1b */
|
||||
diff = mc3_table[mode][index][step_index];
|
||||
if (sign == 0)
|
||||
hist += (- 1 - diff);
|
||||
else
|
||||
hist += diff;
|
||||
|
||||
/* new step + clamp */
|
||||
step_index += step_table[index];
|
||||
if (step_index < 0) step_index = 0;
|
||||
else if (step_index > 63) step_index = 63;
|
||||
|
||||
/* output */
|
||||
outbuf[sample_count] = hist;
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
|
||||
/* internal interleave: increment offset on complete frame */
|
||||
if (i == block_samples) stream->offset += vgmstream->interleave_block_size;
|
||||
|
||||
stream->adpcm_history1_32 = hist;
|
||||
stream->adpcm_step_index = step_index;
|
||||
}
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
|
||||
static const int step_table[4] = {
|
||||
5, 1, -1, -3
|
||||
};
|
||||
|
||||
static const int mc3_table[4][4][64] = {
|
||||
{
|
||||
{
|
||||
2, 2, 3, 7, 15, 27, 45, 70, 104, 148, 202, 268, 347, 441, 551, 677,
|
||||
821, 984, 1168, 1374, 1602, 1854, 2131, 2435, 2767, 3127, 3517, 3938, 4392, 4880, 5402, 5960,
|
||||
6555, 7189, 7862, 8577, 9333, 10132, 10976, 11865, 12802, 13786, 14819, 15903, 17038, 18226, 19469, 20766,
|
||||
22120, 23531, 25001, 26531, 28123, 29776, 31494, 33276, 35124, 37039, 39023, 41076, 43201, 45397, 47666, 50010
|
||||
},
|
||||
{
|
||||
1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465,
|
||||
564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097,
|
||||
4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276,
|
||||
15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382
|
||||
},
|
||||
{
|
||||
0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211,
|
||||
256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862,
|
||||
2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489,
|
||||
6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 1, 3, 5, 8, 13, 18, 25, 33, 43, 55, 68, 84,
|
||||
102, 123, 146, 171, 200, 231, 266, 304, 345, 390, 439, 492, 549, 610, 675, 745,
|
||||
819, 898, 982, 1072, 1166, 1266, 1372, 1483, 1600, 1723, 1852, 1987, 2129, 2278, 2433, 2595,
|
||||
2765, 2941, 3125, 3316, 3515, 3722, 3936, 4159, 4390, 4629, 4877, 5134, 5400, 5674, 5958, 6251
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
1, 1, 2, 4, 9, 17, 28, 44, 65, 92, 126, 167, 217, 276, 344, 423,
|
||||
513, 615, 730, 858, 1001, 1159, 1332, 1522, 1729, 1954, 2198, 2461, 2745, 3050, 3376, 3725,
|
||||
4097, 4493, 4914, 5360, 5833, 6332, 6860, 7416, 8001, 8616, 9262, 9939, 10649, 11391, 12168, 12978,
|
||||
13825, 14707, 15626, 16582, 17576, 18610, 19683, 20797, 21952, 23149, 24389, 25673, 27000, 28373, 29791, 31256
|
||||
},
|
||||
{
|
||||
0, 0, 1, 2, 5, 10, 17, 26, 39, 55, 75, 100, 130, 165, 206, 254,
|
||||
308, 369, 438, 515, 600, 695, 799, 913, 1037, 1172, 1319, 1477, 1647, 1830, 2025, 2235,
|
||||
2458, 2696, 2948, 3216, 3499, 3799, 4116, 4449, 4800, 5169, 5557, 5963, 6389, 6835, 7300, 7787,
|
||||
8295, 8824, 9375, 9949, 10546, 11166, 11810, 12478, 13171, 13889, 14633, 15403, 16200, 17023, 17874, 18753
|
||||
},
|
||||
{
|
||||
0, 0, 0, 1, 3, 6, 11, 17, 26, 37, 50, 67, 86, 110, 137, 169,
|
||||
205, 246, 292, 343, 400, 463, 532, 608, 691, 781, 879, 984, 1098, 1220, 1350, 1490,
|
||||
1638, 1797, 1965, 2144, 2333, 2533, 2744, 2966, 3200, 3446, 3704, 3975, 4259, 4556, 4867, 5191,
|
||||
5530, 5882, 6250, 6632, 7030, 7444, 7873, 8319, 8781, 9259, 9755, 10269, 10800, 11349, 11916, 12502
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 0, 1, 2, 4, 6, 9, 12, 16, 21, 27, 34, 42,
|
||||
51, 61, 73, 85, 100, 115, 133, 152, 172, 195, 219, 246, 274, 305, 337, 372,
|
||||
409, 449, 491, 536, 583, 633, 686, 741, 800, 861, 926, 993, 1064, 1139, 1216, 1297,
|
||||
1382, 1470, 1562, 1658, 1757, 1861, 1968, 2079, 2195, 2314, 2438, 2567, 2700, 2837, 2979, 3125
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
1, 1, 2, 5, 10, 18, 31, 48, 72, 101, 139, 184, 239, 303, 378, 465,
|
||||
564, 677, 803, 944, 1101, 1274, 1465, 1674, 1902, 2150, 2418, 2707, 3019, 3355, 3714, 4097,
|
||||
4507, 4942, 5405, 5896, 6416, 6966, 7546, 8157, 8801, 9477, 10188, 10933, 11714, 12530, 13384, 14276,
|
||||
15207, 16177, 17188, 18240, 19334, 20471, 21652, 22877, 24148, 25464, 26828, 28240, 29700, 31210, 32770, 34382
|
||||
},
|
||||
{
|
||||
1, 1, 1, 3, 7, 13, 22, 35, 52, 74, 101, 134, 173, 220, 275, 338,
|
||||
410, 492, 584, 687, 801, 927, 1065, 1217, 1383, 1563, 1758, 1969, 2196, 2440, 2701, 2980,
|
||||
3277, 3594, 3931, 4288, 4666, 5066, 5488, 5932, 6401, 6893, 7409, 7951, 8519, 9113, 9734, 10383,
|
||||
11060, 11765, 12500, 13265, 14061, 14888, 15747, 16638, 17562, 18519, 19511, 20538, 21600, 22698, 23833, 25005
|
||||
},
|
||||
{
|
||||
0, 0, 1, 2, 4, 8, 14, 22, 32, 46, 63, 83, 108, 138, 172, 211,
|
||||
256, 307, 365, 429, 500, 579, 666, 761, 864, 977, 1099, 1230, 1372, 1525, 1688, 1862,
|
||||
2048, 2246, 2457, 2680, 2916, 3166, 3430, 3708, 4000, 4308, 4631, 4969, 5324, 5695, 6084, 6489,
|
||||
6912, 7353, 7813, 8291, 8788, 9305, 9841, 10398, 10976, 11574, 12194, 12836, 13500, 14186, 14895, 15628
|
||||
},
|
||||
{
|
||||
0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127,
|
||||
154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117,
|
||||
1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893,
|
||||
4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
1, 1, 2, 5, 11, 20, 34, 53, 78, 111, 151, 201, 260, 331, 413, 508,
|
||||
616, 738, 876, 1030, 1201, 1390, 1598, 1826, 2075, 2345, 2638, 2954, 3294, 3660, 4051, 4470,
|
||||
4916, 5392, 5897, 6432, 6999, 7599, 8232, 8899, 9601, 10339, 11114, 11927, 12779, 13670, 14601, 15574,
|
||||
16590, 17648, 18751, 19898, 21092, 22332, 23620, 24957, 26343, 27779, 29267, 30807, 32400, 34047, 35749, 37507
|
||||
},
|
||||
{
|
||||
1, 1, 1, 3, 6, 11, 19, 31, 45, 64, 88, 117, 152, 193, 241, 296,
|
||||
359, 430, 511, 601, 701, 811, 932, 1065, 1210, 1368, 1538, 1723, 1921, 2135, 2363, 2607,
|
||||
2868, 3145, 3440, 3752, 4083, 4433, 4802, 5191, 5600, 6031, 6483, 6957, 7454, 7974, 8517, 9085,
|
||||
9677, 10295, 10938, 11607, 12303, 13027, 13778, 14558, 15366, 16204, 17072, 17971, 18900, 19861, 20854, 21879
|
||||
},
|
||||
{
|
||||
0, 0, 0, 1, 2, 5, 8, 13, 19, 27, 37, 50, 65, 82, 103, 127,
|
||||
154, 184, 219, 257, 300, 347, 399, 456, 518, 586, 659, 738, 823, 915, 1012, 1117,
|
||||
1229, 1348, 1474, 1608, 1749, 1899, 2058, 2224, 2400, 2584, 2778, 2981, 3194, 3417, 3650, 3893,
|
||||
4147, 4412, 4687, 4974, 5273, 5583, 5905, 6239, 6585, 6944, 7316, 7701, 8100, 8511, 8937, 9376
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* MC3 3-bit ADPCM (Paradigm Entertainment games).
|
||||
*
|
||||
* Layout: blocks with 32b header + 32b ch1, 32b ch2, 32b ch1...
|
||||
* Each 32b is a sub-block with 10 samples (3b x10) sharing a 'mode' of sorts.
|
||||
* More than one block is rarely used though.
|
||||
*
|
||||
* Tables and original algorithm by daemon1
|
||||
*/
|
||||
void decode_mc3(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
int i, sample_count = 0;
|
||||
|
||||
int32_t hist = stream->adpcm_history1_32;
|
||||
int step_index = stream->adpcm_step_index;
|
||||
|
||||
/* internal interleave */
|
||||
int block_samples = (vgmstream->interleave_block_size - 4) / 4 / vgmstream->channels * 10;
|
||||
first_sample = first_sample % block_samples;
|
||||
|
||||
|
||||
/* block header */
|
||||
if (first_sample == 0) {
|
||||
uint32_t header = (uint32_t)read_32bitLE(stream->offset, stream->streamfile);
|
||||
header = (header >> channel*16); /* lower 16=ch1, upper 16b=ch2 */
|
||||
step_index = header & 0x3f; /* 6b */
|
||||
hist = header & 0xffc0; /* 16b sans 6b */
|
||||
if (hist > 0x7fff) hist -= 0x10000; /* sign extend */
|
||||
}
|
||||
|
||||
|
||||
/* block samples */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
uint32_t subblock, mode, samples, index, sign, diff;
|
||||
|
||||
/* header + ch shift + sub-block number (ex. ch0 i=10: sub-block 1, ch0 i=23: sub-block 2) */
|
||||
off_t subblock_offset = stream->offset + 4 + 4*channel + (i/10)*(4*vgmstream->channels);
|
||||
int sample_shift = (i%10)*3;
|
||||
|
||||
/* expand 3b */
|
||||
subblock = (uint32_t)read_32bitLE(subblock_offset, stream->streamfile);
|
||||
mode = (subblock >> 30) & 0x3; /* upper 2b */
|
||||
samples = (subblock) & 0x3FFFFFFF; /* lower 3b*10 */
|
||||
|
||||
index = (samples >> sample_shift) & 3; /* lower 2b */
|
||||
sign = (samples >> sample_shift) & 4; /* upper 1b */
|
||||
diff = mc3_table[mode][index][step_index];
|
||||
if (sign == 0)
|
||||
hist += (- 1 - diff);
|
||||
else
|
||||
hist += diff;
|
||||
|
||||
/* new step + clamp */
|
||||
step_index += step_table[index];
|
||||
if (step_index < 0) step_index = 0;
|
||||
else if (step_index > 63) step_index = 63;
|
||||
|
||||
/* output */
|
||||
outbuf[sample_count] = hist;
|
||||
sample_count += channelspacing;
|
||||
}
|
||||
|
||||
|
||||
/* internal interleave: increment offset on complete frame */
|
||||
if (i == block_samples) stream->offset += vgmstream->interleave_block_size;
|
||||
|
||||
stream->adpcm_history1_32 = hist;
|
||||
stream->adpcm_step_index = step_index;
|
||||
}
|
||||
|
|
|
@ -1,254 +1,254 @@
|
|||
#include "../util.h"
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
static const int16_t msadpcm_steps[16] = {
|
||||
230, 230, 230, 230,
|
||||
307, 409, 512, 614,
|
||||
768, 614, 512, 409,
|
||||
307, 230, 230, 230
|
||||
};
|
||||
|
||||
static const int16_t msadpcm_coefs[7][2] = {
|
||||
{ 256, 0 },
|
||||
{ 512, -256 },
|
||||
{ 0, 0 },
|
||||
{ 192, 64 },
|
||||
{ 240, 0 },
|
||||
{ 460, -208 },
|
||||
{ 392, -232 }
|
||||
};
|
||||
|
||||
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL *ch1,*ch2;
|
||||
STREAMFILE *streamfile;
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
ch1 = &vgmstream->ch[0];
|
||||
ch2 = &vgmstream->ch[1];
|
||||
streamfile = ch1->streamfile;
|
||||
|
||||
/* external interleave (variable size), stereo */
|
||||
bytes_per_frame = vgmstream->frame_size;
|
||||
samples_per_frame = (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = ch1->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0];
|
||||
ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1];
|
||||
ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0];
|
||||
ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1];
|
||||
ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile);
|
||||
ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile);
|
||||
ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile);
|
||||
ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile);
|
||||
ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile);
|
||||
ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample==0) {
|
||||
outbuf[0] = ch1->adpcm_history2_16;
|
||||
outbuf[1] = ch2->adpcm_history2_16;
|
||||
outbuf += 2;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = ch1->adpcm_history1_16;
|
||||
outbuf[1] = ch2->adpcm_history1_16;
|
||||
outbuf += 2;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int ch;
|
||||
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch];
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (ch == 0) ? /* L = high nibble first */
|
||||
get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) :
|
||||
get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile));
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted / 256;
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = vgmstream->frame_size;
|
||||
samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = stream->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
|
||||
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
|
||||
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
|
||||
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile);
|
||||
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample == 0) {
|
||||
outbuf[0] = stream->adpcm_history2_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = stream->adpcm_history1_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (i & 1) ? /* high nibble first */
|
||||
get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
|
||||
get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted / 256;
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order
|
||||
* (their tools may convert to float/others but internally it's all PCM16, from debugging). */
|
||||
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = vgmstream->frame_size;
|
||||
samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = stream->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
|
||||
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
|
||||
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
|
||||
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */
|
||||
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample == 0) {
|
||||
outbuf[0] = stream->adpcm_history2_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = stream->adpcm_history1_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */
|
||||
get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
|
||||
get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted >> 8; /* probably no difference vs MSADPCM */
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
|
||||
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) {
|
||||
if (block_size <= 0 || channels <= 0) return 0;
|
||||
return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels
|
||||
+ ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0);
|
||||
}
|
||||
|
||||
/* test if MSADPCM coefs were re-defined (possible in theory but not used in practice) */
|
||||
int msadpcm_check_coefs(STREAMFILE *sf, off_t offset) {
|
||||
int i;
|
||||
int count = read_16bitLE(offset, sf);
|
||||
if (count != 7) {
|
||||
VGM_LOG("MSADPCM: bad count %i at %lx\n", count, offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset += 0x02;
|
||||
for (i = 0; i < 7; i++) {
|
||||
int16_t coef1 = read_16bitLE(offset + 0x00, sf);
|
||||
int16_t coef2 = read_16bitLE(offset + 0x02, sf);
|
||||
|
||||
if (coef1 != msadpcm_coefs[i][0] || coef2 != msadpcm_coefs[i][1]) {
|
||||
VGM_LOG("MSADPCM: bad coef %i/%i vs %i/%i\n", coef1, coef2, msadpcm_coefs[i][0], msadpcm_coefs[i][1]);
|
||||
goto fail;
|
||||
}
|
||||
offset += 0x02 + 0x02;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
#include "../util.h"
|
||||
#include "coding.h"
|
||||
|
||||
|
||||
static const int16_t msadpcm_steps[16] = {
|
||||
230, 230, 230, 230,
|
||||
307, 409, 512, 614,
|
||||
768, 614, 512, 409,
|
||||
307, 230, 230, 230
|
||||
};
|
||||
|
||||
static const int16_t msadpcm_coefs[7][2] = {
|
||||
{ 256, 0 },
|
||||
{ 512, -256 },
|
||||
{ 0, 0 },
|
||||
{ 192, 64 },
|
||||
{ 240, 0 },
|
||||
{ 460, -208 },
|
||||
{ 392, -232 }
|
||||
};
|
||||
|
||||
void decode_msadpcm_stereo(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t first_sample, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL *ch1,*ch2;
|
||||
STREAMFILE *streamfile;
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
ch1 = &vgmstream->ch[0];
|
||||
ch2 = &vgmstream->ch[1];
|
||||
streamfile = ch1->streamfile;
|
||||
|
||||
/* external interleave (variable size), stereo */
|
||||
bytes_per_frame = vgmstream->frame_size;
|
||||
samples_per_frame = (vgmstream->frame_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = ch1->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0];
|
||||
ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1];
|
||||
ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0];
|
||||
ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1];
|
||||
ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile);
|
||||
ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile);
|
||||
ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile);
|
||||
ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile);
|
||||
ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile);
|
||||
ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample==0) {
|
||||
outbuf[0] = ch1->adpcm_history2_16;
|
||||
outbuf[1] = ch2->adpcm_history2_16;
|
||||
outbuf += 2;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = ch1->adpcm_history1_16;
|
||||
outbuf[1] = ch2->adpcm_history1_16;
|
||||
outbuf += 2;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int ch;
|
||||
|
||||
for (ch = 0; ch < 2; ch++) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch];
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (ch == 0) ? /* L = high nibble first */
|
||||
get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) :
|
||||
get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile));
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted / 256;
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode_msadpcm_mono(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = vgmstream->frame_size;
|
||||
samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = stream->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
|
||||
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
|
||||
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
|
||||
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile);
|
||||
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample == 0) {
|
||||
outbuf[0] = stream->adpcm_history2_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = stream->adpcm_history1_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (i & 1) ? /* high nibble first */
|
||||
get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
|
||||
get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted / 256;
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order
|
||||
* (their tools may convert to float/others but internally it's all PCM16, from debugging). */
|
||||
void decode_msadpcm_ck(VGMSTREAM* vgmstream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
|
||||
int i, frames_in;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
off_t frame_offset;
|
||||
|
||||
/* external interleave (variable size), mono */
|
||||
bytes_per_frame = vgmstream->frame_size;
|
||||
samples_per_frame = (vgmstream->frame_size - 0x07)*2 + 2;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
frame_offset = stream->offset + frames_in*bytes_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
if (first_sample == 0) {
|
||||
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
|
||||
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
|
||||
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
|
||||
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */
|
||||
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
|
||||
}
|
||||
|
||||
/* write header samples (needed) */
|
||||
if (first_sample == 0) {
|
||||
outbuf[0] = stream->adpcm_history2_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
if (first_sample == 1 && samples_to_do > 0) {
|
||||
outbuf[0] = stream->adpcm_history1_16;
|
||||
outbuf += channelspacing;
|
||||
first_sample++;
|
||||
samples_to_do--;
|
||||
}
|
||||
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample+samples_to_do; i++) {
|
||||
int32_t hist1,hist2, predicted;
|
||||
int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */
|
||||
get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
|
||||
get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
|
||||
|
||||
hist1 = stream->adpcm_history1_16;
|
||||
hist2 = stream->adpcm_history2_16;
|
||||
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
|
||||
predicted = predicted >> 8; /* probably no difference vs MSADPCM */
|
||||
predicted = predicted + sample_nibble*stream->adpcm_scale;
|
||||
outbuf[0] = clamp16(predicted);
|
||||
|
||||
stream->adpcm_history2_16 = stream->adpcm_history1_16;
|
||||
stream->adpcm_history1_16 = outbuf[0];
|
||||
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8;
|
||||
if (stream->adpcm_scale < 0x10)
|
||||
stream->adpcm_scale = 0x10;
|
||||
|
||||
outbuf += channelspacing;
|
||||
}
|
||||
}
|
||||
|
||||
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels) {
|
||||
if (block_size <= 0 || channels <= 0) return 0;
|
||||
return (bytes / block_size) * (block_size - (7-1)*channels) * 2 / channels
|
||||
+ ((bytes % block_size) ? ((bytes % block_size) - (7-1)*channels) * 2 / channels : 0);
|
||||
}
|
||||
|
||||
/* test if MSADPCM coefs were re-defined (possible in theory but not used in practice) */
|
||||
int msadpcm_check_coefs(STREAMFILE* sf, off_t offset) {
|
||||
int i;
|
||||
int count = read_16bitLE(offset, sf);
|
||||
if (count != 7) {
|
||||
VGM_LOG("MSADPCM: bad count %i at %lx\n", count, offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
offset += 0x02;
|
||||
for (i = 0; i < 7; i++) {
|
||||
int16_t coef1 = read_16bitLE(offset + 0x00, sf);
|
||||
int16_t coef2 = read_16bitLE(offset + 0x02, sf);
|
||||
|
||||
if (coef1 != msadpcm_coefs[i][0] || coef2 != msadpcm_coefs[i][1]) {
|
||||
VGM_LOG("MSADPCM: bad coef %i/%i vs %i/%i\n", coef1, coef2, msadpcm_coefs[i][0], msadpcm_coefs[i][1]);
|
||||
goto fail;
|
||||
}
|
||||
offset += 0x02 + 0x02;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,207 +1,207 @@
|
|||
#include <math.h>
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* PSVita ADPCM table */
|
||||
static const int16_t hevag_coefs[128][4] = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 7680, 0, 0, 0 },
|
||||
{ 14720, -6656, 0, 0 },
|
||||
{ 12544, -7040, 0, 0 },
|
||||
{ 15616, -7680, 0, 0 },
|
||||
{ 14731, -7059, 0, 0 },
|
||||
{ 14507, -7366, 0, 0 },
|
||||
{ 13920, -7522, 0, 0 },
|
||||
{ 13133, -7680, 0, 0 },
|
||||
{ 12028, -7680, 0, 0 },
|
||||
{ 10764, -7680, 0, 0 },
|
||||
{ 9359, -7680, 0, 0 },
|
||||
{ 7832, -7680, 0, 0 },
|
||||
{ 6201, -7680, 0, 0 },
|
||||
{ 4488, -7680, 0, 0 },
|
||||
{ 2717, -7680, 0, 0 },
|
||||
{ 910, -7680, 0, 0 },
|
||||
{ -910, -7680, 0, 0 },
|
||||
{ -2717, -7680, 0, 0 },
|
||||
{ -4488, -7680, 0, 0 },
|
||||
{ -6201, -7680, 0, 0 },
|
||||
{ -7832, -7680, 0, 0 },
|
||||
{ -9359, -7680, 0, 0 },
|
||||
{ -10764, -7680, 0, 0 },
|
||||
{ -12028, -7680, 0, 0 },
|
||||
{ -13133, -7680, 0, 0 },
|
||||
{ -13920, -7522, 0, 0 },
|
||||
{ -14507, -7366, 0, 0 },
|
||||
{ -14731, -7059, 0, 0 },
|
||||
{ 5376, -9216, 3328, -3072 },
|
||||
{ -6400, -7168, -3328, -2304 },
|
||||
{ -10496, -7424, -3584, -1024 },
|
||||
{ -167, -2722, -494, -541 },
|
||||
{ -7430, -2221, -2298, 424 },
|
||||
{ -8001, -3166, -2814, 289 },
|
||||
{ 6018, -4750, 2649, -1298 },
|
||||
{ 3798, -6946, 3875, -1216 },
|
||||
{ -8237, -2596, -2071, 227 },
|
||||
{ 9199, 1982, -1382, -2316 },
|
||||
{ 13021, -3044, -3792, 1267 },
|
||||
{ 13112, -4487, -2250, 1665 },
|
||||
{ -1668, -3744, -6456, 840 },
|
||||
{ 7819, -4328, 2111, -506 },
|
||||
{ 9571, -1336, -757, 487 },
|
||||
{ 10032, -2562, 300, 199 },
|
||||
{ -4745, -4122, -5486, -1493 },
|
||||
{ -5896, 2378, -4787, -6947 },
|
||||
{ -1193, -9117, -1237, -3114 },
|
||||
{ 2783, -7108, -1575, -1447 },
|
||||
{ -7334, -2062, -2212, 446 },
|
||||
{ 6127, -2577, -315, -18 },
|
||||
{ 9457, -1858, 102, 258 },
|
||||
{ 7876, -4483, 2126, -538 },
|
||||
{ -7172, -1795, -2069, 482 },
|
||||
{ -7358, -2102, -2233, 440 },
|
||||
{ -9170, -3509, -2674, -391 },
|
||||
{ -2638, -2647, -1929, -1637 },
|
||||
{ 1873, 9183, 1860, -5746 },
|
||||
{ 9214, 1859, -1124, -2427 },
|
||||
{ 13204, -3012, -4139, 1370 },
|
||||
{ 12437, -4792, -256, 622 },
|
||||
{ -2653, -1144, -3182, -6878 },
|
||||
{ 9331, -1048, -828, 507 },
|
||||
{ 1642, -620, -946, -4229 },
|
||||
{ 4246, -7585, -533, -2259 },
|
||||
{ -8988, -3891, -2807, 44 },
|
||||
{ -2562, -2735, -1730, -1899 },
|
||||
{ 3182, -483, -714, -1421 },
|
||||
{ 7937, -3844, 2821, -1019 },
|
||||
{ 10069, -2609, 314, 195 },
|
||||
{ 8400, -3297, 1551, -155 },
|
||||
{ -8529, -2775, -2432, -336 },
|
||||
{ 9477, -1882, 108, 256 },
|
||||
{ 75, -2241, -298, -6937 },
|
||||
{ -9143, -4160, -2963, 5 },
|
||||
{ -7270, -1958, -2156, 460 },
|
||||
{ -2740, 3745, 5936, -1089 },
|
||||
{ 8993, 1948, -683, -2704 },
|
||||
{ 13101, -2835, -3854, 1055 },
|
||||
{ 9543, -1961, 130, 250 },
|
||||
{ 5272, -4270, 3124, -3157 },
|
||||
{ -7696, -3383, -2907, -456 },
|
||||
{ 7309, 2523, 434, -2461 },
|
||||
{ 10275, -2867, 391, 172 },
|
||||
{ 10940, -3721, 665, 97 },
|
||||
{ 24, -310, -1262, 320 },
|
||||
{ -8122, -2411, -2311, -271 },
|
||||
{ -8511, -3067, -2337, 163 },
|
||||
{ 326, -3846, 419, -933 },
|
||||
{ 8895, 2194, -541, -2880 },
|
||||
{ 12073, -1876, -2017, -601 },
|
||||
{ 8729, -3423, 1674, -169 },
|
||||
{ 12950, -3847, -3007, 1946 },
|
||||
{ 10038, -2570, 302, 198 },
|
||||
{ 9385, -2757, 1008, 41 },
|
||||
{ -4720, -5006, -2852, -1161 },
|
||||
{ 7869, -4326, 2135, -501 },
|
||||
{ 2450, -8597, 1299, -2780 },
|
||||
{ 10192, -2763, 360, 181 },
|
||||
{ 11313, -4213, 833, 53 },
|
||||
{ 10154, -2716, 345, 185 },
|
||||
{ 9638, -1417, -737, 482 },
|
||||
{ 3854, -4554, 2843, -3397 },
|
||||
{ 6699, -5659, 2249, -1074 },
|
||||
{ 11082, -3908, 728, 80 },
|
||||
{ -1026, -9810, -805, -3462 },
|
||||
{ 10396, -3746, 1367, -96 },
|
||||
{ 10287, 988, -1915, -1437 },
|
||||
{ 7953, 3878, -764, -3263 },
|
||||
{ 12689, -3375, -3354, 2079 },
|
||||
{ 6641, 3166, 231, -2089 },
|
||||
{ -2348, -7354, -1944, -4122 },
|
||||
{ 9290, -4039, 1885, -246 },
|
||||
{ 4633, -6403, 1748, -1619 },
|
||||
{ 11247, -4125, 802, 61 },
|
||||
{ 9807, -2284, 219, 222 },
|
||||
{ 9736, -1536, -706, 473 },
|
||||
{ 8440, -3436, 1562, -176 },
|
||||
{ 9307, -1021, -835, 509 },
|
||||
{ 1698, -9025, 688, -3037 },
|
||||
{ 10214, -2791, 368, 179 },
|
||||
{ 8390, 3248, -758, -2989 },
|
||||
{ 7201, 3316, 46, -2614 },
|
||||
{ -88, -7809, -538, -4571 },
|
||||
{ 6193, -5189, 2760, -1245 },
|
||||
{ 12325, -1290, -3284, 253 },
|
||||
{ 13064, -4075, -2824, 1877 },
|
||||
{ 5333, 2999, 775, -1132 }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sony's HEVAG (High Efficiency VAG) ADPCM, used in PSVita games (hardware decoded).
|
||||
* Evolution of the regular VAG (same flags and frames), uses 4 history samples and a bigger table.
|
||||
*
|
||||
* Original research and algorithm by id-daemon / daemon1.
|
||||
*/
|
||||
void decode_hevag(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x10] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int coef_index, shift_factor, flag;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int32_t hist3 = stream->adpcm_history3_32;
|
||||
int32_t hist4 = stream->adpcm_history4_32;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x10;
|
||||
samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
shift_factor = (frame[0] >> 0) & 0xf;
|
||||
coef_index = ((frame[1] >> 0) & 0xf0) | coef_index;
|
||||
flag = (frame[1] >> 0) & 0xf; /* same flags */
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 127 || shift_factor > 12, "HEVAG: in+correct coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
if (coef_index > 127)
|
||||
coef_index = 127; /* ? */
|
||||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* ? */
|
||||
|
||||
shift_factor = 20 - shift_factor;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
|
||||
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
|
||||
uint8_t nibbles = frame[0x02 + i/2];
|
||||
|
||||
sample = (i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles):
|
||||
get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/
|
||||
sample = ((hist1 * hevag_coefs[coef_index][0] +
|
||||
hist2 * hevag_coefs[coef_index][1] +
|
||||
hist3 * hevag_coefs[coef_index][2] +
|
||||
hist4 * hevag_coefs[coef_index][3]) >> 5) + sample;
|
||||
sample >>= 8;
|
||||
}
|
||||
|
||||
outbuf[sample_count] = clamp16(sample); /*clamping*/
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist4 = hist3;
|
||||
hist3 = hist2;
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
stream->adpcm_history3_32 = hist3;
|
||||
stream->adpcm_history4_32 = hist4;
|
||||
}
|
||||
#include <math.h>
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* PSVita ADPCM table */
|
||||
static const int16_t hevag_coefs[128][4] = {
|
||||
{ 0, 0, 0, 0 },
|
||||
{ 7680, 0, 0, 0 },
|
||||
{ 14720, -6656, 0, 0 },
|
||||
{ 12544, -7040, 0, 0 },
|
||||
{ 15616, -7680, 0, 0 },
|
||||
{ 14731, -7059, 0, 0 },
|
||||
{ 14507, -7366, 0, 0 },
|
||||
{ 13920, -7522, 0, 0 },
|
||||
{ 13133, -7680, 0, 0 },
|
||||
{ 12028, -7680, 0, 0 },
|
||||
{ 10764, -7680, 0, 0 },
|
||||
{ 9359, -7680, 0, 0 },
|
||||
{ 7832, -7680, 0, 0 },
|
||||
{ 6201, -7680, 0, 0 },
|
||||
{ 4488, -7680, 0, 0 },
|
||||
{ 2717, -7680, 0, 0 },
|
||||
{ 910, -7680, 0, 0 },
|
||||
{ -910, -7680, 0, 0 },
|
||||
{ -2717, -7680, 0, 0 },
|
||||
{ -4488, -7680, 0, 0 },
|
||||
{ -6201, -7680, 0, 0 },
|
||||
{ -7832, -7680, 0, 0 },
|
||||
{ -9359, -7680, 0, 0 },
|
||||
{ -10764, -7680, 0, 0 },
|
||||
{ -12028, -7680, 0, 0 },
|
||||
{ -13133, -7680, 0, 0 },
|
||||
{ -13920, -7522, 0, 0 },
|
||||
{ -14507, -7366, 0, 0 },
|
||||
{ -14731, -7059, 0, 0 },
|
||||
{ 5376, -9216, 3328, -3072 },
|
||||
{ -6400, -7168, -3328, -2304 },
|
||||
{ -10496, -7424, -3584, -1024 },
|
||||
{ -167, -2722, -494, -541 },
|
||||
{ -7430, -2221, -2298, 424 },
|
||||
{ -8001, -3166, -2814, 289 },
|
||||
{ 6018, -4750, 2649, -1298 },
|
||||
{ 3798, -6946, 3875, -1216 },
|
||||
{ -8237, -2596, -2071, 227 },
|
||||
{ 9199, 1982, -1382, -2316 },
|
||||
{ 13021, -3044, -3792, 1267 },
|
||||
{ 13112, -4487, -2250, 1665 },
|
||||
{ -1668, -3744, -6456, 840 },
|
||||
{ 7819, -4328, 2111, -506 },
|
||||
{ 9571, -1336, -757, 487 },
|
||||
{ 10032, -2562, 300, 199 },
|
||||
{ -4745, -4122, -5486, -1493 },
|
||||
{ -5896, 2378, -4787, -6947 },
|
||||
{ -1193, -9117, -1237, -3114 },
|
||||
{ 2783, -7108, -1575, -1447 },
|
||||
{ -7334, -2062, -2212, 446 },
|
||||
{ 6127, -2577, -315, -18 },
|
||||
{ 9457, -1858, 102, 258 },
|
||||
{ 7876, -4483, 2126, -538 },
|
||||
{ -7172, -1795, -2069, 482 },
|
||||
{ -7358, -2102, -2233, 440 },
|
||||
{ -9170, -3509, -2674, -391 },
|
||||
{ -2638, -2647, -1929, -1637 },
|
||||
{ 1873, 9183, 1860, -5746 },
|
||||
{ 9214, 1859, -1124, -2427 },
|
||||
{ 13204, -3012, -4139, 1370 },
|
||||
{ 12437, -4792, -256, 622 },
|
||||
{ -2653, -1144, -3182, -6878 },
|
||||
{ 9331, -1048, -828, 507 },
|
||||
{ 1642, -620, -946, -4229 },
|
||||
{ 4246, -7585, -533, -2259 },
|
||||
{ -8988, -3891, -2807, 44 },
|
||||
{ -2562, -2735, -1730, -1899 },
|
||||
{ 3182, -483, -714, -1421 },
|
||||
{ 7937, -3844, 2821, -1019 },
|
||||
{ 10069, -2609, 314, 195 },
|
||||
{ 8400, -3297, 1551, -155 },
|
||||
{ -8529, -2775, -2432, -336 },
|
||||
{ 9477, -1882, 108, 256 },
|
||||
{ 75, -2241, -298, -6937 },
|
||||
{ -9143, -4160, -2963, 5 },
|
||||
{ -7270, -1958, -2156, 460 },
|
||||
{ -2740, 3745, 5936, -1089 },
|
||||
{ 8993, 1948, -683, -2704 },
|
||||
{ 13101, -2835, -3854, 1055 },
|
||||
{ 9543, -1961, 130, 250 },
|
||||
{ 5272, -4270, 3124, -3157 },
|
||||
{ -7696, -3383, -2907, -456 },
|
||||
{ 7309, 2523, 434, -2461 },
|
||||
{ 10275, -2867, 391, 172 },
|
||||
{ 10940, -3721, 665, 97 },
|
||||
{ 24, -310, -1262, 320 },
|
||||
{ -8122, -2411, -2311, -271 },
|
||||
{ -8511, -3067, -2337, 163 },
|
||||
{ 326, -3846, 419, -933 },
|
||||
{ 8895, 2194, -541, -2880 },
|
||||
{ 12073, -1876, -2017, -601 },
|
||||
{ 8729, -3423, 1674, -169 },
|
||||
{ 12950, -3847, -3007, 1946 },
|
||||
{ 10038, -2570, 302, 198 },
|
||||
{ 9385, -2757, 1008, 41 },
|
||||
{ -4720, -5006, -2852, -1161 },
|
||||
{ 7869, -4326, 2135, -501 },
|
||||
{ 2450, -8597, 1299, -2780 },
|
||||
{ 10192, -2763, 360, 181 },
|
||||
{ 11313, -4213, 833, 53 },
|
||||
{ 10154, -2716, 345, 185 },
|
||||
{ 9638, -1417, -737, 482 },
|
||||
{ 3854, -4554, 2843, -3397 },
|
||||
{ 6699, -5659, 2249, -1074 },
|
||||
{ 11082, -3908, 728, 80 },
|
||||
{ -1026, -9810, -805, -3462 },
|
||||
{ 10396, -3746, 1367, -96 },
|
||||
{ 10287, 988, -1915, -1437 },
|
||||
{ 7953, 3878, -764, -3263 },
|
||||
{ 12689, -3375, -3354, 2079 },
|
||||
{ 6641, 3166, 231, -2089 },
|
||||
{ -2348, -7354, -1944, -4122 },
|
||||
{ 9290, -4039, 1885, -246 },
|
||||
{ 4633, -6403, 1748, -1619 },
|
||||
{ 11247, -4125, 802, 61 },
|
||||
{ 9807, -2284, 219, 222 },
|
||||
{ 9736, -1536, -706, 473 },
|
||||
{ 8440, -3436, 1562, -176 },
|
||||
{ 9307, -1021, -835, 509 },
|
||||
{ 1698, -9025, 688, -3037 },
|
||||
{ 10214, -2791, 368, 179 },
|
||||
{ 8390, 3248, -758, -2989 },
|
||||
{ 7201, 3316, 46, -2614 },
|
||||
{ -88, -7809, -538, -4571 },
|
||||
{ 6193, -5189, 2760, -1245 },
|
||||
{ 12325, -1290, -3284, 253 },
|
||||
{ 13064, -4075, -2824, 1877 },
|
||||
{ 5333, 2999, 775, -1132 }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sony's HEVAG (High Efficiency VAG) ADPCM, used in PSVita games (hardware decoded).
|
||||
* Evolution of the regular VAG (same flags and frames), uses 4 history samples and a bigger table.
|
||||
*
|
||||
* Original research and algorithm by id-daemon / daemon1.
|
||||
*/
|
||||
void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
|
||||
uint8_t frame[0x10] = {0};
|
||||
off_t frame_offset;
|
||||
int i, frames_in, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int coef_index, shift_factor, flag;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
int32_t hist3 = stream->adpcm_history3_32;
|
||||
int32_t hist4 = stream->adpcm_history4_32;
|
||||
|
||||
|
||||
/* external interleave (fixed size), mono */
|
||||
bytes_per_frame = 0x10;
|
||||
samples_per_frame = (bytes_per_frame - 0x02) * 2; /* always 28 */
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
coef_index = (frame[0] >> 4) & 0xf;
|
||||
shift_factor = (frame[0] >> 0) & 0xf;
|
||||
coef_index = ((frame[1] >> 0) & 0xf0) | coef_index;
|
||||
flag = (frame[1] >> 0) & 0xf; /* same flags */
|
||||
|
||||
VGM_ASSERT_ONCE(coef_index > 127 || shift_factor > 12, "HEVAG: in+correct coefs/shift at %x\n", (uint32_t)frame_offset);
|
||||
if (coef_index > 127)
|
||||
coef_index = 127; /* ? */
|
||||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* ? */
|
||||
|
||||
shift_factor = 20 - shift_factor;
|
||||
/* decode nibbles */
|
||||
for (i = first_sample; i < first_sample + samples_to_do; i++) {
|
||||
int32_t sample = 0;
|
||||
|
||||
if (flag < 0x07) { /* with flag 0x07 decoded sample must be 0 */
|
||||
uint8_t nibbles = frame[0x02 + i/2];
|
||||
|
||||
sample = (i&1 ? /* low nibble first */
|
||||
get_high_nibble_signed(nibbles):
|
||||
get_low_nibble_signed(nibbles)) << shift_factor; /*scale*/
|
||||
sample = ((hist1 * hevag_coefs[coef_index][0] +
|
||||
hist2 * hevag_coefs[coef_index][1] +
|
||||
hist3 * hevag_coefs[coef_index][2] +
|
||||
hist4 * hevag_coefs[coef_index][3]) >> 5) + sample;
|
||||
sample >>= 8;
|
||||
}
|
||||
|
||||
outbuf[sample_count] = clamp16(sample); /*clamping*/
|
||||
sample_count += channelspacing;
|
||||
|
||||
hist4 = hist3;
|
||||
hist3 = hist2;
|
||||
hist2 = hist1;
|
||||
hist1 = sample;
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
stream->adpcm_history3_32 = hist3;
|
||||
stream->adpcm_history4_32 = hist4;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
#include "coding.h"
|
||||
#include "coding_utils_samples.h"
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
#include "speex/speex.h"
|
||||
|
||||
#define SPEEX_MAX_FRAME_SIZE 0x100 /* frame sizes are stored in a byte */
|
||||
#define SPEEX_MAX_FRAME_SAMPLES 640 /* nb=160, wb/uwb=320*2 */
|
||||
#define SPEEX_CTL_OK 0 /* -1=request unknown, -2=invalid param */
|
||||
#define SPEEX_DECODE_OK 0 /* -1 for end of stream, -2 corrupt stream */
|
||||
|
||||
|
||||
/* opaque struct */
|
||||
struct speex_codec_data {
|
||||
/* config */
|
||||
int channels;
|
||||
int samples_discard;
|
||||
int encoder_delay;
|
||||
|
||||
uint8_t buf[SPEEX_MAX_FRAME_SIZE];
|
||||
uint8_t frame_size;
|
||||
|
||||
int16_t* samples;
|
||||
int frame_samples;
|
||||
|
||||
/* frame state */
|
||||
s16buf_t sbuf;
|
||||
|
||||
void* state;
|
||||
SpeexBits bits;
|
||||
};
|
||||
|
||||
|
||||
/* raw SPEEX */
|
||||
speex_codec_data* init_speex_ea(int channels) {
|
||||
int res, sample_rate;
|
||||
speex_codec_data* data = NULL;
|
||||
|
||||
|
||||
data = calloc(1, sizeof(speex_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
//TODO: EA uses N decoders, unknown layout (known samples are mono)
|
||||
data->channels = channels;
|
||||
if (channels != 1)
|
||||
goto fail;
|
||||
|
||||
/* Modes: narrowband=nb, wideband=wb, ultrawideband=uwb modes.
|
||||
* EASpeex seem to always use uwb so use that for now until config is needed.
|
||||
* Examples normally use &speex_*_mode but export seem problematic? */
|
||||
data->state = speex_decoder_init(speex_lib_get_mode(SPEEX_MODEID_UWB));
|
||||
if (!data->state) goto fail;
|
||||
|
||||
speex_bits_init(&data->bits);
|
||||
|
||||
res = speex_decoder_ctl(data->state, SPEEX_GET_FRAME_SIZE, &data->frame_samples);
|
||||
if (res != SPEEX_CTL_OK) goto fail;
|
||||
|
||||
if (data->frame_samples > SPEEX_MAX_FRAME_SAMPLES)
|
||||
goto fail;
|
||||
|
||||
/* forced in EA's code, doesn't seem to affect decoding (all EAAC headers use this rate too) */
|
||||
sample_rate = 32000;
|
||||
res = speex_decoder_ctl(data->state, SPEEX_SET_SAMPLING_RATE, &sample_rate);
|
||||
if (res != SPEEX_CTL_OK) goto fail;
|
||||
|
||||
/* default "latency" for EASpeex */
|
||||
data->encoder_delay = 509;
|
||||
data->samples_discard = data->encoder_delay;
|
||||
|
||||
data->samples = malloc(channels * data->frame_samples * sizeof(int16_t));
|
||||
if (!data->samples) goto fail;
|
||||
|
||||
return data;
|
||||
|
||||
fail:
|
||||
free_speex(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static int decode_frame(speex_codec_data* data) {
|
||||
int res;
|
||||
|
||||
data->sbuf.samples = data->samples;
|
||||
data->sbuf.channels = 1;
|
||||
data->sbuf.filled = 0;
|
||||
|
||||
speex_bits_read_from(&data->bits, (const char*)data->buf, data->frame_size);
|
||||
|
||||
res = speex_decode_int(data->state, &data->bits, data->sbuf.samples);
|
||||
if (res != SPEEX_DECODE_OK) goto fail;
|
||||
|
||||
data->sbuf.filled = data->frame_samples;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* for simple style speex (seen in EA-Speex and libspeex's sampledec.c) */
|
||||
static int read_frame(speex_codec_data* data, VGMSTREAMCHANNEL* stream) {
|
||||
uint8_t bytes;
|
||||
|
||||
data->frame_size = read_u8(stream->offset, stream->streamfile);
|
||||
stream->offset += 0x01;
|
||||
if (data->frame_size == 0) goto fail;
|
||||
|
||||
bytes = read_streamfile(data->buf, stream->offset, data->frame_size, stream->streamfile);
|
||||
stream->offset += data->frame_size;
|
||||
if (bytes != data->frame_size) goto fail;
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void decode_speex(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) {
|
||||
VGMSTREAMCHANNEL* stream = &vgmstream->ch[0];
|
||||
speex_codec_data* data = vgmstream->codec_data;
|
||||
int ok;
|
||||
|
||||
|
||||
while (samples_to_do > 0) {
|
||||
s16buf_t* sbuf = &data->sbuf;
|
||||
|
||||
if (sbuf->filled <= 0) {
|
||||
ok = read_frame(data, stream);
|
||||
if (!ok) goto fail;
|
||||
|
||||
ok = decode_frame(data);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
if (data->samples_discard)
|
||||
s16buf_discard(&outbuf, sbuf, &data->samples_discard);
|
||||
else
|
||||
s16buf_consume(&outbuf, sbuf, &samples_to_do);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
/* on error just put some 0 samples */
|
||||
VGM_LOG("SPEEX: decode fail at %x, missing %i samples\n", (uint32_t)stream->offset, samples_to_do);
|
||||
s16buf_silence(&outbuf, &samples_to_do, data->channels);
|
||||
}
|
||||
|
||||
|
||||
void reset_speex(speex_codec_data* data) {
|
||||
int res;
|
||||
|
||||
if (!data) return;
|
||||
|
||||
res = speex_decoder_ctl(data->state, SPEEX_RESET_STATE, NULL);
|
||||
if (res != SPEEX_CTL_OK) goto fail;
|
||||
|
||||
data->sbuf.filled = 0;
|
||||
data->samples_discard = data->encoder_delay;
|
||||
|
||||
return;
|
||||
fail:
|
||||
return; /* ? */
|
||||
}
|
||||
|
||||
void seek_speex(VGMSTREAM* vgmstream, int32_t num_sample) {
|
||||
speex_codec_data* data = vgmstream->codec_data;
|
||||
if (!data) return;
|
||||
|
||||
reset_speex(data);
|
||||
data->samples_discard += num_sample;
|
||||
|
||||
/* loop offsets are set during decode; force them to stream start so discard works */
|
||||
if (vgmstream->loop_ch)
|
||||
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset;
|
||||
}
|
||||
|
||||
void free_speex(speex_codec_data* data) {
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
speex_decoder_destroy(data->state);
|
||||
speex_bits_destroy(&data->bits);
|
||||
|
||||
free(data->samples);
|
||||
free(data);
|
||||
}
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -1,162 +1,162 @@
|
|||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do
|
||||
// XA coefs int math in different ways (see comments below), not 100% accurate.
|
||||
// May be implemented like the SNES/SPC700 BRR.
|
||||
|
||||
/* XA ADPCM gain values */
|
||||
#if 0
|
||||
static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
#endif
|
||||
/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */
|
||||
static const int IK0[4] = { 0, -960, -1840, -1568 };
|
||||
static const int IK1[4] = { 0, 0, 832, 880 };
|
||||
|
||||
/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs.
|
||||
* The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new.
|
||||
*
|
||||
* Decoding is defined in diagrams, roughly as:
|
||||
* pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 )
|
||||
* - Range (12-range=shift) and filter index are renewed every ~28 samples.
|
||||
* - nibble is expanded to a signed 16b sample, reimplemented as:
|
||||
* short sample = ((nibble << 12) & 0xf000) >> shift
|
||||
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
|
||||
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N
|
||||
* sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N
|
||||
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N
|
||||
* etc
|
||||
* (rounding differences should be inaudible, so public implementations may be approximations)
|
||||
*
|
||||
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
|
||||
* differently, maybe using one of the above methods in software/CPU, but in XA's case may be done
|
||||
* like the SNES/SPC700 BRR, with specific per-filter ops.
|
||||
* int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
|
||||
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
|
||||
*
|
||||
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
|
||||
* are supported by the CD hardware and will play if found.
|
||||
*
|
||||
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
||||
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
|
||||
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
|
||||
*/
|
||||
|
||||
void decode_xa(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x80] = {0};
|
||||
off_t frame_offset;
|
||||
int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
|
||||
/* data layout (mono):
|
||||
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames
|
||||
* (handled externally, this decoder only gets frames)
|
||||
* - a frame ("sound group") is divided into 8 subframes ("sound unit"), with
|
||||
* subframe headers ("sound parameters") first then subframe nibbles ("sound data")
|
||||
* - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
|
||||
* (repeats may be for error correction, though probably unused)
|
||||
* - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
|
||||
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
|
||||
*
|
||||
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
|
||||
*
|
||||
* example:
|
||||
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
|
||||
*/
|
||||
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
bytes_per_frame = 0x80;
|
||||
samples_per_frame = 28*8 / channelspacing;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
VGM_ASSERT(get_32bitBE(frame+0x0) != get_32bitBE(frame+0x4) || get_32bitBE(frame+0x8) != get_32bitBE(frame+0xC),
|
||||
"bad frames at %x\n", (uint32_t)frame_offset);
|
||||
|
||||
|
||||
/* decode subframes */
|
||||
for (i = 0; i < 8 / channelspacing; i++) {
|
||||
int32_t coef1, coef2;
|
||||
uint8_t coef_index, shift_factor;
|
||||
|
||||
/* parse current subframe (sound unit)'s header (sound parameters) */
|
||||
sp_pos = 0x04 + i*channelspacing + channel;
|
||||
coef_index = (frame[sp_pos] >> 4) & 0xf;
|
||||
shift_factor = (frame[sp_pos] >> 0) & 0xf;
|
||||
|
||||
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos);
|
||||
if (coef_index > 4)
|
||||
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
|
||||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* supposedly, from Nocash PSX docs */
|
||||
|
||||
coef1 = IK0[coef_index];
|
||||
coef2 = IK1[coef_index];
|
||||
|
||||
|
||||
/* decode subframe nibbles */
|
||||
for(j = 0; j < 28; j++) {
|
||||
uint8_t nibbles;
|
||||
int32_t new_sample;
|
||||
|
||||
int su_pos = (channelspacing==1) ?
|
||||
0x10 + j*0x04 + (i/2) : /* mono */
|
||||
0x10 + j*0x04 + i; /* stereo */
|
||||
int get_high_nibble = (channelspacing==1) ?
|
||||
(i&1) : /* mono (even subframes = low, off subframes = high) */
|
||||
(channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */
|
||||
|
||||
/* skip half decodes to make sure hist isn't touched (kinda hack-ish) */
|
||||
if (!(sample_count >= first_sample && samples_done < samples_to_do)) {
|
||||
sample_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
nibbles = frame[su_pos];
|
||||
|
||||
new_sample = get_high_nibble ?
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles >> 0) & 0x0f;
|
||||
|
||||
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
new_sample = new_sample << 4;
|
||||
new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10);
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample; /* must go before clamp, somehow */
|
||||
new_sample = new_sample >> 4;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[samples_done * channelspacing] = new_sample;
|
||||
samples_done++;
|
||||
|
||||
sample_count++;
|
||||
}
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2) {
|
||||
if (is_blocked) {
|
||||
return (bytes / 0x930) * (28*8/ channels) * (is_form2 ? 18 : 16);
|
||||
}
|
||||
else {
|
||||
return (bytes / 0x80) * (28*8 / channels);
|
||||
}
|
||||
}
|
||||
#include "coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
// todo this is based on Kazzuya's old code; different emus (PCSX, Mame, Mednafen, etc) do
|
||||
// XA coefs int math in different ways (see comments below), not 100% accurate.
|
||||
// May be implemented like the SNES/SPC700 BRR.
|
||||
|
||||
/* XA ADPCM gain values */
|
||||
#if 0
|
||||
static const float K0[4] = { 0.0, 0.9375, 1.796875, 1.53125 };
|
||||
static const float K1[4] = { 0.0, 0.0, -0.8125, -0.859375 };
|
||||
#endif
|
||||
/* K0/1 floats to int, -K*2^10 = -K*(1<<10) = -K*1024 */
|
||||
static const int IK0[4] = { 0, -960, -1840, -1568 };
|
||||
static const int IK1[4] = { 0, 0, 832, 880 };
|
||||
|
||||
/* Sony XA ADPCM, defined for CD-DA/CD-i in the "Red Book" (private) or "Green Book" (public) specs.
|
||||
* The algorithm basically is BRR (Bit Rate Reduction) from the SNES SPC700, while the data layout is new.
|
||||
*
|
||||
* Decoding is defined in diagrams, roughly as:
|
||||
* pcm = clamp( signed_nibble * 2^(12-range) + K0[index]*hist1 + K1[index]*hist2 )
|
||||
* - Range (12-range=shift) and filter index are renewed every ~28 samples.
|
||||
* - nibble is expanded to a signed 16b sample, reimplemented as:
|
||||
* short sample = ((nibble << 12) & 0xf000) >> shift
|
||||
* or: int sample = ((nibble << 28) & 0xf0000000) >> (shift + N)
|
||||
* - K0/K1 are float coefs are typically redefined with int math in various ways, with non-equivalent rounding:
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) / 2^N
|
||||
* (sample + K0*2^N*hist1 + K1*2^N*hist2 + [(2^N)/2]) >> N
|
||||
* sample + (K0*2^N*hist1 + K1*2^N*hist2)>>N
|
||||
* sample + (K0*2^N*hist1)>>N + (K1*2^N*hist2)>>N
|
||||
* etc
|
||||
* (rounding differences should be inaudible, so public implementations may be approximations)
|
||||
*
|
||||
* Various XA descendants (PS-ADPCM, EA-XA, NGC DTK, FADPCM, etc) do filters/rounding slightly
|
||||
* differently, maybe using one of the above methods in software/CPU, but in XA's case may be done
|
||||
* like the SNES/SPC700 BRR, with specific per-filter ops.
|
||||
* int coef tables commonly use N = 6 or 8, so K0 0.9375*64 = 60 or 0.9375*256 = 240
|
||||
* PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this.
|
||||
*
|
||||
* XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently
|
||||
* are supported by the CD hardware and will play if found.
|
||||
*
|
||||
* Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf
|
||||
* BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples
|
||||
* (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316
|
||||
*/
|
||||
|
||||
void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
|
||||
uint8_t frame[0x80] = {0};
|
||||
off_t frame_offset;
|
||||
int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0;
|
||||
size_t bytes_per_frame, samples_per_frame;
|
||||
int32_t hist1 = stream->adpcm_history1_32;
|
||||
int32_t hist2 = stream->adpcm_history2_32;
|
||||
|
||||
|
||||
/* data layout (mono):
|
||||
* - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames
|
||||
* (handled externally, this decoder only gets frames)
|
||||
* - a frame ("sound group") is divided into 8 subframes ("sound unit"), with
|
||||
* subframe headers ("sound parameters") first then subframe nibbles ("sound data")
|
||||
* - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header)
|
||||
* (repeats may be for error correction, though probably unused)
|
||||
* - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc
|
||||
* (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc)
|
||||
*
|
||||
* stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R
|
||||
*
|
||||
* example:
|
||||
* subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c
|
||||
* subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d
|
||||
* ...
|
||||
* subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f
|
||||
*/
|
||||
|
||||
/* external interleave (fixed size), mono/stereo */
|
||||
bytes_per_frame = 0x80;
|
||||
samples_per_frame = 28*8 / channelspacing;
|
||||
frames_in = first_sample / samples_per_frame;
|
||||
first_sample = first_sample % samples_per_frame;
|
||||
|
||||
/* parse frame header */
|
||||
frame_offset = stream->offset + bytes_per_frame * frames_in;
|
||||
read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */
|
||||
|
||||
VGM_ASSERT(get_32bitBE(frame+0x0) != get_32bitBE(frame+0x4) || get_32bitBE(frame+0x8) != get_32bitBE(frame+0xC),
|
||||
"bad frames at %x\n", (uint32_t)frame_offset);
|
||||
|
||||
|
||||
/* decode subframes */
|
||||
for (i = 0; i < 8 / channelspacing; i++) {
|
||||
int32_t coef1, coef2;
|
||||
uint8_t coef_index, shift_factor;
|
||||
|
||||
/* parse current subframe (sound unit)'s header (sound parameters) */
|
||||
sp_pos = 0x04 + i*channelspacing + channel;
|
||||
coef_index = (frame[sp_pos] >> 4) & 0xf;
|
||||
shift_factor = (frame[sp_pos] >> 0) & 0xf;
|
||||
|
||||
VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos);
|
||||
if (coef_index > 4)
|
||||
coef_index = 0; /* only 4 filters are used, rest is apparently 0 */
|
||||
if (shift_factor > 12)
|
||||
shift_factor = 9; /* supposedly, from Nocash PSX docs */
|
||||
|
||||
coef1 = IK0[coef_index];
|
||||
coef2 = IK1[coef_index];
|
||||
|
||||
|
||||
/* decode subframe nibbles */
|
||||
for(j = 0; j < 28; j++) {
|
||||
uint8_t nibbles;
|
||||
int32_t new_sample;
|
||||
|
||||
int su_pos = (channelspacing==1) ?
|
||||
0x10 + j*0x04 + (i/2) : /* mono */
|
||||
0x10 + j*0x04 + i; /* stereo */
|
||||
int get_high_nibble = (channelspacing==1) ?
|
||||
(i&1) : /* mono (even subframes = low, off subframes = high) */
|
||||
(channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */
|
||||
|
||||
/* skip half decodes to make sure hist isn't touched (kinda hack-ish) */
|
||||
if (!(sample_count >= first_sample && samples_done < samples_to_do)) {
|
||||
sample_count++;
|
||||
continue;
|
||||
}
|
||||
|
||||
nibbles = frame[su_pos];
|
||||
|
||||
new_sample = get_high_nibble ?
|
||||
(nibbles >> 4) & 0x0f :
|
||||
(nibbles >> 0) & 0x0f;
|
||||
|
||||
new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */
|
||||
new_sample = new_sample << 4;
|
||||
new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10);
|
||||
|
||||
hist2 = hist1;
|
||||
hist1 = new_sample; /* must go before clamp, somehow */
|
||||
new_sample = new_sample >> 4;
|
||||
new_sample = clamp16(new_sample);
|
||||
|
||||
outbuf[samples_done * channelspacing] = new_sample;
|
||||
samples_done++;
|
||||
|
||||
sample_count++;
|
||||
}
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
stream->adpcm_history2_32 = hist2;
|
||||
}
|
||||
|
||||
size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2) {
|
||||
if (is_blocked) {
|
||||
return (bytes / 0x930) * (28*8/ channels) * (is_form2 ? 18 : 16);
|
||||
}
|
||||
else {
|
||||
return (bytes / 0x80) * (28*8 / channels);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,10 @@ void free_codec(VGMSTREAM* vgmstream) {
|
|||
free_imuse(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
free_compresswave(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
free_ea_mt(vgmstream->codec_data, vgmstream->channels);
|
||||
}
|
||||
|
@ -96,6 +100,12 @@ void free_codec(VGMSTREAM* vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
if (vgmstream->coding_type == coding_SPEEX) {
|
||||
free_speex(vgmstream->codec_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type == coding_ACM) {
|
||||
free_acm(vgmstream->codec_data);
|
||||
}
|
||||
|
@ -127,6 +137,10 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
|||
seek_imuse(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
seek_compresswave(vgmstream->codec_data, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
seek_ea_mt(vgmstream, vgmstream->loop_current_sample);
|
||||
}
|
||||
|
@ -171,6 +185,12 @@ void seek_codec(VGMSTREAM* vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
if (vgmstream->coding_type == coding_SPEEX) {
|
||||
seek_speex(vgmstream, vgmstream->loop_current_sample);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
if (vgmstream->coding_type == coding_MPEG_custom ||
|
||||
vgmstream->coding_type == coding_MPEG_ealayer3 ||
|
||||
|
@ -219,6 +239,10 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
|||
reset_imuse(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
reset_compresswave(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_EA_MT) {
|
||||
reset_ea_mt(vgmstream);
|
||||
}
|
||||
|
@ -269,6 +293,12 @@ void reset_codec(VGMSTREAM* vgmstream) {
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
if (vgmstream->coding_type == coding_SPEEX) {
|
||||
reset_speex(vgmstream->codec_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
if (vgmstream->coding_type == coding_FFmpeg) {
|
||||
reset_ffmpeg(vgmstream->codec_data);
|
||||
|
@ -469,6 +499,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
return 0; /* varies per mode */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_COMPRESSWAVE:
|
||||
return 0; /* multiple of 2 */
|
||||
case coding_EA_MT:
|
||||
return 0; /* 432, but variable in looped files */
|
||||
case coding_CIRCUS_VQ:
|
||||
|
@ -492,6 +524,10 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
#ifdef VGM_USE_CELT
|
||||
case coding_CELT_FSB:
|
||||
return 0; /* 512? */
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case coding_SPEEX:
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
return 0;
|
||||
|
@ -673,6 +709,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
return 0; /* varies per mode? */
|
||||
case coding_IMUSE:
|
||||
return 0; /* varies per frame */
|
||||
case coding_COMPRESSWAVE:
|
||||
return 0; /* huffman bits */
|
||||
case coding_EA_MT:
|
||||
return 0; /* variable (frames of bit counts or PCM frames) */
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
|
@ -682,6 +720,10 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
#ifdef VGM_USE_CELT
|
||||
case coding_CELT_FSB:
|
||||
return 0; /* varies, usually 0x80-100 */
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case coding_SPEEX:
|
||||
return 0; /* varies, usually 0x40-60 */
|
||||
#endif
|
||||
default: /* Vorbis, MPEG, ACM, etc */
|
||||
return 0;
|
||||
|
@ -1219,6 +1261,11 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
case coding_CELT_FSB:
|
||||
decode_celt_fsb(vgmstream, buffer, samples_to_do, vgmstream->channels);
|
||||
break;
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case coding_SPEEX:
|
||||
decode_speex(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
#endif
|
||||
case coding_ACM:
|
||||
decode_acm(vgmstream->codec_data, buffer, samples_to_do, vgmstream->channels);
|
||||
|
@ -1375,6 +1422,10 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
decode_imuse(vgmstream, buffer, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_COMPRESSWAVE:
|
||||
decode_compresswave(vgmstream->codec_data, buffer, samples_to_do);
|
||||
break;
|
||||
|
||||
case coding_EA_MT:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_ea_mt(vgmstream, buffer+ch, vgmstream->channels, samples_to_do, ch);
|
||||
|
|
|
@ -118,6 +118,7 @@ static const char* extension_list[] = {
|
|||
"bo2",
|
||||
"brstm",
|
||||
"brstmspm",
|
||||
"bsnd",
|
||||
"btsnd",
|
||||
"bvg",
|
||||
"bwav",
|
||||
|
@ -138,6 +139,7 @@ static const char* extension_list[] = {
|
|||
"csa", //txth/reserved [LEGO Racers 2 (PS2)]
|
||||
"csmp",
|
||||
"cvs",
|
||||
"cwav",
|
||||
"cxs",
|
||||
|
||||
"da",
|
||||
|
@ -250,6 +252,7 @@ static const char* extension_list[] = {
|
|||
"kovs", //fake extension/header id for .kvs
|
||||
"kns",
|
||||
"kraw",
|
||||
"ktac",
|
||||
"ktsl2asbin",
|
||||
"ktss", //fake extension/header id for .kns
|
||||
"kvs",
|
||||
|
@ -303,6 +306,7 @@ static const char* extension_list[] = {
|
|||
"mds",
|
||||
"mdsp",
|
||||
"med",
|
||||
"mjb",
|
||||
"mi4",
|
||||
"mib",
|
||||
"mic",
|
||||
|
@ -781,6 +785,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_OKI4S, "OKI 4-bit ADPCM (4-shift)"},
|
||||
{coding_PTADPCM, "Platinum 4-bit ADPCM"},
|
||||
{coding_IMUSE, "LucasArts iMUSE VIMA ADPCM"},
|
||||
{coding_COMPRESSWAVE, "CompressWave Huffman ADPCM"},
|
||||
|
||||
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
|
||||
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
|
||||
|
@ -825,6 +830,9 @@ static const coding_info coding_info_list[] = {
|
|||
#ifdef VGM_USE_CELT
|
||||
{coding_CELT_FSB, "Custom CELT"},
|
||||
#endif
|
||||
#ifdef VGM_USE_SPEEX
|
||||
{coding_SPEEX, "Custom Speex"},
|
||||
#endif
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{coding_FFmpeg, "FFmpeg"},
|
||||
#endif
|
||||
|
@ -919,7 +927,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_PS2_EXST, "Sony EXST header"},
|
||||
{meta_SVAG_KCET, "Konami SVAG header"},
|
||||
{meta_PS_HEADERLESS, "Headerless PS-ADPCM raw header"},
|
||||
{meta_PS2_MIB_MIH, "Sony MultiStream MIH+MIB header"},
|
||||
{meta_MIB_MIH, "Sony MultiStream MIH+MIB header"},
|
||||
{meta_DSP_MPDSP, "Single DSP header stereo by .mpdsp extension"},
|
||||
{meta_PS2_MIC, "KOEI .MIC header"},
|
||||
{meta_DSP_JETTERS, "Double DSP header stereo by _lr.dsp extension"},
|
||||
|
@ -1326,6 +1334,10 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_SBK, "Team17 SBK header"},
|
||||
{meta_DSP_WIIADPCM, "Exient WIIADPCM header"},
|
||||
{meta_DSP_CWAC, "CRI CWAC header"},
|
||||
{meta_COMPRESSWAVE, "CompressWave .cwav header"},
|
||||
{meta_KTAC, "Koei Tecmo KTAC header"},
|
||||
{meta_MJB_MJH, "Sony MultiStream MJH+MJB header"},
|
||||
{meta_BSNF, "id Software BSNF header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
|
|
@ -243,6 +243,9 @@ static const adxkey_info adxkey9_list[] = {
|
|||
/* Persona 5 Royal (PS4) */
|
||||
{0x0000,0x1c85,0x7043, NULL,29915170}, // 0000000001C87822
|
||||
|
||||
/* Assault Lily Last Bullet (Android) */
|
||||
{0x0aca,0x0ef5,0x05c9, NULL,0}, // guessed with VGAudio (possible key: 5650EF42E5 / 370725044965)
|
||||
|
||||
};
|
||||
|
||||
static const int adxkey8_list_count = sizeof(adxkey8_list) / sizeof(adxkey8_list[0]);
|
||||
|
|
|
@ -1,36 +1,40 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* .AO - from AlphaOgg lib [Cloudphobia (PC)] */
|
||||
VGMSTREAM * init_vgmstream_ao(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if ( !check_extensions(streamFile,"ao") )
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x414C5048) /* "ALPH" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0x414F4747) /* "AOGG" */
|
||||
goto fail;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
|
||||
ovmi.meta_type = meta_AO;
|
||||
/* values at 0x08/0x0c/0x10 may be related to looping? */
|
||||
start_offset = 0xc8;
|
||||
vgmstream = init_vgmstream_ogg_vorbis_callbacks(streamFile, NULL, start_offset, &ovmi);
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* .AO - from AlphaOgg lib [Cloudphobia (PC), GEO ~The Sword Millennia~ Kasumi no Tani no Kaibutsu (PC)] */
|
||||
VGMSTREAM* init_vgmstream_ao(STREAMFILE *sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"ao"))
|
||||
goto fail;
|
||||
if (!is_id64be(0x00,sf, "ALPHAOGG"))
|
||||
goto fail;
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
{
|
||||
ogg_vorbis_meta_info_t ovmi = {0};
|
||||
int sample_rate = read_u32le(0xF0, sf); /* Ogg header */
|
||||
|
||||
ovmi.meta_type = meta_AO;
|
||||
|
||||
ovmi.loop_start = read_f32le(0x08, sf) * sample_rate;
|
||||
ovmi.loop_end = read_f32le(0x0c, sf) * sample_rate; /* also num_samples in some versions */
|
||||
ovmi.loop_end_found = 1;
|
||||
ovmi.loop_flag = read_u8(0x10, sf) != 0; /* count or -1=infinite, u32 in some versions */
|
||||
/* AlphaOgg defines up to 16 loop points for some reason */
|
||||
|
||||
start_offset = 0xc8;
|
||||
vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi);
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .CWAV - from CompressWave lib, found in few Japanese (doujin?) games around 1995-2002 [RADIO ZONDE (PC), GEO ~The Sword Millennia~ (PC)] */
|
||||
VGMSTREAM* init_vgmstream_compresswave(STREAMFILE *sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channels;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "cwav"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id64be(0x00,sf, "CmpWave\0"))
|
||||
goto fail;
|
||||
|
||||
channels = 2; /* always, header channels is internal config */
|
||||
start_offset = 0x00;
|
||||
loop_flag = 1; //read_u8(0x430, sf) != 0; /* wrong count, see below */
|
||||
/* codec allows to use a cipher value, not seen */
|
||||
/* there is also title and artist, but default to "UnTitled" / "NoName" */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_COMPRESSWAVE;
|
||||
vgmstream->sample_rate = 44100; /* always, header rate is internal config */
|
||||
/* in PCM bytes */
|
||||
vgmstream->num_samples = read_u64le(0x418, sf) / sizeof(int16_t) / channels;
|
||||
/* known files have wrong loop values and just repeat */
|
||||
vgmstream->loop_start_sample = 0; //read_u64le(0x420, sf) / sizeof(int16_t) / channels;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples; //read_u64le(0x428, sf) / sizeof(int16_t) / channels;
|
||||
|
||||
vgmstream->codec_data = init_compresswave(sf);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_COMPRESSWAVE;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -1332,7 +1332,6 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
@ -1345,7 +1344,23 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE* sf_head, STREAM
|
|||
}
|
||||
#endif
|
||||
|
||||
case EAAC_CODEC_EASPEEX: /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */ //todo
|
||||
#ifdef VGM_USE_SPEEX
|
||||
case EAAC_CODEC_EASPEEX: { /* "Esp0"?: EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) [FIFA 14 (PS4), FIFA 2020 (Switch)] */
|
||||
/* EASpeex looks normal but simplify with custom IO to avoid worrying about blocks.
|
||||
* First block samples count frames' samples subtracting encoder delay. */
|
||||
|
||||
vgmstream->codec_data = init_speex_ea(eaac.channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_SPEEX;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
temp_sf = setup_eaac_audio_streamfile(sf, eaac.version, eaac.codec, eaac.streamed,0,0, 0x00);
|
||||
if (!temp_sf) goto fail;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
VGM_LOG("EA EAAC: unknown codec 0x%02x\n", eaac.codec);
|
||||
goto fail;
|
||||
|
|
|
@ -87,6 +87,7 @@ static size_t eaac_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
|
|||
case 0x05: /* EALayer3 v1 */
|
||||
case 0x06: /* EALayer3 v2 "PCM" */
|
||||
case 0x07: /* EALayer3 v2 "Spike" */
|
||||
case 0x09: /* EASpeex */
|
||||
case 0x0b: /* EAMP3 */
|
||||
case 0x0c: /* EAOpus */
|
||||
data->skip_size = 0x08;
|
||||
|
@ -208,6 +209,7 @@ static size_t eaac_io_size(STREAMFILE *streamfile, eaac_io_data* data) {
|
|||
case 0x05: /* EALayer3 v1 */
|
||||
case 0x06: /* EALayer3 v2 "PCM" */
|
||||
case 0x07: /* EALayer3 v2 "Spike" */
|
||||
case 0x09: /* EASpeex */
|
||||
case 0x0b: /* EAMP3 */
|
||||
case 0x0c: /* EAOpus */
|
||||
data_size = block_size - 0x08;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef _FSB_ENCRYPTED_STREAMFILE_H_
|
||||
#define _FSB_ENCRYPTED_H_
|
||||
|
||||
#define FSB_KEY_MAX 128 /* probably 32 */
|
||||
#define FSB_KEY_MAX 0x10000 //0x168
|
||||
|
||||
|
||||
typedef struct {
|
||||
|
|
|
@ -385,6 +385,9 @@ static const hcakey_info hcakey_list[] = {
|
|||
/* HoneyWorks Premium Live (Android) */
|
||||
{20200401000000}, // 0000125F45B9D640
|
||||
|
||||
/* Assault Lily Last Bullet (Android) */
|
||||
{6349046567469313}, // 00168E6C99510101
|
||||
|
||||
/* Dragalia Lost (iOS/Android) */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
|
|
|
@ -0,0 +1,489 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "idtech_streamfile.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* mzrt - id Tech 4.5 audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */
|
||||
VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channels, codec, sample_rate, block_size = 0, bps = 0, num_samples;
|
||||
STREAMFILE* temp_sf = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "idwav,idmsf,idxma"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf, "mzrt"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x04, sf) != 0) /* version */
|
||||
goto fail;
|
||||
|
||||
/* this format is bizarrely mis-aligned (and mis-designed too) */
|
||||
|
||||
num_samples = read_s32be(0x11,sf);
|
||||
codec = read_u16le(0x15,sf);
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
case 0x0002:
|
||||
case 0x0166:
|
||||
channels = read_u16le(0x17,sf);
|
||||
sample_rate = read_u32le(0x19, sf);
|
||||
block_size = read_u16le(0x21, sf);
|
||||
bps = read_u16le(0x23,sf);
|
||||
|
||||
start_offset = 0x25;
|
||||
break;
|
||||
|
||||
case 0x0000:
|
||||
sample_rate = read_u32be(0x1D, sf);
|
||||
channels = read_u32be(0x21, sf);
|
||||
|
||||
start_offset = 0x29;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* skip MSADPCM data */
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(sf, start_offset + 0x02 + 0x02))
|
||||
goto fail;
|
||||
|
||||
start_offset += 0x02 + read_u16le(start_offset, sf);
|
||||
}
|
||||
|
||||
/* skip extra data */
|
||||
if (codec == 0x0166) {
|
||||
start_offset += 0x02 + read_u16le(start_offset, sf);
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
if (codec == 0x0000) {
|
||||
start_offset += 0x04 + read_u32be(start_offset, sf) * 0x04;
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
start_offset += 0x04 + read_u32be(start_offset, sf);
|
||||
|
||||
/* skip block info */
|
||||
if (codec != 0x0000) {
|
||||
/* 0x00: de-blocked size
|
||||
* 0x04: block count*/
|
||||
start_offset += 0x08;
|
||||
|
||||
/* idwav only uses 1 super-block though */
|
||||
temp_sf = setup_mzrt_streamfile(sf, start_offset);
|
||||
if (!temp_sf) goto fail;
|
||||
}
|
||||
else {
|
||||
/* 0x00: de-blocked size */
|
||||
start_offset += 0x04;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MZRT;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
if (bps != 16) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channels;
|
||||
break;
|
||||
|
||||
case 0x0002:
|
||||
if (bps != 4) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_size;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0166: {
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
size_t stream_size = get_streamfile_size(temp_sf);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, sf, 0);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(temp_sf, buf,bytes, 0x00,stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples_hb(vgmstream, sf, temp_sf, 0x00, stream_size, 0x15, 0,0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0000: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 576; /* assumed */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sf, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, temp_sf == NULL ? sf : temp_sf, temp_sf == NULL ? start_offset : 0x00))
|
||||
goto fail;
|
||||
close_streamfile(temp_sf);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_sf);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mzrt - id Tech 5 audio [Rage (PS3), The Evil Within (PS3)] */
|
||||
VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, offset;
|
||||
size_t stream_size;
|
||||
int loop_flag, channels, codec, type, sample_rate, block_size = 0, bps = 0;
|
||||
int32_t num_samples, loop_start = 0;
|
||||
STREAMFILE* sb = NULL;
|
||||
const char* extension = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "idmsf")) //idmsa: untested
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf, "mzrt"))
|
||||
goto fail;
|
||||
if (read_u32be(0x04, sf) != 1) /* version */
|
||||
goto fail;
|
||||
|
||||
type = read_s32be(0x09,sf);
|
||||
if (type == 0) { /* Rage */
|
||||
/* 0x0d: null */
|
||||
/* 0x11: crc? */
|
||||
/* 0x15: flag? */
|
||||
offset = 0x19;
|
||||
}
|
||||
else { /* TEW */
|
||||
offset = 0x0D;
|
||||
}
|
||||
|
||||
stream_size = read_u32be(offset + 0x00,sf);
|
||||
offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */
|
||||
|
||||
/* 0x00: crc? */
|
||||
codec = read_u8(offset + 0x04,sf); /* assumed */
|
||||
switch(codec) {
|
||||
case 0x00: {
|
||||
/* 0x05: null? */
|
||||
num_samples = read_s32be(offset + 0x09,sf);
|
||||
/* 0x0D: null? */
|
||||
/* 0x11: loop related? */
|
||||
/* 0x1d: stream size? */
|
||||
/* others: ? */
|
||||
|
||||
/* fmt at 0x31 (codec, avg bitrate, etc) */
|
||||
channels = read_u16le(offset + 0x33, sf);
|
||||
sample_rate = read_u32le(offset + 0x35, sf);
|
||||
block_size = read_u16le(offset + 0x3d, sf);
|
||||
bps = read_u16le(offset + 0x3f, sf);
|
||||
|
||||
/* 0x41: MSADPCM fmt extra */
|
||||
if (!msadpcm_check_coefs(sf, offset + 0x41 + 0x02 + 0x02))
|
||||
goto fail;
|
||||
|
||||
extension = "msadpcm";
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x01: {
|
||||
uint32_t table_entries;
|
||||
|
||||
/* 0x05: stream size? */
|
||||
num_samples = read_s32be(offset + 0x09,sf);
|
||||
/* 0x0D: 0x40? */
|
||||
/* 0x11: loop related? */
|
||||
loop_start = read_s32be(offset + 0x15,sf);
|
||||
/* 0x19: null */
|
||||
table_entries = read_u32be(offset + 0x1d,sf);
|
||||
|
||||
/* skip seek table, format: frame size (16b) + frame samples (16b)
|
||||
* (first entry may be 0 then next entry x2 samples to mimic encoder latency?) */
|
||||
offset += 0x21 + table_entries * 0x04;
|
||||
|
||||
sample_rate = read_u32be(offset + 0x00, sf);
|
||||
channels = read_u32be(offset + 0x04, sf);
|
||||
/* 0x0c: MSF codec */
|
||||
|
||||
extension = type == 0 ? "msadpcm" : "msf";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sb = open_streamfile_by_ext(sf, extension);
|
||||
if (!sb) goto fail;
|
||||
|
||||
if (stream_size != get_streamfile_size(sb))
|
||||
goto fail;
|
||||
|
||||
|
||||
loop_flag = (loop_start > 0);
|
||||
start_offset = 0x00;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MZRT;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
case 0x0002:
|
||||
if (bps != 4) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_size;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x01: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 1152; /* seems ok */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples -= cfg.skip_samples;
|
||||
vgmstream->loop_start_sample -= cfg.skip_samples;
|
||||
vgmstream->loop_end_sample -= cfg.skip_samples;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sb, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sb);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sb);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* bsnf - id Tech 5 audio [Wolfenstein: The New Order (multi), Wolfenstein: The Old Blood (PS4)] */
|
||||
VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, offset, extra_offset;
|
||||
size_t stream_size;
|
||||
int loop_flag, channels, codec, sample_rate; //, block_size = 0, bps = 0;
|
||||
int32_t num_samples, loop_start = 0;
|
||||
STREAMFILE* sb = NULL;
|
||||
const char* suffix = NULL;
|
||||
const char* extension = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "bsnd"))
|
||||
goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sf, "bsnf")) /* null-terminated string */
|
||||
goto fail;
|
||||
if (read_u32be(0x05, sf) != 0x00000100) /* version */
|
||||
goto fail;
|
||||
|
||||
offset = 0x18;
|
||||
|
||||
stream_size = read_u32be(offset + 0x00,sf);
|
||||
offset = read_u32be(offset + 0x04,sf); /* absolute but typically right after this */
|
||||
|
||||
/* 0x00: crc? */
|
||||
/* 0x04: CBR samples or 0 if VBR */
|
||||
num_samples = read_s32be(offset + 0x08,sf);
|
||||
loop_start = read_s32be(offset + 0x0c,sf);
|
||||
/* 0x10: stream size? */
|
||||
|
||||
codec = read_u16le(offset + 0x14,sf);
|
||||
channels = read_u16le(offset + 0x16, sf);
|
||||
sample_rate = read_u32le(offset + 0x18, sf);
|
||||
//block_size = read_u16le(offset + 0x20, sf);
|
||||
//bps = read_u16le(offset + 0x22, sf);
|
||||
|
||||
extra_offset = offset + 0x24;
|
||||
extension = "ogg"; /* same for all codecs */
|
||||
switch(codec) {
|
||||
case 0x0055: /* msf */
|
||||
/* 0x00: table entries */
|
||||
/* 0x04: seek table, format: frame size (16b) + frame samples (16b) */
|
||||
suffix = "_msf.bsnd";
|
||||
break;
|
||||
|
||||
case 0x0166: /* xma */
|
||||
/* 0x00: extra size */
|
||||
/* 0x02: xma config and block table */
|
||||
suffix = "_xma.bsnd";
|
||||
break;
|
||||
|
||||
case 0x674F: /* vorbis */
|
||||
/* 0x00: extra size */
|
||||
/* 0x02: num samples */
|
||||
suffix = "_vorbis.bsnd";
|
||||
goto fail; //untested
|
||||
//break;
|
||||
|
||||
case 0x42D2: /* at9 */
|
||||
/* 0x00: extra size */
|
||||
/* 0x02: encoder delay */
|
||||
/* 0x04: channel config */
|
||||
/* 0x08: ATRAC9 GUID */
|
||||
/* 0x1c: ATRAC9 config */
|
||||
suffix = "_at9.bsnd";
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{
|
||||
int suffix_len = strlen(suffix);
|
||||
int filename_len;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
get_streamfile_filename(sf, filename, sizeof(filename));
|
||||
filename_len = strlen(filename);
|
||||
|
||||
if (filename_len < suffix_len)
|
||||
goto fail;
|
||||
filename[filename_len - suffix_len + 0] = '.';
|
||||
filename[filename_len - suffix_len + 1] = '\0';
|
||||
strcat(filename, extension);
|
||||
|
||||
sb = open_streamfile_by_filename(sf, filename);
|
||||
if (!sb) goto fail;
|
||||
}
|
||||
|
||||
if (stream_size != get_streamfile_size(sb))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (loop_start > 0);
|
||||
start_offset = 0x00;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_BSNF;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = loop_start;
|
||||
vgmstream->loop_end_sample = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0055: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 1152; /* seems ok */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples -= cfg.skip_samples;
|
||||
vgmstream->loop_start_sample -= cfg.skip_samples;
|
||||
vgmstream->loop_end_sample -= cfg.skip_samples;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0166: {
|
||||
uint8_t buf[0x100];
|
||||
size_t bytes, block_size, block_count;
|
||||
|
||||
block_size = 0x800;
|
||||
block_count = stream_size / block_size;
|
||||
|
||||
bytes = ffmpeg_make_riff_xma2(buf, sizeof(buf), num_samples, stream_size, channels, sample_rate, block_count, block_size);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf, bytes, start_offset, stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples(vgmstream, sb, start_offset, stream_size, 0x00, 1,1);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
case 0x674F: {
|
||||
vgmstream->codec_data = init_ogg_vorbis(sb, start_offset, stream_size, NULL);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_OGG_VORBIS;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x42D2: {
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.encoder_delay = read_u16le(extra_offset + 0x02,sf);
|
||||
cfg.config_data = read_u32be(extra_offset + 0x1c,sf);
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_ATRAC9;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sb, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sb);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sb);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -0,0 +1,514 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
int channels;
|
||||
int sample_rate;
|
||||
int loop_flag;
|
||||
int32_t num_samples;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
uint32_t file_size;
|
||||
uint32_t stream_offset;
|
||||
uint32_t stream_size;
|
||||
uint32_t table_offset;
|
||||
uint32_t table_entries;
|
||||
int type;
|
||||
int encoder_delay;
|
||||
int end_padding;
|
||||
int frame_samples;
|
||||
} ktac_header_t;
|
||||
|
||||
|
||||
static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf);
|
||||
|
||||
/* KTAC - Koei Tecmo custom AAC [Kin'iro no Corda 3 (Vita), Shingeki no Kyojin: Shichi kara no Dasshutsu (3DS), Dynasty Warriors (PS4)] */
|
||||
VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
ktac_header_t ktac = {0};
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .ktac: header id */
|
||||
if (!check_extensions(sf,"ktac"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "KTAC"))
|
||||
goto fail;
|
||||
|
||||
/* 0x04: version? (always 1) */
|
||||
ktac.file_size = read_u32le(0x08,sf);
|
||||
if (ktac.file_size != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
ktac.stream_offset = read_u32le(0x0c,sf);
|
||||
ktac.stream_size = read_u32le(0x10,sf);
|
||||
ktac.type = read_u32le(0x14,sf);
|
||||
ktac.sample_rate = read_u32le(0x18,sf);
|
||||
ktac.num_samples = read_u32le(0x1c,sf); /* full samples */
|
||||
ktac.channels = read_u16le(0x20,sf);
|
||||
ktac.frame_samples = read_u16le(0x22,sf);
|
||||
ktac.encoder_delay = read_u16le(0x24,sf);
|
||||
ktac.end_padding = read_u16le(0x26,sf);
|
||||
ktac.loop_start = read_u32le(0x28,sf);
|
||||
ktac.loop_end = read_u32le(0x2c,sf);
|
||||
/* 0x30: ? (big, related to loops) */
|
||||
/* 0x34: ? (always null) */
|
||||
ktac.table_offset = read_u32le(0x38,sf);
|
||||
ktac.table_entries= read_u32le(0x3c,sf);
|
||||
|
||||
ktac.loop_flag = (ktac.loop_end > 0);
|
||||
|
||||
/* type 1 files crash during sample_copy, wrong fake header/esds?
|
||||
* (0=AoT, KnC3 bgm, 1=KnC3 1ch voices, 2=DW4, Atelier Ryza) */
|
||||
if (ktac.type == 1)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ktac.channels, ktac.loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_KTAC;
|
||||
vgmstream->sample_rate = ktac.sample_rate;
|
||||
vgmstream->num_samples = ktac.num_samples - ktac.encoder_delay - ktac.end_padding;
|
||||
vgmstream->loop_start_sample = ktac.loop_start * ktac.frame_samples - ktac.encoder_delay;
|
||||
vgmstream->loop_end_sample = ktac.loop_end * ktac.frame_samples - ktac.encoder_delay;
|
||||
|
||||
/* KTAC uses AAC, but not type found in .aac (that has headered frames, like mp3) but raw
|
||||
* packets + frame size table (similar to .mp4/m4a). We make a fake M4A header to feed FFmpeg */
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
ffmpeg_codec_data* ffmpeg_data = NULL;
|
||||
int bytes;
|
||||
uint8_t* buf = NULL;
|
||||
int buf_len = 0x400 + ktac.table_entries * 0x4;
|
||||
|
||||
if (buf_len > 0x100000) /* ??? */
|
||||
goto fail;
|
||||
|
||||
buf = malloc(buf_len);
|
||||
if (!buf) goto fail;
|
||||
|
||||
bytes = make_m4a_header(buf, buf_len, &ktac, sf);
|
||||
ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, ktac.stream_offset, ktac.stream_size);
|
||||
free(buf);
|
||||
|
||||
if (!ffmpeg_data) goto fail;
|
||||
vgmstream->codec_data = ffmpeg_data;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
/* not part of fake header since it's kinda complex to add (iTunes string comment) */
|
||||
ffmpeg_set_skip_samples(ffmpeg_data, ktac.encoder_delay);
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* *********************************************************** */
|
||||
|
||||
/* Helpers for M4A headers, an insane soup of chunks (AKA "atoms").
|
||||
* Needs *A LOT* of atoms and fields so this is more elaborate than usual.
|
||||
* - https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFPreface/qtffPreface.html
|
||||
*/
|
||||
|
||||
/* generic additions */
|
||||
typedef struct {
|
||||
uint8_t* out;
|
||||
int bytes;
|
||||
} m4a_state_t;
|
||||
|
||||
typedef struct {
|
||||
STREAMFILE* sf;
|
||||
ktac_header_t* ktac; /* config */
|
||||
uint8_t* out; /* current position */
|
||||
int bytes; /* written bytes */
|
||||
m4a_state_t chunks; /* chunks offsets are absolute, save position until we know header size */
|
||||
} m4a_header_t;
|
||||
|
||||
static void add_u32b(m4a_header_t* h, uint32_t value) {
|
||||
put_u32be(h->out, value);
|
||||
h->out += 0x04;
|
||||
h->bytes += 0x04;
|
||||
}
|
||||
|
||||
static void add_u24b(m4a_header_t* h, uint32_t value) {
|
||||
put_u16be(h->out + 0x00, (value >> 8u) & 0xFFFF);
|
||||
put_u8 (h->out + 0x02, (value >> 0u) & 0xFF);
|
||||
h->out += 0x03;
|
||||
h->bytes += 0x03;
|
||||
}
|
||||
|
||||
static void add_u16b(m4a_header_t* h, uint16_t value) {
|
||||
put_u16be(h->out, value);
|
||||
h->out += 0x02;
|
||||
h->bytes += 0x02;
|
||||
}
|
||||
|
||||
static void add_u8(m4a_header_t* h, uint32_t value) {
|
||||
put_u8(h->out, value);
|
||||
h->out += 0x01;
|
||||
h->bytes += 0x01;
|
||||
}
|
||||
|
||||
static void add_name(m4a_header_t* h, const char* name) {
|
||||
memcpy(h->out, name, 0x4);
|
||||
h->out += 0x04;
|
||||
h->bytes += 0x04;
|
||||
}
|
||||
|
||||
static void add_atom(m4a_header_t* h, const char* name, uint32_t size) {
|
||||
add_u32b(h, size);
|
||||
add_name(h, name);
|
||||
}
|
||||
|
||||
/* register + write final size for atoms of variable/complex size */
|
||||
static void save_atom(m4a_header_t* h, m4a_state_t* s) {
|
||||
s->out = h->out;
|
||||
s->bytes = h->bytes;
|
||||
}
|
||||
|
||||
static void load_atom(m4a_header_t* h, m4a_state_t* s) {
|
||||
put_u32be(s->out, h->bytes - s->bytes);
|
||||
}
|
||||
|
||||
/* common atoms */
|
||||
|
||||
static void add_ftyp(m4a_header_t* h) {
|
||||
add_atom(h, "ftyp", 0x18);
|
||||
add_name(h, "M4A "); /* major brand */
|
||||
add_u32b(h, 512); /* minor version */
|
||||
add_name(h, "isom"); /* compatible brands */
|
||||
add_name(h, "iso2"); /* compatible brands */
|
||||
}
|
||||
|
||||
static void add_free(m4a_header_t* h) {
|
||||
add_atom(h, "free", 0x08);
|
||||
}
|
||||
|
||||
static void add_mdat(m4a_header_t* h) {
|
||||
add_atom(h, "mdat", 0x08 + h->ktac->stream_size);
|
||||
}
|
||||
|
||||
/* variable atoms */
|
||||
|
||||
|
||||
static void add_stco(m4a_header_t* h) {
|
||||
add_atom(h, "stco", 0x10 + 1 * 0x04);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
/* there may be an entry per frame, but only first seems needed */
|
||||
save_atom(h, &h->chunks);
|
||||
add_u32b(h, 0); /* Absolute offset N */
|
||||
}
|
||||
static void add_stsz(m4a_header_t* h) {
|
||||
int i;
|
||||
|
||||
add_atom(h, "stsz", 0x14 + h->ktac->table_entries * 0x04);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 0); /* Sample size (CBR) */
|
||||
add_u32b(h, h->ktac->table_entries); /* Number of entries (VBR) */
|
||||
for (i = 0; i < h->ktac->table_entries; i++) {
|
||||
uint32_t size = read_u32le(h->ktac->table_offset + i*0x04, h->sf);
|
||||
add_u32b(h, size); /* Sample N */
|
||||
}
|
||||
}
|
||||
|
||||
static void add_stsc(m4a_header_t* h) {
|
||||
add_atom(h, "stsc", 0x1c);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_u32b(h, 1); /* First chunk */
|
||||
add_u32b(h, h->ktac->table_entries); /* Samples per chunk */
|
||||
add_u32b(h, 1); /* Sample description ID */
|
||||
}
|
||||
|
||||
static void add_stts(m4a_header_t* h) {
|
||||
add_atom(h, "stts", 0x18);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_u32b(h, h->ktac->table_entries); /* Sample count */
|
||||
add_u32b(h, h->ktac->frame_samples); /* Sample duration */
|
||||
}
|
||||
|
||||
/* from mpeg4audio.c (also see ff_mp4_read_dec_config_descr) */
|
||||
static const int m4a_sample_rates[16] = {
|
||||
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
|
||||
};
|
||||
static const uint8_t m4a_channels[14] = {
|
||||
0,
|
||||
1, // mono (1/0)
|
||||
2, // stereo (2/0)
|
||||
3, // 3/0
|
||||
4, // 3/1
|
||||
5, // 3/2
|
||||
6, // 3/2.1
|
||||
8, // 5/2.1
|
||||
//0,
|
||||
//0,
|
||||
//0,
|
||||
//7, // 3/3.1
|
||||
//8, // 3/2/2.1
|
||||
//24 // 3/3/3 - 5/2/3 - 3/0/0.2
|
||||
};
|
||||
|
||||
static void add_esds(m4a_header_t* h) {
|
||||
uint16_t config = 0;
|
||||
|
||||
/* ES_descriptor (TLV format see ISO 14496-1) and DecSpecificInfoTag define actual decoding
|
||||
- config (channels/rate/etc), other atoms with the same stuff is just info
|
||||
* - http://ecee.colorado.edu/~ecen5653/ecen5653/papers/ISO%2014496-1%202004.PDF */
|
||||
|
||||
{
|
||||
uint8_t object_type = 0x02; /* 0x00=none, 0x01=AAC main, 0x02=AAC LC */
|
||||
uint8_t sr_index = 0;
|
||||
uint8_t ch_index = 0;
|
||||
uint8_t unknown = 0;
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (m4a_sample_rates[i] == h->ktac->sample_rate) {
|
||||
sr_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (m4a_channels[i] == h->ktac->channels) {
|
||||
ch_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
config |= (object_type & 0x1F) << 11; /* 5b */
|
||||
config |= (sr_index & 0x0F) << 7; /* 4b */
|
||||
config |= (ch_index & 0x0F) << 3; /* 4b */
|
||||
config |= (unknown & 0x07) << 0; /* 3b */
|
||||
}
|
||||
|
||||
add_atom(h, "esds", 0x33);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
|
||||
add_u8 (h, 0x03); /* ES_DescrTag */
|
||||
add_u32b(h, 0x80808022); /* size 0x22 */
|
||||
add_u16b(h, 0x0000); /* stream Id */
|
||||
add_u8 (h, 0x00); /* flags */
|
||||
|
||||
add_u8 (h, 0x04); /* DecoderConfigDescrTag */
|
||||
add_u32b(h, 0x80808014); /* size 0x14 */
|
||||
add_u8 (h, 0x40); /* object type (0x40=audio) */
|
||||
add_u8 (h, 0x15); /* stream type (6b: 0x5=audio) + upstream (1b) + reserved (1b: const 1) */
|
||||
add_u24b(h, 0x000000); /* buffer size */
|
||||
add_u32b(h, 0); /* max bitrate (256000?)*/
|
||||
add_u32b(h, 0); /* average bitrate (256000?) */
|
||||
|
||||
add_u8 (h, 0x05); /* DecSpecificInfoTag */
|
||||
add_u32b(h, 0x80808002); /* size 0x02 */
|
||||
add_u16b(h, config); /* actual decoder info */
|
||||
|
||||
add_u8 (h, 0x06); /* SLConfigDescrTag */
|
||||
add_u32b(h, 0x80808001); /* size 0x01 */
|
||||
add_u8 (h, 0x02); /* predefined (2=default) */
|
||||
}
|
||||
|
||||
static void add_mp4a(m4a_header_t* h) {
|
||||
add_atom(h, "mp4a", 0x57);
|
||||
add_u32b(h, 0); /* ? */
|
||||
add_u32b(h, 1); /* Data reference index */
|
||||
add_u32b(h, 0); /* Reserved */
|
||||
add_u32b(h, 0); /* Reserved 2 */
|
||||
add_u16b(h, h->ktac->channels); /* Channel count */
|
||||
add_u16b(h, 16); /* Sample size */
|
||||
add_u32b(h, 0); /* Pre-defined */
|
||||
add_u16b(h, h->ktac->sample_rate); /* Sample rate */
|
||||
add_u16b(h, 0); /* ? */
|
||||
add_esds(h); /* elementary stream descriptor */
|
||||
}
|
||||
|
||||
static void add_stsd(m4a_header_t* h) {
|
||||
add_atom(h, "stsd", 0x67);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_mp4a(h);
|
||||
}
|
||||
|
||||
static void add_stbl(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "stbl", 0x00);
|
||||
add_stsd(h); /* Sample description */
|
||||
add_stts(h); /* Time-to-sample */
|
||||
add_stsc(h); /* Sample-to-chunk */
|
||||
add_stsz(h); /* Sample size */
|
||||
add_stco(h); /* Chunk offset */
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_dinf(m4a_header_t* h) {
|
||||
add_atom(h, "dinf", 0x24);
|
||||
add_atom(h, "dref", 0x1c);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 1); /* Number of entries */
|
||||
add_atom(h, "url ", 0x0c);
|
||||
add_u32b(h, 1); /* Version (1 byte) + Flags (3 byte) */
|
||||
}
|
||||
|
||||
static void add_smhd(m4a_header_t* h) {
|
||||
add_atom(h, "smhd", 0x10);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u16b(h, 0); /* Balance */
|
||||
add_u16b(h, 0); /* Reserved */
|
||||
}
|
||||
|
||||
static void add_minf(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "minf", 0x00);
|
||||
add_smhd(h);
|
||||
add_dinf(h);
|
||||
add_stbl(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_hdlr(m4a_header_t* h) {
|
||||
add_atom(h, "hdlr", 0x22);
|
||||
add_u32b(h, 0); /* version (1 byte) + flags (3 byte) */
|
||||
add_u32b(h, 0); /* Component type */
|
||||
add_name(h, "soun"); /* Component subtype */
|
||||
add_u32b(h, 0); /* Component manufacturer */
|
||||
add_u32b(h, 0); /* Component flags */
|
||||
add_u32b(h, 0); /* Component flags mask */
|
||||
add_u16b(h, 0); /* Component name */
|
||||
}
|
||||
|
||||
static void add_mdhd(m4a_header_t* h) {
|
||||
add_atom(h, "mdhd", 0x20);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 0); /* Creation time */
|
||||
add_u32b(h, 0); /* Modification time */
|
||||
add_u32b(h, h->ktac->sample_rate); /* Time scale */
|
||||
add_u32b(h, h->ktac->num_samples); /* Duration */
|
||||
add_u16b(h, 0); /* Language (0xC455=eng?) */
|
||||
add_u16b(h, 0); /* Quality */
|
||||
}
|
||||
|
||||
static void add_mdia(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "mdia", 0x00);
|
||||
add_mdhd(h);
|
||||
add_hdlr(h);
|
||||
add_minf(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_tkhd(m4a_header_t* h) {
|
||||
add_atom(h, "tkhd", 0x5C);
|
||||
add_u32b(h, 0x00000001); /* Version (1 byte) + Flags (3 byte), 1=track enabled */
|
||||
add_u32b(h, 0); /* Creation time */
|
||||
add_u32b(h, 0); /* Modification time */
|
||||
add_u32b(h, 1); /* Track ID */
|
||||
add_u32b(h, 0); /* Reserved 1 */
|
||||
add_u32b(h, h->ktac->num_samples); /* Duration */
|
||||
add_u32b(h, 0); /* Reserved 1 */
|
||||
add_u32b(h, 0); /* Reserved 2 */
|
||||
add_u16b(h, 0); /* Layer */
|
||||
add_u16b(h, 0); /* Alternate group (1?) */
|
||||
add_u16b(h, 0x0100); /* Volume */
|
||||
add_u16b(h, 0); /* Reserved */
|
||||
add_u32b(h, 0x00010000); /* matrix_A */
|
||||
add_u32b(h, 0); /* matrix_B */
|
||||
add_u32b(h, 0); /* matrix_U */
|
||||
add_u32b(h, 0); /* matrix_C */
|
||||
add_u32b(h, 0x00010000); /* matrix_D */
|
||||
add_u32b(h, 0); /* matrix_V */
|
||||
add_u32b(h, 0); /* matrix_X */
|
||||
add_u32b(h, 0); /* matrix_Y */
|
||||
add_u32b(h, 0x40000000); /* matrix_W */
|
||||
add_u32b(h, 0); /* Width */
|
||||
add_u32b(h, 0); /* Height */
|
||||
}
|
||||
|
||||
static void add_trak(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "trak", 0x00);
|
||||
add_tkhd(h);
|
||||
add_mdia(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
static void add_mvhd(m4a_header_t* h) {
|
||||
add_atom(h, "mvhd", 0x6c);
|
||||
add_u32b(h, 0); /* Version (1 byte) + Flags (3 byte) */
|
||||
add_u32b(h, 0); /* Creation time */
|
||||
add_u32b(h, 0); /* Modification time */
|
||||
add_u32b(h, h->ktac->sample_rate); /* Time scale */
|
||||
add_u32b(h, h->ktac->num_samples); /* Duration */
|
||||
add_u32b(h, 0x00010000); /* Preferred rate */
|
||||
add_u16b(h, 0x0100); /* Preferred volume */
|
||||
add_u32b(h, 0); /* Reserved 1 */
|
||||
add_u32b(h, 0); /* Reserved 2 */
|
||||
add_u16b(h, 0); /* Reserved 3 */
|
||||
add_u32b(h, 0x00010000); /* matrix_A */
|
||||
add_u32b(h, 0); /* matrix_B */
|
||||
add_u32b(h, 0); /* matrix_U */
|
||||
add_u32b(h, 0); /* matrix_C */
|
||||
add_u32b(h, 0x00010000); /* matrix_D */
|
||||
add_u32b(h, 0); /* matrix_V */
|
||||
add_u32b(h, 0); /* matrix_X */
|
||||
add_u32b(h, 0); /* matrix_Y */
|
||||
add_u32b(h, 0x40000000); /* matrix_W */
|
||||
add_u32b(h, 0); /* Preview time */
|
||||
add_u32b(h, 0); /* Preview duration */
|
||||
add_u32b(h, 0); /* Poster time */
|
||||
add_u32b(h, 0); /* Selection time */
|
||||
add_u32b(h, 0); /* Selection duration */
|
||||
add_u32b(h, 0); /* Current time */
|
||||
add_u32b(h, 2); /* Next track ID */
|
||||
}
|
||||
|
||||
static void add_moov(m4a_header_t* h) {
|
||||
m4a_state_t s;
|
||||
|
||||
save_atom(h, &s);
|
||||
add_atom(h, "moov", 0x00);
|
||||
add_mvhd(h);
|
||||
add_trak(h);
|
||||
//add_udta(h);
|
||||
load_atom(h, &s);
|
||||
}
|
||||
|
||||
/* *** */
|
||||
|
||||
static int make_m4a_header(uint8_t* buf, int buf_len, ktac_header_t* ktac, STREAMFILE* sf) {
|
||||
m4a_header_t h = {0};
|
||||
|
||||
if (buf_len < 0x300 + ktac->table_entries * 0x4) /* approx */
|
||||
goto fail;
|
||||
|
||||
h.sf = sf;
|
||||
h.ktac = ktac;
|
||||
h.out = buf;
|
||||
|
||||
add_ftyp(&h);
|
||||
add_free(&h);
|
||||
add_moov(&h);
|
||||
add_mdat(&h);
|
||||
|
||||
|
||||
/* define absolute chunk offset after all calcs */
|
||||
put_u32be(h.chunks.out, h.bytes);
|
||||
|
||||
return h.bytes;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
|
@ -851,7 +851,9 @@ VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_smk(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_mzrt(STREAMFILE * streamFile);
|
||||
VGMSTREAM* init_vgmstream_mzrt_v0(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_mzrt_v1(STREAMFILE* sf);
|
||||
VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile);
|
||||
|
||||
|
@ -936,4 +938,10 @@ VGMSTREAM* init_vgmstream_ifs(STREAMFILE* sf);
|
|||
|
||||
VGMSTREAM* init_vgmstream_acx(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_compresswave(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
|
|
@ -1,72 +1,72 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_mib_mih(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
off_t header_offset, start_offset;
|
||||
size_t data_size, frame_size, frame_last, frame_count, name_size;
|
||||
int channel_count, loop_flag, sample_rate;
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(streamFile, "mib"))
|
||||
goto fail;
|
||||
|
||||
streamHeader = open_streamfile_by_ext(streamFile,"mih");
|
||||
if (!streamHeader) goto fail;
|
||||
|
||||
header_offset = 0x00;
|
||||
|
||||
if (read_32bitLE(0x00,streamHeader) != 0x40) { /* header size */
|
||||
name_size = read_32bitLE(0x00, streamHeader);
|
||||
if (read_32bitLE(0x04 + name_size, streamHeader) == 0x40 &&
|
||||
read_32bitLE(0x04 + name_size + 0x04, streamHeader) == 0x40) {
|
||||
/* Marc Ecko's Getting Up (PS2) has a name at the start */
|
||||
header_offset = 0x04 + name_size + 0x04;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
loop_flag = 0; /* MIB+MIH don't loop (nor use PS-ADPCM flags) per spec */
|
||||
start_offset = 0x00;
|
||||
|
||||
/* 0x04: padding size (always 0x20, MIH header must be multiple of 0x40) */
|
||||
frame_last = (uint32_t)read_32bitLE(header_offset + 0x05,streamHeader) & 0x00FFFFFF; /* 24b */
|
||||
channel_count = read_32bitLE(header_offset + 0x08,streamHeader);
|
||||
sample_rate = read_32bitLE(header_offset + 0x0c,streamHeader);
|
||||
frame_size = read_32bitLE(header_offset + 0x10,streamHeader);
|
||||
frame_count = read_32bitLE(header_offset + 0x14,streamHeader);
|
||||
if (frame_count == 0) { /* rarely [Gladius (PS2)] */
|
||||
frame_count = get_streamfile_size(streamFile) / (frame_size * channel_count);
|
||||
}
|
||||
|
||||
data_size = frame_count * frame_size;
|
||||
if (frame_last)
|
||||
data_size -= frame_size - frame_last; /* last frame has less usable data */
|
||||
data_size *= channel_count;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
|
||||
vgmstream->meta_type = meta_PS2_MIB_MIH;
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = frame_size;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
close_streamfile(streamHeader);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamHeader);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* MIB+MIH - SCEE MultiStream interleaved bank (header+data) [namCollection: Ace Combat 2 (PS2), Rampage: Total Destruction (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_mib_mih(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
off_t header_offset, start_offset;
|
||||
size_t data_size, frame_size, frame_last, frame_count;
|
||||
int channels, loop_flag, sample_rate;
|
||||
|
||||
/* check extension */
|
||||
if (!check_extensions(sf, "mib"))
|
||||
goto fail;
|
||||
|
||||
sh = open_streamfile_by_ext(sf,"mih");
|
||||
if (!sh) goto fail;
|
||||
|
||||
header_offset = 0x00;
|
||||
|
||||
if (read_u32le(0x00,sh) != 0x40) { /* header size */
|
||||
/* Marc Ecko's Getting Up (PS2) has a name at the start (hack, not standard .mib+mih) */
|
||||
size_t name_size = read_u32le(0x00, sh);
|
||||
if (read_u32le(0x04 + name_size, sh) == 0x40 &&
|
||||
read_u32le(0x04 + name_size + 0x04, sh) == 0x40) {
|
||||
header_offset = 0x04 + name_size + 0x04;
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
loop_flag = 0; /* MIB+MIH don't loop (nor use PS-ADPCM flags) per spec */
|
||||
start_offset = 0x00;
|
||||
|
||||
/* 0x04: padding size (always 0x20, MIH header must be multiple of 0x40) */
|
||||
frame_last = read_u32le(header_offset + 0x05,sh) & 0x00FFFFFF; /* 24b */
|
||||
channels = read_u32le(header_offset + 0x08,sh);
|
||||
sample_rate = read_u32le(header_offset + 0x0c,sh);
|
||||
frame_size = read_u32le(header_offset + 0x10,sh);
|
||||
frame_count = read_u32le(header_offset + 0x14,sh);
|
||||
if (frame_count == 0) { /* rarely [Gladius (PS2)] */
|
||||
frame_count = get_streamfile_size(sf) / (frame_size * channels);
|
||||
}
|
||||
|
||||
data_size = frame_count * frame_size;
|
||||
if (frame_last)
|
||||
data_size -= frame_size - frame_last; /* last frame has less usable data */
|
||||
data_size *= channels;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MIB_MIH;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = frame_size;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* MJB+MJH - SCEE MultiStream? bank of MIB+MIH [Star Wars: Bounty Hunter (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
off_t start_offset = 0, header_offset = 0;
|
||||
size_t data_size, frame_size, frame_count;
|
||||
int channels, loop_flag, sample_rate;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "mjb"))
|
||||
goto fail;
|
||||
|
||||
sh = open_streamfile_by_ext(sf, "mjh");
|
||||
if (!sh) goto fail;
|
||||
|
||||
/* parse entries */
|
||||
{
|
||||
int i;
|
||||
off_t subsong_offset = 0;
|
||||
|
||||
total_subsongs = read_s32le(0x00,sh);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
for (i = 0; i < total_subsongs; i++) {
|
||||
off_t offset = 0x40 + 0x40 * i;
|
||||
size_t subsong_size = read_u32le(offset + 0x08,sh) * read_u32le(offset + 0x10,sh) * read_u32le(offset + 0x14,sh);
|
||||
|
||||
if (i + 1== target_subsong) {
|
||||
header_offset = offset;
|
||||
start_offset = subsong_offset;
|
||||
}
|
||||
subsong_offset += subsong_size;
|
||||
}
|
||||
|
||||
if (!header_offset)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
VGM_LOG("3: %lx\n", start_offset);
|
||||
|
||||
/* also see MIB+MIH (same thing but this excludes padding stuff) */
|
||||
if (read_u32le(header_offset + 0x00,sh) != 0x40)
|
||||
goto fail;
|
||||
channels = read_u32le(header_offset + 0x08,sh);
|
||||
sample_rate = read_u32le(header_offset + 0x0c,sh);
|
||||
frame_size = read_u32le(header_offset + 0x10,sh);
|
||||
frame_count = read_u32le(header_offset + 0x14,sh);
|
||||
|
||||
data_size = frame_count * frame_size * channels;
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MJB_MJH;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channels);
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = data_size;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = frame_size;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -1,85 +1,82 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* MSB+MSH - SCEE MultiStream flat bank [namCollection: Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
STREAMFILE * streamHeader = NULL;
|
||||
off_t start_offset, header_offset = 0;
|
||||
size_t stream_size;
|
||||
int loop_flag = 0, channel_count, sample_rate;
|
||||
int total_subsongs, target_subsong = streamFile->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "msb"))
|
||||
goto fail;
|
||||
|
||||
streamHeader = open_streamfile_by_ext(streamFile, "msh");
|
||||
if (!streamHeader) goto fail;
|
||||
|
||||
if (read_32bitLE(0x00,streamHeader) != get_streamfile_size(streamHeader))
|
||||
goto fail;
|
||||
/* 0x04: unknown */
|
||||
|
||||
/* parse entries */
|
||||
{
|
||||
int i;
|
||||
int entries = read_32bitLE(0x08,streamHeader);
|
||||
|
||||
total_subsongs = 0;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
if (read_32bitLE(0x0c + 0x10*i, streamHeader) == 0) /* size 0 = empty entry */
|
||||
continue;
|
||||
|
||||
total_subsongs++;
|
||||
if (total_subsongs == target_subsong && !header_offset) {
|
||||
header_offset = 0x0c + 0x10*i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!header_offset) goto fail;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
}
|
||||
|
||||
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = 1;
|
||||
|
||||
stream_size = read_32bitLE(header_offset+0x00, streamHeader);
|
||||
if (read_32bitLE(header_offset+0x04, streamHeader) != 0) /* stereo flag? */
|
||||
goto fail;
|
||||
start_offset = read_32bitLE(header_offset+0x08, streamHeader);
|
||||
sample_rate = read_32bitLE(header_offset+0x0c, streamHeader); /* Ace Combat 2 seems to set wrong values but probably their bug */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
vgmstream->meta_type = meta_MSB_MSH;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
close_streamfile(streamHeader);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(streamHeader);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* MSB+MSH - SCEE MultiStream flat bank [namCollection: Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */
|
||||
VGMSTREAM* init_vgmstream_msb_msh(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
off_t start_offset, header_offset = 0;
|
||||
size_t stream_size;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "msb"))
|
||||
goto fail;
|
||||
|
||||
sh = open_streamfile_by_ext(sf, "msh");
|
||||
if (!sh) goto fail;
|
||||
|
||||
if (read_u32le(0x00,sh) != get_streamfile_size(sh))
|
||||
goto fail;
|
||||
/* 0x04: unknown */
|
||||
|
||||
/* parse entries */
|
||||
{
|
||||
int i;
|
||||
int entries = read_s32le(0x08,sh);
|
||||
|
||||
total_subsongs = 0;
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
if (read_u32le(0x0c + 0x10*i, sh) == 0) /* size 0 = empty entry */
|
||||
continue;
|
||||
|
||||
total_subsongs++;
|
||||
if (total_subsongs == target_subsong && !header_offset) {
|
||||
header_offset = 0x0c + 0x10*i;
|
||||
}
|
||||
}
|
||||
|
||||
if (!header_offset) goto fail;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
}
|
||||
|
||||
|
||||
loop_flag = 0;
|
||||
channels = 1;
|
||||
|
||||
stream_size = read_u32le(header_offset+0x00, sh);
|
||||
if (read_u32le(header_offset+0x04, sh) != 0) /* stereo flag? */
|
||||
goto fail;
|
||||
start_offset = read_u32le(header_offset+0x08, sh);
|
||||
sample_rate = read_u32le(header_offset+0x0c, sh); /* Ace Combat 2 seems to set wrong values but probably their bug */
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MSB_MSH;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels);
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = stream_size;
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x10;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "../coding/coding.h"
|
||||
|
||||
typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form;
|
||||
typedef enum { PSX, DSP, XBOX, IMA, DAT } musx_codec;
|
||||
typedef enum { PSX, DSP, XBOX, IMA, DAT, NGCA, PCM } musx_codec;
|
||||
typedef struct {
|
||||
int big_endian;
|
||||
int version;
|
||||
|
@ -31,11 +31,11 @@ typedef struct {
|
|||
int32_t loop_end_sample;
|
||||
} musx_header;
|
||||
|
||||
static int parse_musx(STREAMFILE *streamFile, musx_header *musx);
|
||||
static int parse_musx(STREAMFILE* sf, musx_header* musx);
|
||||
|
||||
/* MUSX - from Eurocom's games */
|
||||
VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_musx(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
musx_header musx = {0};
|
||||
|
||||
|
@ -43,12 +43,12 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
|||
/* checks */
|
||||
/* .sfx: actual extension (extracted from bigfiles with sometimes encrypted names)
|
||||
* .musx: header id */
|
||||
if (!check_extensions(streamFile, "sfx,musx"))
|
||||
if (!check_extensions(sf, "sfx,musx"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
|
||||
if (!is_id32be(0x00,sf, "MUSX"))
|
||||
goto fail;
|
||||
|
||||
if (!parse_musx(streamFile, &musx))
|
||||
if (!parse_musx(sf, &musx))
|
||||
goto fail;
|
||||
|
||||
|
||||
|
@ -112,8 +112,41 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
|||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x08;
|
||||
|
||||
dsp_read_coefs(vgmstream,streamFile,musx.coefs_offset+0x1c, 0x60, musx.big_endian);
|
||||
dsp_read_hist(vgmstream,streamFile,musx.coefs_offset+0x40, 0x60, musx.big_endian);
|
||||
dsp_read_coefs(vgmstream,sf,musx.coefs_offset+0x1c, 0x60, musx.big_endian);
|
||||
dsp_read_hist(vgmstream,sf,musx.coefs_offset+0x40, 0x60, musx.big_endian);
|
||||
break;
|
||||
|
||||
case PCM:
|
||||
//vgmstream->num_samples = pcm_bytes_to_samples(musx.stream_size, musx.channels, 16);
|
||||
//vgmstream->loop_start_sample = pcm_bytes_to_samples(musx.loop_start, musx.channels, 16);
|
||||
//vgmstream->loop_end_sample = pcm_bytes_to_samples(musx.loop_end, musx.channels, 16);
|
||||
|
||||
vgmstream->coding_type = musx.big_endian ? coding_PCM16BE : coding_PCM16LE; /* only seen on PC but just in case */
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x02;
|
||||
break;
|
||||
|
||||
case NGCA:
|
||||
if (!is_id32be(start_offset,sf, "NGCA"))
|
||||
goto fail;
|
||||
/* 0x04: size (stereo full-interleaves 2 NGCA headers but sizes count all) */
|
||||
/* 0x08: channels */
|
||||
/* 0x0c: coefs */
|
||||
musx.coefs_offset = start_offset;
|
||||
start_offset += 0x40;
|
||||
//musx.stream_size -= 0x40 * musx.channels; /* needed for interleave */
|
||||
|
||||
//vgmstream->num_samples = dsp_bytes_to_samples(musx.stream_size - 0x40 * musx.channels, musx.channels);
|
||||
//vgmstream->loop_start_sample = dsp_bytes_to_samples(musx.loop_start + 0x40, musx.channels);
|
||||
//vgmstream->loop_end_sample = dsp_bytes_to_samples(musx.loop_end - 0x40 * musx.channels, musx.channels);
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = musx.stream_size / musx.channels;
|
||||
|
||||
dsp_read_coefs(vgmstream, sf, musx.coefs_offset+0x0c, vgmstream->interleave_block_size, musx.big_endian);
|
||||
//dsp_read_hist(vgmstream, sf, musx.coefs_offset+0x30, 0x00, musx.big_endian); /* used? */
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -127,7 +160,7 @@ VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
|
|||
if (musx.loop_flag && musx.loop_end_sample)
|
||||
vgmstream->loop_end_sample = musx.loop_end_sample;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream,sf,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
@ -137,21 +170,21 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
static int parse_musx_stream(STREAMFILE* sf, musx_header* musx) {
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
int default_channels, default_sample_rate;
|
||||
|
||||
if (musx->big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_u32 = read_u32be;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_u32 = read_u32le;
|
||||
}
|
||||
|
||||
|
||||
/* autodetect for older versions that have no info */
|
||||
if (musx->platform == 0) {
|
||||
if (musx->big_endian) {
|
||||
musx->platform = 0x47433032; /* "GC02" (fake) */
|
||||
musx->platform = get_id32be("GC02"); /* (fake) */
|
||||
}
|
||||
else {
|
||||
off_t offset = musx->stream_offset;
|
||||
|
@ -159,10 +192,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
if (max > musx->stream_size)
|
||||
max = musx->stream_size;
|
||||
|
||||
if (ps_check_format(streamFile, offset, max)) {
|
||||
musx->platform = 0x5053325F; /* "PS2_" */
|
||||
if (ps_check_format(sf, offset, max)) {
|
||||
musx->platform = get_id32be("PS2_");
|
||||
} else {
|
||||
musx->platform = 0x58423032; /* "XB02" (fake) */
|
||||
musx->platform = get_id32be("XB02"); /* (fake) */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,19 +291,19 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
* 0x10: volume? (usually <= 100) */
|
||||
|
||||
/* find loops (cues1 also seems to have this info but this looks ok) */
|
||||
cues2_count = read_32bit(musx->loops_offset+0x04, streamFile);
|
||||
cues2_offset = musx->loops_offset + read_32bit(musx->loops_offset+0x0c, streamFile);
|
||||
cues2_count = read_u32(musx->loops_offset+0x04, sf);
|
||||
cues2_offset = musx->loops_offset + read_u32(musx->loops_offset+0x0c, sf);
|
||||
for (i = 0; i < cues2_count; i++) {
|
||||
uint32_t type, offset1, offset2;
|
||||
|
||||
if (musx->is_old) {
|
||||
offset1 = read_32bit(cues2_offset + i*0x20 + 0x04, streamFile);
|
||||
type = read_32bit(cues2_offset + i*0x20 + 0x08, streamFile);
|
||||
offset2 = read_32bit(cues2_offset + i*0x20 + 0x14, streamFile);
|
||||
offset1 = read_u32(cues2_offset + i*0x20 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x20 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x20 + 0x14, sf);
|
||||
} else {
|
||||
offset1 = read_32bit(cues2_offset + i*0x14 + 0x04, streamFile);
|
||||
type = read_32bit(cues2_offset + i*0x14 + 0x08, streamFile);
|
||||
offset2 = read_32bit(cues2_offset + i*0x14 + 0x0c, streamFile);
|
||||
offset1 = read_u32(cues2_offset + i*0x14 + 0x04, sf);
|
||||
type = read_u32(cues2_offset + i*0x14 + 0x08, sf);
|
||||
offset2 = read_u32(cues2_offset + i*0x14 + 0x0c, sf);
|
||||
}
|
||||
|
||||
/* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */
|
||||
|
@ -282,7 +315,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (musx->loops_offset && read_32bitBE(musx->loops_offset, streamFile) != 0xABABABAB) {
|
||||
else if (musx->loops_offset && read_u32be(musx->loops_offset, sf) != 0xABABABAB) {
|
||||
/* parse loop table (loop starts are -1 if non-looping)
|
||||
* 0x00: version?
|
||||
* 0x04: flags? (&1=loops)
|
||||
|
@ -292,10 +325,10 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
* 0x14: loop start sample
|
||||
* 0x18: loop end offset
|
||||
* 0x1c: loop start offset */
|
||||
musx->loop_end_sample = read_32bitLE(musx->loops_offset+0x10, streamFile);
|
||||
musx->loop_start_sample = read_32bitLE(musx->loops_offset+0x14, streamFile);
|
||||
musx->loop_end = read_32bitLE(musx->loops_offset+0x18, streamFile);
|
||||
musx->loop_start = read_32bitLE(musx->loops_offset+0x1c, streamFile);
|
||||
musx->loop_end_sample = read_s32le(musx->loops_offset+0x10, sf);
|
||||
musx->loop_start_sample = read_s32le(musx->loops_offset+0x14, sf);
|
||||
musx->loop_end = read_s32le(musx->loops_offset+0x18, sf);
|
||||
musx->loop_start = read_s32le(musx->loops_offset+0x1c, sf);
|
||||
musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */
|
||||
musx->loop_flag = (musx->loop_start_sample >= 0);
|
||||
}
|
||||
|
@ -310,7 +343,7 @@ static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
|
|||
int pos;
|
||||
off_t offset = musx->stream_offset + musx->stream_size - 0x800;
|
||||
|
||||
if (read_streamfile(buf, offset, sizeof(buf), streamFile) != 0x800)
|
||||
if (read_streamfile(buf, offset, sizeof(buf), sf) != 0x800)
|
||||
goto fail;
|
||||
|
||||
pos = 0x800 - 0x04;
|
||||
|
@ -328,58 +361,66 @@ fail:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
static int parse_musx(STREAMFILE* sf, musx_header* musx) {
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
|
||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
|
||||
int target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* base header is LE */
|
||||
/* 0x04: file ID (referenced in external .h, sometimes files are named HC(id).sfx too) */
|
||||
musx->version = read_32bitLE(0x08,streamFile);
|
||||
musx->file_size = read_32bitLE(0x0c,streamFile);
|
||||
|
||||
/* 0x04: file ID (referenced in external .h, sometimes files are named [prefix](id)[suffix].sfx too) */
|
||||
musx->version = read_u32le(0x08,sf);
|
||||
musx->file_size = read_u32le(0x0c,sf);
|
||||
|
||||
switch(musx->version) {
|
||||
case 201: /* Sphinx and the Cursed Mummy (PS2), Buffy the Vampire Slayer: Chaos Bleeds (PS2/GC/Xbox) */
|
||||
case 1: /* Athens 2004 (PS2) */
|
||||
musx->platform = 0; /* guess later */
|
||||
musx->tables_offset = 0x10;
|
||||
musx->big_endian = guess_endianness32bit(0x10, streamFile);
|
||||
musx->big_endian = guess_endianness32bit(0x10, sf);
|
||||
musx->is_old = 1;
|
||||
break;
|
||||
|
||||
case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC), Athens 2004 (PC) */
|
||||
case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */
|
||||
case 6: /* Batman Begins (GC), Ice Age 2 (PS2/PC) */
|
||||
musx->platform = read_32bitBE(0x10,streamFile);
|
||||
musx->platform = read_u32be(0x10,sf);
|
||||
/* 0x14: some id/hash? */
|
||||
/* 0x18: platform number? */
|
||||
/* 0x1c: null */
|
||||
musx->tables_offset = 0x20;
|
||||
musx->big_endian = (musx->platform == 0x47435F5F); /* "GC__" */
|
||||
musx->big_endian = (musx->platform == get_id32be("GC__"));
|
||||
break;
|
||||
|
||||
case 10: /* 007: Quantum of Solace (PS2), Pirates of the Caribbean: At World's End (PSP), GoldenEye 007 (Wii), Rio (PS3) */
|
||||
musx->platform = read_32bitBE(0x10,streamFile);
|
||||
musx->platform = read_u32be(0x10,sf);
|
||||
/* 0x14: null */
|
||||
/* 0x18: platform number? */
|
||||
/* 0x1c: null */
|
||||
/* 0x20: hash */
|
||||
musx->tables_offset = 0; /* no tables */
|
||||
musx->big_endian = (musx->platform == 0x47435F5F || /* "GC__" */
|
||||
musx->platform == 0x58455F5F || /* "XE__" */
|
||||
musx->platform == 0x5053335F || /* "PS3_" */
|
||||
musx->platform == 0x5749495F); /* "WII_" */
|
||||
musx->big_endian = (
|
||||
musx->platform == get_id32be("GC__") ||
|
||||
musx->platform == get_id32be("XE__") ||
|
||||
musx->platform == get_id32be("PS3_") ||
|
||||
musx->platform == get_id32be("WII_")
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("MUSX: unknown version\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (musx->big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_s32 = read_s32be;
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
} else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_s32 = read_s32le;
|
||||
read_u32 = read_u32le;
|
||||
read_u16 = read_u16le;
|
||||
}
|
||||
|
||||
|
||||
|
@ -388,53 +429,56 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
off_t table1_offset, table2_offset /*, table3_offset, table4_offset*/;
|
||||
size_t /*table1_size, table2_size, table3_size,*/ table4_size;
|
||||
|
||||
table1_offset = read_32bit(musx->tables_offset+0x00, streamFile);
|
||||
//table1_size = read_32bit(musx->tables_offset+0x04, streamFile);
|
||||
table2_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
//table2_size = read_32bit(musx->tables_offset+0x0c, streamFile);
|
||||
//table3_offset = read_32bit(musx->tables_offset+0x10, streamFile);
|
||||
//table3_size = read_32bit(musx->tables_offset+0x14, streamFile);
|
||||
//table4_offset = read_32bit(musx->tables_offset+0x18, streamFile);
|
||||
table4_size = read_32bit(musx->tables_offset+0x1c, streamFile);
|
||||
table1_offset = read_u32(musx->tables_offset+0x00, sf);
|
||||
//table1_size = read_u32(musx->tables_offset+0x04, sf);
|
||||
table2_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
//table2_size = read_u32(musx->tables_offset+0x0c, sf);
|
||||
//table3_offset = read_u32(musx->tables_offset+0x10, sf);
|
||||
//table3_size = read_u32(musx->tables_offset+0x14, sf);
|
||||
//table4_offset = read_u32(musx->tables_offset+0x18, sf);
|
||||
table4_size = read_u32(musx->tables_offset+0x1c, sf);
|
||||
|
||||
if (table2_offset == 0 || table2_offset == 0xABABABAB) {
|
||||
/* possibly a collection of IDs */
|
||||
VGM_LOG("MUSX: unsupported ID type\n");
|
||||
goto fail;
|
||||
}
|
||||
else if (table4_size != 0 && table4_size != 0xABABABAB) {
|
||||
/* sfx banks have table1=id table? table2=header table, table=DSP coefs table (optional), table4=data */
|
||||
musx->form = SFX_BANK;
|
||||
}
|
||||
else if (read_32bit(table1_offset+0x00, streamFile) < 0x9 &&
|
||||
read_32bit(table1_offset+0x08, streamFile) == 0x14 &&
|
||||
read_32bit(table1_offset+0x10, streamFile) <= 100) {
|
||||
else if (read_u32(table1_offset+0x00, sf) < 0x9 &&
|
||||
read_u32(table1_offset+0x08, sf) == 0x14 &&
|
||||
read_u32(table1_offset+0x10, sf) <= 100) {
|
||||
/* streams have a info table with a certain format */
|
||||
musx->form = MFX;
|
||||
}
|
||||
else if (read_32bit(table1_offset+0x00, streamFile) == 0 &&
|
||||
read_32bit(table1_offset+0x04, streamFile) > read_32bit(table1_offset+0x00, streamFile) &&
|
||||
read_32bit(table1_offset+0x08, streamFile) > read_32bit(table1_offset+0x04, streamFile)) {
|
||||
else if (read_u32(table1_offset+0x00, sf) == 0 &&
|
||||
read_u32(table1_offset+0x04, sf) > read_u32(table1_offset+0x00, sf) &&
|
||||
read_u32(table1_offset+0x08, sf) > read_u32(table1_offset+0x04, sf)) {
|
||||
/* a list of offsets starting from 0, each stream then has info table at offset */
|
||||
musx->form = MFX_BANK;
|
||||
}
|
||||
else {
|
||||
VGM_LOG("MUSX: unsupported unknown type\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (read_32bitBE(0x800,streamFile) == 0x53424E4B) { /* "SBNK" */
|
||||
/* SFX_BANK-like sound bank found on v10 Wii games */
|
||||
if (is_id32be(0x800,sf, "SBNK")) {
|
||||
/* SFX_BANK-like sound bank found on v10 Wii/PC games [Dead Space Extraction (Wii), G-Force (PC)] */
|
||||
musx->form = SBNK;
|
||||
}
|
||||
else if (read_32bitBE(0x800,streamFile) == 0x464F524D) { /* "FORM" */
|
||||
else if (is_id32be(0x800,sf, "FORM")) {
|
||||
/* RIFF-like sound bank found on v10 PSP games */
|
||||
musx->form = FORM;
|
||||
}
|
||||
else if (read_32bitBE(0x800,streamFile) == 0x45535044) { /* "ESPD" */
|
||||
else if (is_id32be(0x800,sf, "ESPD")) {
|
||||
/* projectdetails.sfx */
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
/* simplest music type*/
|
||||
musx->form = MFX;
|
||||
}
|
||||
}
|
||||
|
@ -445,27 +489,27 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
case MFX:
|
||||
|
||||
if (musx->tables_offset) {
|
||||
musx->loops_offset = read_32bit(musx->tables_offset+0x00, streamFile);
|
||||
musx->loops_offset = read_u32(musx->tables_offset+0x00, sf);
|
||||
|
||||
musx->stream_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
musx->stream_size = read_32bit(musx->tables_offset+0x0c, streamFile);
|
||||
musx->stream_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
musx->stream_size = read_u32(musx->tables_offset+0x0c, sf);
|
||||
}
|
||||
else {
|
||||
if (read_32bitBE(0x30, streamFile) != 0xABABABAB) {
|
||||
uint32_t miniheader = read_32bitBE(0x40, streamFile);
|
||||
if (read_u32be(0x30, sf) != 0xABABABAB) {
|
||||
uint32_t miniheader = read_u32be(0x40, sf);
|
||||
switch(miniheader) {
|
||||
case 0x44415434: /* "DAT4" */
|
||||
case 0x44415435: /* "DAT5" */
|
||||
case 0x44415438: /* "DAT8" */
|
||||
/* found on PS3/Wii (but not always?) */
|
||||
musx->stream_size = read_32bitLE(0x44, streamFile);
|
||||
musx->channels = read_32bitLE(0x48, streamFile);
|
||||
musx->sample_rate = read_32bitLE(0x4c, streamFile);
|
||||
musx->stream_size = read_u32le(0x44, sf);
|
||||
musx->channels = read_u32le(0x48, sf);
|
||||
musx->sample_rate = read_u32le(0x4c, sf);
|
||||
musx->loops_offset = 0x50;
|
||||
break;
|
||||
default:
|
||||
if (read_32bitBE(0x30, streamFile) == 0x00 &&
|
||||
read_32bitBE(0x34, streamFile) == 0x00) {
|
||||
if (read_u32be(0x30, sf) == 0x00 &&
|
||||
read_u32be(0x34, sf) == 0x00) {
|
||||
/* no subheader [Spider-Man 4 (Wii)] */
|
||||
musx->loops_offset = 0x00;
|
||||
} else {
|
||||
|
@ -480,32 +524,32 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
musx->stream_size = 0; /* read later */
|
||||
}
|
||||
|
||||
if (!parse_musx_stream(streamFile, musx))
|
||||
if (!parse_musx_stream(sf, musx))
|
||||
goto fail;
|
||||
break;
|
||||
|
||||
case MFX_BANK: {
|
||||
off_t target_offset, base_offset, data_offset;
|
||||
|
||||
musx->total_subsongs = read_32bit(musx->tables_offset+0x04, streamFile) / 0x04; /* size */
|
||||
musx->total_subsongs = read_u32(musx->tables_offset+0x04, sf) / 0x04; /* size */
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
|
||||
|
||||
base_offset = read_32bit(musx->tables_offset+0x00, streamFile);
|
||||
data_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
base_offset = read_u32(musx->tables_offset+0x00, sf);
|
||||
data_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
|
||||
target_offset = read_32bit(base_offset + (target_subsong - 1)*0x04, streamFile) + data_offset;
|
||||
target_offset = read_u32(base_offset + (target_subsong - 1)*0x04, sf) + data_offset;
|
||||
|
||||
/* 0x00: id? */
|
||||
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
|
||||
musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
|
||||
musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x08, sf);
|
||||
musx->loops_offset = target_offset + 0x0c;
|
||||
|
||||
/* this looks correct for PS2 and Xbox, not sure about GC */
|
||||
musx->channels = 1;
|
||||
musx->sample_rate = 22050;
|
||||
|
||||
if (!parse_musx_stream(streamFile, musx))
|
||||
if (!parse_musx_stream(sf, musx))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
@ -514,13 +558,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
off_t target_offset, head_offset, coef_offset, data_offset;
|
||||
size_t coef_size;
|
||||
|
||||
//unk_offset = read_32bit(musx->tables_offset+0x00, streamFile); /* ids and cue-like config? */
|
||||
head_offset = read_32bit(musx->tables_offset+0x08, streamFile);
|
||||
coef_offset = read_32bit(musx->tables_offset+0x10, streamFile);
|
||||
coef_size = read_32bit(musx->tables_offset+0x14, streamFile);
|
||||
data_offset = read_32bit(musx->tables_offset+0x18, streamFile);
|
||||
//unk_offset = read_u32(musx->tables_offset+0x00, sf); /* ids and cue-like config? */
|
||||
head_offset = read_u32(musx->tables_offset+0x08, sf);
|
||||
coef_offset = read_u32(musx->tables_offset+0x10, sf);
|
||||
coef_size = read_u32(musx->tables_offset+0x14, sf);
|
||||
data_offset = read_u32(musx->tables_offset+0x18, sf);
|
||||
|
||||
musx->total_subsongs = read_32bit(head_offset+0x00, streamFile);
|
||||
musx->total_subsongs = read_u32(head_offset+0x00, sf);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
|
||||
|
||||
|
@ -529,13 +573,13 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
target_offset = head_offset + 0x04 + (target_subsong - 1)*0x28;
|
||||
|
||||
/* 0x00: flag? */
|
||||
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
|
||||
musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
|
||||
musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile);
|
||||
musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x08, sf);
|
||||
musx->sample_rate = read_u32(target_offset + 0x0c, sf);
|
||||
/* 0x10: size? */
|
||||
/* 0x14: channels? */
|
||||
/* 0x18: always 4? */
|
||||
musx->coefs_offset = read_32bit(target_offset + 0x1c, streamFile) + coef_offset; /* may be set even for non-GC */
|
||||
musx->coefs_offset = read_u32(target_offset + 0x1c, sf) + coef_offset; /* may be set even for non-GC */
|
||||
/* 0x20: null? */
|
||||
/* 0x24: sub-id? */
|
||||
musx->channels = 1;
|
||||
|
@ -544,11 +588,11 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
target_offset = head_offset + 0x04 + (target_subsong - 1)*0x20;
|
||||
|
||||
/* 0x00: flag? */
|
||||
musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
|
||||
musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
|
||||
musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile);
|
||||
musx->stream_offset = read_u32(target_offset + 0x04, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x08, sf);
|
||||
musx->sample_rate = read_u32(target_offset + 0x0c, sf);
|
||||
/* 0x10: size? */
|
||||
musx->coefs_offset = read_32bit(target_offset + 0x14, streamFile) + coef_offset; /* may be set even for non-GC */
|
||||
musx->coefs_offset = read_u32(target_offset + 0x14, sf) + coef_offset; /* may be set even for non-GC */
|
||||
/* 0x18: null? */
|
||||
/* 0x1c: sub-id? */
|
||||
musx->channels = 1;
|
||||
|
@ -557,13 +601,81 @@ static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
|
|||
if (coef_size == 0)
|
||||
musx->coefs_offset = 0; /* disable for later detection */
|
||||
|
||||
if (!parse_musx_stream(streamFile, musx))
|
||||
if (!parse_musx_stream(sf, musx))
|
||||
goto fail;
|
||||
break;
|
||||
}
|
||||
|
||||
case SBNK: {
|
||||
off_t target_offset, head_offset, data_offset;
|
||||
uint8_t codec;
|
||||
|
||||
/* 0x00: id */
|
||||
/* 0x04: always 0x12? */
|
||||
/* 0x08: file ID (same as base file) */
|
||||
/* 0x0c: some ID? */
|
||||
|
||||
/* 0x10: table1 count */
|
||||
/* 0x14: table1 offset (from here) */
|
||||
/* 0x18: table2 count */
|
||||
/* 0x1c: table2 offset (from here) */
|
||||
|
||||
/* 0x20: table3 count */
|
||||
/* 0x24: table3 offset (from here) */
|
||||
/* 0x28: table4 count */
|
||||
/* 0x2c: table4 offset (from here) */
|
||||
|
||||
/* 0x30: table5 count (waves) */
|
||||
/* 0x34: table5 offset (from here) */
|
||||
/* 0x38: table6 count */
|
||||
/* 0x3c: table6 offset (from here) */
|
||||
|
||||
/* 0x40: table7 count */
|
||||
/* 0x44: table7 offset (from here) */
|
||||
/* 0x48: data size */
|
||||
/* 0x4c: data offset (absolute) */
|
||||
|
||||
musx->tables_offset = 0x800;
|
||||
if (!is_id32be(musx->tables_offset+0x00, sf, "SBNK"))
|
||||
goto fail;
|
||||
|
||||
musx->total_subsongs = read_u32(musx->tables_offset+0x30, sf);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
|
||||
|
||||
head_offset = read_u32(musx->tables_offset+0x34, sf) + musx->tables_offset + 0x34;
|
||||
data_offset = read_u32(musx->tables_offset+0x4c, sf);
|
||||
|
||||
target_offset = head_offset + (target_subsong - 1)*0x1c;
|
||||
;VGM_LOG("MUSX: ho=%lx, do=%lx, to=%lx\n", head_offset, data_offset, target_offset);
|
||||
|
||||
/* 0x00: subfile ID */
|
||||
musx->num_samples = read_s32(target_offset + 0x04, sf);
|
||||
musx->loop_start_sample = read_s32(target_offset + 0x08, sf); /* -1 if no loop */
|
||||
musx->sample_rate = read_u16(target_offset + 0x0c, sf);
|
||||
codec = read_u8 (target_offset + 0x0e, sf);
|
||||
musx->channels = read_u8 (target_offset + 0x0f, sf);
|
||||
musx->stream_offset = read_u32(target_offset + 0x10, sf) + data_offset;
|
||||
musx->stream_size = read_u32(target_offset + 0x14, sf);
|
||||
musx->loop_start = read_s32(target_offset + 0x18, sf);
|
||||
|
||||
musx->loop_end_sample = musx->num_samples;
|
||||
musx->loop_flag = (musx->loop_start_sample >= 0);
|
||||
|
||||
switch(codec) {
|
||||
case 0x11: musx->codec = DAT; break;
|
||||
case 0x13: musx->codec = NGCA; break;
|
||||
case 0x14: musx->codec = PCM; break;
|
||||
default:
|
||||
VGM_LOG("MUSX: unknown SBNK codec %x\n", codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
VGM_LOG("MUSX: unknown form\n");
|
||||
VGM_LOG("MUSX: unsupported form\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "mzrt_streamfile.h"
|
||||
|
||||
|
||||
/* mzrt - idTech "4.5" audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */
|
||||
VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count, codec, sample_rate, block_size = 0, bps = 0, num_samples;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "idwav,idmsf,idxma"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x6D7A7274) /* "mzrt" */
|
||||
goto fail;
|
||||
|
||||
/* this format is bizarrely mis-aligned (and mis-designed too) */
|
||||
|
||||
num_samples = read_32bitBE(0x11,streamFile);
|
||||
codec = read_16bitLE(0x15,streamFile);
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
case 0x0002:
|
||||
case 0x0166:
|
||||
channel_count = read_16bitLE(0x17,streamFile);
|
||||
sample_rate = read_32bitLE(0x19, streamFile);
|
||||
block_size = read_16bitLE(0x21, streamFile);
|
||||
bps = read_16bitLE(0x23,streamFile);
|
||||
|
||||
start_offset = 0x25;
|
||||
break;
|
||||
|
||||
case 0x0000:
|
||||
sample_rate = read_32bitBE(0x1D, streamFile);
|
||||
channel_count = read_32bitBE(0x21, streamFile);
|
||||
|
||||
start_offset = 0x29;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* skip MSADPCM data */
|
||||
if (codec == 0x0002) {
|
||||
if (!msadpcm_check_coefs(streamFile, start_offset + 0x02 + 0x02))
|
||||
goto fail;
|
||||
|
||||
start_offset += 0x02 + read_16bitLE(start_offset, streamFile);
|
||||
}
|
||||
|
||||
/* skip extra data */
|
||||
if (codec == 0x0166) {
|
||||
start_offset += 0x02 + read_16bitLE(start_offset, streamFile);
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
if (codec == 0x0000) {
|
||||
start_offset += 0x04 + read_32bitBE(start_offset, streamFile) * 0x04;
|
||||
}
|
||||
|
||||
/* skip unknown table */
|
||||
start_offset += 0x04 + read_32bitBE(start_offset, streamFile);
|
||||
|
||||
/* skip block info */
|
||||
if (codec != 0x0000) {
|
||||
/* 0x00: de-blocked size
|
||||
* 0x04: block count*/
|
||||
start_offset += 0x08;
|
||||
|
||||
/* idwav only uses 1 super-block though */
|
||||
temp_streamFile = setup_mzrt_streamfile(streamFile, start_offset);
|
||||
if (!temp_streamFile) goto fail;
|
||||
}
|
||||
else {
|
||||
/* 0x00: de-blocked size */
|
||||
start_offset += 0x04;
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_MZRT;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
switch(codec) {
|
||||
case 0x0001:
|
||||
if (bps != 16) goto fail;
|
||||
vgmstream->coding_type = coding_PCM16LE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = block_size / channel_count;
|
||||
break;
|
||||
|
||||
case 0x0002:
|
||||
if (bps != 4) goto fail;
|
||||
vgmstream->coding_type = coding_MSADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->frame_size = block_size;
|
||||
break;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
case 0x0166: {
|
||||
uint8_t buf[0x100];
|
||||
int bytes;
|
||||
size_t stream_size = get_streamfile_size(temp_streamFile);
|
||||
|
||||
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, streamFile, 0);
|
||||
vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,stream_size);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
xma_fix_raw_samples_hb(vgmstream, streamFile, temp_streamFile, 0x00,stream_size, 0x15, 0,0);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_MPEG
|
||||
case 0x0000: {
|
||||
mpeg_custom_config cfg = {0};
|
||||
|
||||
cfg.skip_samples = 576; /* assumed */
|
||||
|
||||
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->layout_type = layout_none;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,temp_streamFile == NULL ? streamFile : temp_streamFile,temp_streamFile == NULL ? start_offset : 0x00))
|
||||
goto fail;
|
||||
close_streamfile(temp_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -178,13 +178,18 @@ static VGMSTREAM* init_vgmstream_dsp_common(STREAMFILE* sf, dsp_meta *dspm) {
|
|||
for (i = 0; i < channels; i++) {
|
||||
off_t loop_offset = ch_header[i].loop_start_offset;
|
||||
|
||||
loop_offset = loop_offset / 0x8 * 0x8; /* loop points to a nibble, but we need closest frame header */
|
||||
if (dspm->interleave) {
|
||||
/* Loop offset points to a nibble, but we need closest frame header.
|
||||
* Stereo doesn't always point to a proper offset unless de-adjusted with interleave first. */
|
||||
if (!dspm->interleave) {
|
||||
loop_offset = loop_offset / 0x08 * 0x8;
|
||||
}
|
||||
else {
|
||||
loop_offset = loop_offset / 0x10 * 0x8;
|
||||
loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave);
|
||||
}
|
||||
|
||||
if (ch_header[i].loop_ps != read_u8(dspm->start_offset + i*dspm->interleave + loop_offset,sf)) {
|
||||
//;VGM_LOG("DSP: bad loop ps: %x vs at %lx\n", ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset);
|
||||
//;VGM_LOG("DSP: ch%i bad loop ps: %x vs at %lx\n", i, ch_header[i].loop_ps, dspm->start_offset + i*dspm->interleave + loop_offset);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
@ -660,8 +665,6 @@ VGMSTREAM* init_vgmstream_sadb(STREAMFILE* sf) {
|
|||
dspm.start_offset = read_32bitBE(0x48,sf);
|
||||
dspm.interleave = 0x10;
|
||||
|
||||
dspm.ignore_loop_ps = 1; /* does something strange with offsets/etc, ignore */
|
||||
|
||||
dspm.meta_type = meta_DSP_SADB;
|
||||
return init_vgmstream_dsp_common(sf, &dspm);
|
||||
fail:
|
||||
|
@ -1370,7 +1373,6 @@ VGMSTREAM* init_vgmstream_dsp_cwac(STREAMFILE* sf) {
|
|||
dspm.max_channels = 2;
|
||||
dspm.header_spacing = dspm.interleave;
|
||||
dspm.start_offset = dspm.header_offset + 0x60;
|
||||
dspm.ignore_loop_ps = 1; /* loop offset seems relative to CWAC? also interleave affects it */
|
||||
|
||||
dspm.meta_type = meta_DSP_CWAC;
|
||||
return init_vgmstream_dsp_common(sf, &dspm);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,8 +35,7 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) {
|
|||
/* .aac: actual extension, .laac: for players to avoid hijacking MP4/AAC */
|
||||
if (!check_extensions(sf, "aac,laac"))
|
||||
goto fail;
|
||||
if (read_u32be(0x00, sf) != 0x41414320 &&
|
||||
read_u32le(0x00, sf) != 0x41414320) /* "AAC " */
|
||||
if (!is_id32be(0x00, sf, "AAC ") && !is_id32le(0x00, sf, "AAC "))
|
||||
goto fail;
|
||||
|
||||
if (!parse_aac(sf, &aac))
|
||||
|
@ -92,12 +91,28 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) {
|
|||
}
|
||||
#endif
|
||||
#ifdef VGM_USE_ATRAC9
|
||||
case 0x08: { /* Judas Code (Vita) */
|
||||
case 0x08: /* Judas Code (Vita) */
|
||||
case 0x18: { /* Resonance of Fate (PS4) */
|
||||
atrac9_config cfg = {0};
|
||||
|
||||
cfg.channels = vgmstream->channels;
|
||||
cfg.encoder_delay = read_s32le(aac.extra_offset + 0x04,sf);
|
||||
cfg.config_data = read_u32be(aac.extra_offset + 0x08,sf);
|
||||
|
||||
if (aac.codec == 0x08) {
|
||||
/* 0x00: ? (related to bitrate/channels?) */
|
||||
cfg.encoder_delay = read_s32le(aac.extra_offset + 0x04,sf);
|
||||
cfg.config_data = read_u32be(aac.extra_offset + 0x08,sf);
|
||||
}
|
||||
else {
|
||||
/* 0x00: ? (related to bitrate/channels?) */
|
||||
cfg.encoder_delay = read_s16le(aac.extra_offset + 0x04,sf);
|
||||
/* 0x06: samples per frame */
|
||||
/* 0x08: num samples (without encoder delay) */
|
||||
cfg.config_data = read_u32le(aac.extra_offset + 0x0c,sf);
|
||||
/* 0x10: loop start sample (without encoder delay) */
|
||||
/* 0x14: loop end sample (without encoder delay) */
|
||||
/* 0x18: related to loop start (adjustment? same as loop start when less than a sample) */
|
||||
/* using loop samples causes clicks in some tracks, so maybe it's info only,
|
||||
* or it's meant to be adjusted with value at 0x18 */
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_atrac9(&cfg);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
|
@ -158,6 +173,7 @@ VGMSTREAM* init_vgmstream_ta_aac(STREAMFILE* sf) {
|
|||
}
|
||||
#endif
|
||||
default:
|
||||
VGM_LOG("AAC: unknown codec %x\n", aac.codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -189,7 +205,7 @@ static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) {
|
|||
/* 0x20: config? (0x00010003) */
|
||||
/* 0x30+ DIR + dirn subsongs */
|
||||
|
||||
if (read_u32be(0x30, sf) != 0x44495220) /* "DIR " */
|
||||
if (!is_id32be(0x30, sf, "DIR "))
|
||||
goto fail;
|
||||
aac->total_subsongs = read_u32be(0x40, sf);
|
||||
|
||||
|
@ -220,7 +236,7 @@ static int parse_aac_v1(STREAMFILE* sf, aac_header* aac) {
|
|||
}
|
||||
}
|
||||
|
||||
if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32be(offset + 0x00, sf, "WAVE"))
|
||||
goto fail;
|
||||
wave_offset = offset;
|
||||
offset += 0x10;
|
||||
|
@ -291,9 +307,9 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) {
|
|||
if (target_subsong == 0) target_subsong = 1;
|
||||
aac->total_subsongs = 0;
|
||||
|
||||
if (read_u32be(start + 0x00, sf) == 0x414D4620) { /* "AMF " */
|
||||
if (is_id32be(start + 0x00, sf, "AMF ")) {
|
||||
/* GUID subsongs */
|
||||
if (read_u32be(start + 0x10, sf) != 0x68656164) /* "head" */
|
||||
if (!is_id32be(start + 0x10, sf, "head"))
|
||||
goto fail;
|
||||
size = read_u32be(start + 0x10 + 0x10, sf);
|
||||
|
||||
|
@ -321,7 +337,7 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) {
|
|||
test_offset += entry_size;
|
||||
}
|
||||
}
|
||||
else if (read_u32be(start + 0x00, sf) == 0x41534320) { /* "ASC " */
|
||||
else if (is_id32be(start + 0x00, sf, "ASC ")) {
|
||||
/* regular subsongs */
|
||||
offset = 0;
|
||||
for (test_offset = 0x30; test_offset < start; test_offset += 0x10) {
|
||||
|
@ -342,14 +358,14 @@ static int parse_aac_v2(STREAMFILE* sf, aac_header* aac) {
|
|||
|
||||
if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail;
|
||||
|
||||
if (read_u32be(offset + 0x00, sf) != 0x41534320) /* "ASC " */
|
||||
if (!is_id32be(offset + 0x00, sf, "ASC "))
|
||||
goto fail;
|
||||
asc_offset = offset;
|
||||
|
||||
/* ASC section has offsets to "PLBK" chunk (?) and "WAVE" (header), may be followed by "VRC " (?) */
|
||||
/* 0x50: PLBK offset */
|
||||
offset += read_u32be(offset + 0x54, sf); /* WAVE offset */
|
||||
if (read_u32be(offset + 0x00, sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32be(offset + 0x00, sf, "WAVE"))
|
||||
goto fail;
|
||||
offset += 0x10;
|
||||
|
||||
|
@ -415,14 +431,14 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) {
|
|||
/* 0x00: id */
|
||||
/* 0x04: size */
|
||||
/* 0x10: config? (0x00020100/0x00020002/0x00020301/etc) */
|
||||
/* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC) */
|
||||
/* 0x14: platform ("VITA"=Vita, "DRD "=Android, "MSPC"=PC, "PS4 "=PS4) */
|
||||
|
||||
/* offsets table: offset + flag? + size + align? */
|
||||
offset = read_u32le(0x20, sf); /* "AAOB" table (audio object?) */
|
||||
/* 0x30: "VRCB" table (some cue/config? related to subsongs? may be empty) */
|
||||
/* 0x40: "WAVB" table (wave body, has offset + size per stream then data, not needed since offsets are elsewhere too) */
|
||||
|
||||
if (read_u32le(offset + 0x00, sf) != 0x41414F42) /* "AAOB" */
|
||||
if (!is_id32le(offset + 0x00, sf, "AAOB"))
|
||||
goto fail;
|
||||
size = read_u32le(offset + 0x04, sf);
|
||||
|
||||
|
@ -436,7 +452,7 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) {
|
|||
uint32_t entry_offset = read_u32le(test_offset + 0x00, sf);
|
||||
/* 0x04: entry size */
|
||||
|
||||
if (entry_offset == 0x41414F20) /* reached first "AAO " */
|
||||
if (entry_offset == get_id32be("AAO ")) /* reached end */
|
||||
break;
|
||||
|
||||
if (entry_offset) { /* often 0 */
|
||||
|
@ -450,14 +466,14 @@ static int parse_aac_v3(STREAMFILE* sf, aac_header* aac) {
|
|||
|
||||
if (target_subsong < 0 || target_subsong > aac->total_subsongs || aac->total_subsongs < 1) goto fail;
|
||||
|
||||
if (read_u32le(offset + 0x00, sf) != 0x41414F20) /* "AAO " */
|
||||
if (!is_id32le(offset + 0x00, sf, "AAO "))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* AAO section has offsets to "PLBK" chunk (?) and "WAVE" (header) */
|
||||
/* 0x14: PLBK offset */
|
||||
offset += read_u32le(offset + 0x18, sf); /* WAVE offset */
|
||||
if (read_u32le(offset + 0x00, sf) != 0x57415645) /* "WAVE" */
|
||||
if (!is_id32le(offset + 0x00, sf, "WAVE"))
|
||||
goto fail;
|
||||
offset += 0x10;
|
||||
|
||||
|
|
|
@ -307,10 +307,20 @@ static inline int min_s32(int32_t a, int32_t b) { return a < b ? a : b; }
|
|||
//align32, align16, clamp16, etc
|
||||
#endif
|
||||
|
||||
static inline const int is_id32be(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
/* fastest to compare would be read_u32x == (uint32), but should be pre-optimized (see get_id32x) */
|
||||
static inline /*const*/ int is_id32be(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
return read_u32be(offset, sf) == get_id32be(s);
|
||||
}
|
||||
|
||||
static inline /*const*/ int is_id32le(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
return read_u32le(offset, sf) == get_id32be(s);
|
||||
}
|
||||
|
||||
static inline /*const*/ int is_id64be(off_t offset, STREAMFILE* sf, const char* s) {
|
||||
return read_u64be(offset, sf) == get_id64be(s);
|
||||
}
|
||||
|
||||
|
||||
//TODO: maybe move to streamfile.c
|
||||
/* guess byte endianness from a given value, return true if big endian and false if little endian */
|
||||
static inline int guess_endianness16bit(off_t offset, STREAMFILE* sf) {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
/* very common functions, so static inline in .h is useful to avoid some call overhead */
|
||||
/* very common functions, so static (inline) in .h as compiler can optimize to avoid some call overhead */
|
||||
|
||||
/* host endian independent multi-byte integer reading */
|
||||
|
||||
|
@ -98,10 +98,31 @@ static inline int clamp16(int32_t val) {
|
|||
else return val;
|
||||
}
|
||||
|
||||
static inline const uint32_t get_id32be(const char* s) {
|
||||
|
||||
/* transforms a string to uint32 (for comparison), but if this is static + all goes well
|
||||
* compiler should pre-calculate and use uint32 directly */
|
||||
static inline /*const*/ uint32_t get_id32be(const char* s) {
|
||||
return (uint32_t)(s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0);
|
||||
}
|
||||
|
||||
//static inline /*const*/ uint32_t get_id32le(const char* s) {
|
||||
// return (uint32_t)(s[0] << 0) | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
|
||||
//}
|
||||
|
||||
static inline /*const*/ uint64_t get_id64be(const char* s) {
|
||||
return (uint64_t)(
|
||||
((uint64_t)s[0] << 56) |
|
||||
((uint64_t)s[1] << 48) |
|
||||
((uint64_t)s[2] << 40) |
|
||||
((uint64_t)s[3] << 32) |
|
||||
((uint64_t)s[4] << 24) |
|
||||
((uint64_t)s[5] << 16) |
|
||||
((uint64_t)s[6] << 8) |
|
||||
((uint64_t)s[7] << 0)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/* less common functions, no need to inline */
|
||||
|
||||
int round10(int val);
|
||||
|
|
|
@ -463,7 +463,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_acb,
|
||||
init_vgmstream_rad,
|
||||
init_vgmstream_smk,
|
||||
init_vgmstream_mzrt,
|
||||
init_vgmstream_mzrt_v0,
|
||||
init_vgmstream_xavs,
|
||||
init_vgmstream_psf_single,
|
||||
init_vgmstream_psf_segmented,
|
||||
|
@ -517,6 +517,11 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_dsp_cwac,
|
||||
init_vgmstream_ifs,
|
||||
init_vgmstream_acx,
|
||||
init_vgmstream_compresswave,
|
||||
init_vgmstream_ktac,
|
||||
init_vgmstream_mjb_mjh,
|
||||
init_vgmstream_mzrt_v1,
|
||||
init_vgmstream_bsnf,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
|
@ -1333,6 +1338,10 @@ static STREAMFILE* get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM* v
|
|||
return acm_get_streamfile(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
if (vgmstream->coding_type == coding_COMPRESSWAVE) {
|
||||
return compresswave_get_streamfile(vgmstream->codec_data);
|
||||
}
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
if (vgmstream->coding_type == coding_OGG_VORBIS) {
|
||||
return ogg_vorbis_get_streamfile(vgmstream->codec_data);
|
||||
|
|
|
@ -28,6 +28,7 @@ enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */
|
|||
//#define VGM_USE_FFMPEG
|
||||
//#define VGM_USE_ATRAC9
|
||||
//#define VGM_USE_CELT
|
||||
//#define VGM_USE_SPEEX
|
||||
|
||||
|
||||
#ifdef VGM_USE_VORBIS
|
||||
|
@ -178,6 +179,7 @@ typedef enum {
|
|||
coding_OKI4S, /* OKI 4-bit ADPCM with 16-bit output and cuadruple step */
|
||||
coding_PTADPCM, /* Platinum 4-bit ADPCM */
|
||||
coding_IMUSE, /* LucasArts iMUSE Variable ADPCM */
|
||||
coding_COMPRESSWAVE, /* CompressWave Huffman ADPCM */
|
||||
|
||||
/* others */
|
||||
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
|
||||
|
@ -234,6 +236,10 @@ typedef enum {
|
|||
coding_CELT_FSB, /* Custom Xiph CELT (MDCT-based) */
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_SPEEX
|
||||
coding_SPEEX, /* Custom Speex (CELP-based) */
|
||||
#endif
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
coding_FFmpeg, /* Formats handled by FFmpeg (ATRAC3, XMA, AC3, etc) */
|
||||
#endif
|
||||
|
@ -361,7 +367,7 @@ typedef enum {
|
|||
meta_PS2_EXST, /* Shadow of Colossus EXST */
|
||||
meta_SVAG_KCET,
|
||||
meta_PS_HEADERLESS, /* headerless PS-ADPCM */
|
||||
meta_PS2_MIB_MIH, /* MIB File + MIH Header*/
|
||||
meta_MIB_MIH,
|
||||
meta_PS2_MIC, /* KOEI MIC File */
|
||||
meta_PS2_VAGi, /* VAGi Interleaved File */
|
||||
meta_PS2_VAGp, /* VAGp Mono File */
|
||||
|
@ -749,6 +755,10 @@ typedef enum {
|
|||
meta_SBK,
|
||||
meta_DSP_WIIADPCM,
|
||||
meta_DSP_CWAC,
|
||||
meta_COMPRESSWAVE,
|
||||
meta_KTAC,
|
||||
meta_MJB_MJH,
|
||||
meta_BSNF,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
|
Loading…
Reference in New Issue