mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
commit
e3e8a8269d
@ -364,6 +364,7 @@ Manually setting values gets old, so TXTP supports a bunch of simple macros. The
|
||||
- `remix N (channels)`: same, but mixes selected channels to N channels properly adjusting volume (for layered bgm)
|
||||
- `crosstrack N`: crossfades between Nch tracks after every loop (loop count is adjusted as needed)
|
||||
- `crosslayer-v/b/e N`: crossfades Nch layers to the main track after every loop (loop count is adjusted as needed)
|
||||
- `downmix`: downmixes up to 8 channels (7.1, 5.1, etc) to stereo, using standard downmixing formulas.
|
||||
|
||||
`channels` can be multiple comma-separated channels or N~M ranges and may be ommited were applicable to mean "all channels" (channel order doesn't matter but it's internally fixed).
|
||||
|
||||
@ -519,9 +520,11 @@ The parser is fairly simplistic and lax, and may be erratic with edge cases or b
|
||||
|
||||
|
||||
## MINI-TXTP
|
||||
To simplify TXTP creation, if the .txtp is empty (0 bytes) its filename is used directly as a command. Note that extension is also included (since vgmstream needs a full filename).
|
||||
To simplify TXTP creation, if the .txtp doesn't set a name inside its filename is used directly including config. Note that extension must be included (since vgmstream needs a full filename). You can set `commands` inside the .txtp too:
|
||||
- *bgm.sxd2#12.txtp*: plays subsong 12
|
||||
- *Ryoshima Coast 1 & 2.aix#c1,2.txtp*: channel mask
|
||||
- *bgm.sxd2#12.txtp*, , inside has `commands = #@volume 0.5`: plays subsong 12 at half volume
|
||||
- *bgm.sxd2.txtp*, , inside has `commands = #12 #@volume 0.5`: plays subsong 12 at half volume
|
||||
- *Ryoshima Coast 1 & 2.aix#C1,2.txtp*: channel downmix
|
||||
- *boss2_3ningumi_ver6.adx#l2#F.txtp*: loop twice then play song end file normally
|
||||
- etc
|
||||
|
||||
|
@ -255,8 +255,8 @@ static void blitz_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset
|
||||
delta = (step >> 1) + delta * step; /* custom */
|
||||
sample_decoded += delta;
|
||||
|
||||
/* somehow the exe tries to clamp hist, but actually doesn't (bug?),
|
||||
* not sure if pcm buffer would be clamped outside though */
|
||||
/* in Zapper somehow the exe tries to clamp hist but actually doesn't (bug? not in Lilo & Stitch),
|
||||
* seems the pcm buffer must be clamped outside though to fix some scratchiness */
|
||||
*hist1 = sample_decoded;//clamp16(sample_decoded);
|
||||
*step_index += IMA_IndexTable[sample_nibble];
|
||||
if (*step_index < 0) *step_index=0;
|
||||
@ -445,7 +445,7 @@ void decode_blitz_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channels
|
||||
int nibble_shift = (i&1?4:0); //low nibble first
|
||||
|
||||
blitz_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
|
||||
outbuf[sample_count] = (short)(hist1);
|
||||
outbuf[sample_count] = (short)clamp16(hist1);
|
||||
}
|
||||
|
||||
stream->adpcm_history1_32 = hist1;
|
||||
|
@ -99,6 +99,7 @@ void reset_ogg_vorbis(VGMSTREAM *vgmstream) {
|
||||
ogg_vorbis_codec_data *data = vgmstream->codec_data;
|
||||
if (!data) return;
|
||||
|
||||
/* this seek cleans internal buffers */
|
||||
ov_pcm_seek(&data->ogg_vorbis_file, 0);
|
||||
}
|
||||
|
||||
@ -106,6 +107,8 @@ void seek_ogg_vorbis(VGMSTREAM *vgmstream, int32_t num_sample) {
|
||||
ogg_vorbis_codec_data *data = vgmstream->codec_data;
|
||||
if (!data) return;
|
||||
|
||||
/* this seek crosslaps to avoid possible clicks, so seeking to 0 will
|
||||
* decode a bit differently than ov_pcm_seek */
|
||||
ov_pcm_seek_lap(&data->ogg_vorbis_file, num_sample);
|
||||
}
|
||||
|
||||
|
@ -198,6 +198,7 @@ static const char* extension_list[] = {
|
||||
"ikm",
|
||||
"ild",
|
||||
"ilv", //txth/reserved [Star Wars Episode III (PS2)]
|
||||
"ima",
|
||||
"imc",
|
||||
"int",
|
||||
"isd",
|
||||
@ -321,6 +322,7 @@ static const char* extension_list[] = {
|
||||
"ovb",
|
||||
|
||||
"p04", //txth/reserved [Psychic Force 2012 (DC)]
|
||||
"p16", //txth/reserved [Astal (SAT)]
|
||||
"p1d", //txth/reserved [Farming Simulator 18 (3DS)]
|
||||
"p2a", //txth/reserved [Thunderhawk Operation Phoenix (PS2)]
|
||||
"p2bt",
|
||||
@ -388,9 +390,11 @@ static const char* extension_list[] = {
|
||||
"sbin",
|
||||
"sc",
|
||||
"scd",
|
||||
"sch",
|
||||
"sd9",
|
||||
"sdf",
|
||||
"sdt",
|
||||
"seb",
|
||||
"seg",
|
||||
"sf0",
|
||||
"sfl",
|
||||
@ -817,8 +821,8 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_PS2_VAGi, "Sony VAGi header"},
|
||||
{meta_PS2_VAGp, "Sony VAGp header"},
|
||||
{meta_PS2_pGAV, "Sony pGAV header"},
|
||||
{meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"},
|
||||
{meta_STR_WAV, "Blitz Games STR+WAV header"},
|
||||
{meta_SEB, "Game Arts .SEB header"},
|
||||
{meta_STR_WAV, "Blitz Games .STR+WAV header"},
|
||||
{meta_PS2_ILD, "ILD header"},
|
||||
{meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"},
|
||||
{meta_XBOX_WAVM, "Xbox WAVM raw header"},
|
||||
@ -1190,6 +1194,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_XAVS, "Reflections XAVS header"},
|
||||
{meta_PSF, "Pivotal PSF header"},
|
||||
{meta_DSP_ITL_i, "Infernal .ITL DSP header"},
|
||||
{meta_IMA, "Blitz Games .IMA header"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -708,6 +708,10 @@
|
||||
RelativePath=".\meta\nub_idsp.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ima.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\imc.c"
|
||||
>
|
||||
@ -1395,7 +1399,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\psx_gms.c"
|
||||
RelativePath=".\meta\seb.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
|
@ -164,6 +164,7 @@
|
||||
<ClCompile Include="meta\ezw.c" />
|
||||
<ClCompile Include="meta\ffmpeg.c" />
|
||||
<ClCompile Include="meta\g1l.c" />
|
||||
<ClCompile Include="meta\ima.c" />
|
||||
<ClCompile Include="meta\imc.c" />
|
||||
<ClCompile Include="meta\ios_psnd.c" />
|
||||
<ClCompile Include="meta\ktss.c" />
|
||||
@ -440,7 +441,7 @@
|
||||
<ClCompile Include="meta\xa.c" />
|
||||
<ClCompile Include="meta\fag.c" />
|
||||
<ClCompile Include="meta\ffdl.c" />
|
||||
<ClCompile Include="meta\psx_gms.c" />
|
||||
<ClCompile Include="meta\seb.c" />
|
||||
<ClCompile Include="meta\ea_swvr.c" />
|
||||
<ClCompile Include="meta\raw.c" />
|
||||
<ClCompile Include="meta\redspark.c" />
|
||||
|
@ -859,7 +859,7 @@
|
||||
<ClCompile Include="meta\ffdl.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\psx_gms.c">
|
||||
<ClCompile Include="meta\seb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ea_swvr.c">
|
||||
@ -1447,6 +1447,9 @@
|
||||
<ClCompile Include="meta\ios_psnd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ima.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\imc.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -79,6 +79,23 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->codec_data = hca_data;
|
||||
|
||||
/* assumed mappings */
|
||||
{
|
||||
static const uint32_t hca_mappings[] = {
|
||||
0,
|
||||
mapping_MONO,
|
||||
mapping_STEREO,
|
||||
mapping_2POINT1,
|
||||
mapping_QUAD,
|
||||
mapping_5POINT0,
|
||||
mapping_5POINT1,
|
||||
mapping_7POINT0,
|
||||
mapping_7POINT1,
|
||||
};
|
||||
|
||||
vgmstream->channel_layout = hca_mappings[vgmstream->channels];
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
|
52
src/meta/ima.c
Normal file
52
src/meta/ima.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* .IMA - Blitz Games early games [Lilo & Stitch: Trouble in Paradise (PC)] */
|
||||
VGMSTREAM * init_vgmstream_ima(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, num_samples, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "ima"))
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x02000000) /* version? */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0)
|
||||
goto fail;
|
||||
|
||||
num_samples = read_32bitLE(0x08, streamFile);
|
||||
channel_count = read_32bitLE(0x0c,streamFile);
|
||||
sample_rate = read_32bitLE(0x10, streamFile);
|
||||
|
||||
loop_flag = 0;
|
||||
start_offset = 0x14;
|
||||
|
||||
if (channel_count > 1) /* unknown interleave */
|
||||
goto fail;
|
||||
if (num_samples != ima_bytes_to_samples(get_streamfile_size(streamFile) - start_offset, channel_count))
|
||||
goto fail;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_IMA;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->coding_type = coding_BLITZ_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -88,7 +88,7 @@ VGMSTREAM * init_vgmstream_raw(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_vag(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile);
|
||||
|
||||
@ -856,5 +856,8 @@ VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_psf_single(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE * streamFile);
|
||||
VGMSTREAM * init_vgmstream_sch(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ima(STREAMFILE * streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
475
src/meta/psf.c
475
src/meta/psf.c
@ -25,32 +25,36 @@ VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) {
|
||||
flags = read_8bit(0x03,streamFile);
|
||||
switch(flags) {
|
||||
case 0xC0: /* [The Great Escape (PS2), Conflict: Desert Storm (PS2)] */
|
||||
case 0x40: /* [The Great Escape (PS2)] */
|
||||
case 0xA1: /* [Conflict: Desert Storm 2 (PS2)] */
|
||||
case 0x21: /* [Conflict: Desert Storm 2 (PS2), Conflict: Global Storm (PS2)] */
|
||||
//case 0x22: /* [Conflict: Vietman (PS2)] */ //todo weird size value, stereo, only one found
|
||||
channel_count = 2;
|
||||
if (flags == 0x21)
|
||||
channel_count = 1;
|
||||
interleave = 0x10;
|
||||
codec = coding_PSX;
|
||||
interleave = 0x10;
|
||||
|
||||
channel_count = 2;
|
||||
if (flags == 0x21 || flags == 0x40)
|
||||
channel_count = 1;
|
||||
start_offset = 0x08;
|
||||
break;
|
||||
|
||||
case 0x80: /* [The Great Escape (PC/Xbox), Conflict: Desert Storm (Xbox/GC), Conflict: Desert Storm 2 (Xbox)] */
|
||||
case 0x81: /* [Conflict: Desert Storm 2 (Xbox), Conflict: Vietnam (Xbox)] */
|
||||
case 0x01: /* [Conflict: Global Storm (Xbox)] */
|
||||
codec = coding_PSX_pivotal;
|
||||
interleave = 0x10;
|
||||
|
||||
channel_count = 2;
|
||||
if (flags == 0x01)
|
||||
channel_count = 1;
|
||||
interleave = 0x10;
|
||||
codec = coding_PSX_pivotal;
|
||||
start_offset = 0x08;
|
||||
break;
|
||||
|
||||
case 0xD1: /* [Conflict: Desert Storm 2 (GC)] */
|
||||
channel_count = 2;
|
||||
interleave = 0x08;
|
||||
codec = coding_NGC_DSP;
|
||||
interleave = 0x08;
|
||||
|
||||
channel_count = 2;
|
||||
start_offset = 0x08 + 0x60 * channel_count;
|
||||
break;
|
||||
|
||||
@ -65,16 +69,14 @@ VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) {
|
||||
/* pitch/cents? */
|
||||
rate_value = (psf_config >> 20) & 0xFFF;
|
||||
switch(rate_value) {
|
||||
//case 0xEB5:
|
||||
//case 0xEB4:
|
||||
case 0xEB3: sample_rate = 44100; break;
|
||||
case 0x555: sample_rate = 16000; break;
|
||||
case 0x355: sample_rate = 11050; break;
|
||||
case 0x1d5: sample_rate = 6000; break; /* ? */
|
||||
case 0x1cc: sample_rate = 5000; break;
|
||||
case 3763: sample_rate = 44100; break;
|
||||
case 1365: sample_rate = 16000; break;
|
||||
case 940: sample_rate = 11050; break;
|
||||
case 460: sample_rate = 5000; break;
|
||||
default:
|
||||
VGM_LOG("PSF: unknown rate value %x\n", rate_value);
|
||||
goto fail;
|
||||
sample_rate = rate_value * 11.72; /* not exact but works well enough */
|
||||
break;
|
||||
}
|
||||
|
||||
data_size = (psf_config & 0xFFFFF) * (interleave * channel_count); /* in blocks */
|
||||
@ -119,6 +121,8 @@ VGMSTREAM * init_vgmstream_psf_single(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->stream_size = data_size;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
@ -144,7 +148,7 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) != 0x50534660 && /* "PSF\60" [The Great Escape (PC/Xbox/PS2), Conflict: Desert Storm (Xbox/GC)] */
|
||||
read_32bitBE(0x00,streamFile) != 0x50534631) /* "PSF\31" [Conflict: Desert Storm 2 (Xbox/GC/PS2)] */
|
||||
read_32bitBE(0x00,streamFile) != 0x50534631) /* "PSF\31" [Conflict: Desert Storm 2 (Xbox/GC/PS2), Conflict: Global Terror (Xbox)] */
|
||||
goto fail;
|
||||
|
||||
segment_count = read_32bitLE(0x04, streamFile);
|
||||
@ -161,10 +165,10 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) {
|
||||
for (i = 0; i < segment_count; i++) {
|
||||
off_t psf_offset;
|
||||
size_t psf_size;
|
||||
uint32_t psf_id;
|
||||
|
||||
/* mini table */
|
||||
psf_offset = read_32bitLE(offset + 0x00, streamFile);
|
||||
psf_size = get_streamfile_size(streamFile) - psf_offset; /* not ok but meh */
|
||||
/* 0x04-0c: 0x02*4 transition segments (possibly to 4 song variations) */
|
||||
|
||||
/* use last section transition as loop */
|
||||
@ -175,15 +179,7 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
/* multiple segment can point to the same PSF offset (for repeated song sections) */
|
||||
//todo reuse repeated VGMSTREAMs to improve memory a bit
|
||||
|
||||
psf_id = read_32bitBE(psf_offset + 0x00, streamFile);
|
||||
psf_size = read_32bitLE(psf_offset + 0x04, streamFile);
|
||||
if (psf_id == 0x505346D1) //todo improve
|
||||
psf_size = (psf_size & 0xFFFFF) * 0x10;
|
||||
else
|
||||
psf_size = (psf_size & 0xFFFFF) * 0x20;
|
||||
//;VGM_LOG("PSF: offset=%lx, size=%x\n", psf_offset, psf_size);
|
||||
//todo reuse repeated VGMSTREAMs to improve memory and bitrate calcs a bit
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(streamFile, psf_offset, psf_size, "psf");
|
||||
if (!temp_streamFile) goto fail;
|
||||
@ -200,8 +196,6 @@ VGMSTREAM * init_vgmstream_psf_segmented(STREAMFILE *streamFile) {
|
||||
vgmstream = allocate_segmented_vgmstream(data,loop_flag, loop_start, loop_end);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->stream_size = get_streamfile_size(streamFile);
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
if (!vgmstream) free_layout_segmented(data);
|
||||
@ -210,33 +204,422 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
VGMSTREAM * init_vgmstream_sch(STREAMFILE *streamFile) {
|
||||
/* ***************************************************** */
|
||||
|
||||
static VGMSTREAM * init_vgmstream_psf_pfsm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
int loop_flag, channel_count, sample_rate = 0, rate_value = 0, interleave, big_endian;
|
||||
size_t data_size;
|
||||
coding_t codec;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
||||
/* standard:
|
||||
* 0x00: -1/number (lang?)
|
||||
* 0x04: config/size?
|
||||
* 0x08: channel size? only ok for PSX-pivotal
|
||||
* 0x0c: sample rate or rate_value
|
||||
* 0x0e: 0x4=PSX-pivotal or 0xFF=PSX
|
||||
* 0x0f: name size (0xCC/FF=null)
|
||||
* 0x10: data
|
||||
*
|
||||
* GC is similar with 0x20-align between some fields
|
||||
*/
|
||||
|
||||
/* checks */
|
||||
//if (!check_extensions(streamFile, "psf"))
|
||||
// goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5046534D && /* "PFSM" */
|
||||
read_32bitLE(0x00,streamFile) != 0x5046534D) /* "PFSM" (BE) */
|
||||
goto fail;
|
||||
|
||||
big_endian = (read_32bitLE(0x00,streamFile) == 0x5046534D);
|
||||
if (big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
read_16bit = read_16bitBE;
|
||||
}
|
||||
else {
|
||||
read_32bit = read_32bitLE;
|
||||
read_16bit = read_16bitLE;
|
||||
}
|
||||
|
||||
loop_flag = 0;
|
||||
|
||||
|
||||
if (big_endian && read_32bit(0x50, streamFile) != 0) { /* GC */
|
||||
codec = coding_NGC_DSP;
|
||||
interleave = 0x08;
|
||||
channel_count = 1;
|
||||
rate_value = (uint16_t)read_16bit(0x48, streamFile);
|
||||
|
||||
start_offset = 0x60 + 0x60 * channel_count;
|
||||
}
|
||||
else if (big_endian) { /* GC */
|
||||
codec = coding_PCM16BE;
|
||||
interleave = 0x02;
|
||||
channel_count = 1;
|
||||
rate_value = (uint16_t)read_16bit(0x48, streamFile);
|
||||
|
||||
start_offset = 0x60;
|
||||
}
|
||||
else if ((uint8_t)read_8bit(0x16, streamFile) == 0xFF) { /* PS2 */
|
||||
codec = coding_PSX;
|
||||
interleave = 0x10;
|
||||
rate_value = (uint16_t)read_16bit(0x14, streamFile);
|
||||
channel_count = 1;
|
||||
|
||||
start_offset = 0x18;
|
||||
}
|
||||
else { /* PC/Xbox, some PS2/GC */
|
||||
codec = coding_PSX_pivotal;
|
||||
interleave = 0x10;
|
||||
sample_rate = (uint16_t)read_16bit(0x14, streamFile);
|
||||
channel_count = 1;
|
||||
|
||||
start_offset = 0x18;
|
||||
}
|
||||
|
||||
data_size = get_streamfile_size(streamFile) - start_offset;
|
||||
|
||||
/* pitch/cents? */
|
||||
if (sample_rate == 0) {
|
||||
/* pitch/cents? */
|
||||
switch(rate_value) {
|
||||
case 3763: sample_rate = 44100; break;
|
||||
case 1365: sample_rate = 16000; break;
|
||||
case 940: sample_rate = 11050; break;
|
||||
case 460: sample_rate = 5000; break;
|
||||
default:
|
||||
VGM_LOG("PSF: unknown rate value %x\n", rate_value);
|
||||
sample_rate = rate_value * 11.72; /* not exact but works well enough */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_PSF;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
switch(codec) {
|
||||
case coding_PCM16BE:
|
||||
vgmstream->coding_type = coding_PCM16BE;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
|
||||
break;
|
||||
|
||||
case coding_PSX:
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
case coding_PSX_pivotal:
|
||||
vgmstream->coding_type = coding_PSX_pivotal;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
|
||||
vgmstream->num_samples = ps_cfg_bytes_to_samples(data_size, 0x10, channel_count);
|
||||
break;
|
||||
|
||||
case coding_NGC_DSP:
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = interleave;
|
||||
/* has standard DSP headers at 0x08 */
|
||||
dsp_read_coefs_be(vgmstream,streamFile,0x60+0x1c,0x60);
|
||||
dsp_read_hist_be (vgmstream,streamFile,0x60+0x40,0x60);
|
||||
|
||||
vgmstream->num_samples = read_32bitBE(0x60, streamFile);//dsp_bytes_to_samples(data_size, channel_count);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef enum { UNKNOWN, IMUS, PFST, PFSM } sch_type;
|
||||
|
||||
|
||||
|
||||
/* SCH - Pivotal games multi-audio container [The Great Escape, Conflict series] */
|
||||
VGMSTREAM * init_vgmstream_sch(STREAMFILE *streamFile) {
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
STREAMFILE *external_streamFile = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
off_t skip = 0, chunk_offset, target_offset = 0, header_offset, subfile_offset = 0;
|
||||
size_t file_size, chunk_padding, target_size = 0, subfile_size = 0;
|
||||
int big_endian;
|
||||
int total_subsongs = 0, target_subsong = streamFile->stream_index;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||
sch_type target_type = UNKNOWN;
|
||||
char stream_name[STREAM_NAME_SIZE] ={0};
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "sch"))
|
||||
goto fail;
|
||||
|
||||
/* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids):
|
||||
* - SCH\0: start
|
||||
* - IMUS: points to a external .psf + segment table (same as in .psf, TGE only?)
|
||||
* - BANK: volume/etc info? points to something?
|
||||
* - PFSM: single .psf-like file (larger header)
|
||||
* - PFST: points to single PSF offset (.psf in TGE, or STREAMS.SWD); may be chained to next PFST?
|
||||
*
|
||||
* no other info so total subsongs would be count of usable chunks
|
||||
* in later games, segmented .psf seems to be removed and PFST is used instead
|
||||
*/
|
||||
if (read_32bitBE(0x00,streamFile) == 0x48445253) /* "HDRSND" (found on later games) */
|
||||
skip = 0x0E;
|
||||
if (read_32bitBE(skip + 0x00,streamFile) != 0x53434800 && /* "SCH\0" */
|
||||
read_32bitLE(skip + 0x00,streamFile) != 0x53434800) /* "SCH\0" (BE consoles) */
|
||||
goto fail;
|
||||
|
||||
|
||||
/* chunked format (id+size, GC pads to 0x20 and uses BE/inverted ids):
|
||||
* no other info so total subsongs would be count of usable chunks
|
||||
* (offsets are probably in level .dat files) */
|
||||
big_endian = (read_32bitLE(skip + 0x00,streamFile) == 0x53434800);
|
||||
if (big_endian) {
|
||||
read_32bit = read_32bitBE;
|
||||
chunk_padding = 0x18;
|
||||
}
|
||||
else {
|
||||
read_32bit = read_32bitLE;
|
||||
chunk_padding = 0;
|
||||
}
|
||||
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
if (read_32bit(skip + 0x04,streamFile) + skip + 0x08 + chunk_padding < file_size) /* sometimes padded */
|
||||
goto fail;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
|
||||
chunk_offset = skip + 0x08 + chunk_padding;
|
||||
|
||||
/* get all files*/
|
||||
while (chunk_offset < file_size) {
|
||||
uint32_t chunk_id = read_32bitBE(chunk_offset + 0x00,streamFile);
|
||||
uint32_t chunk_size = read_32bit(chunk_offset + 0x04,streamFile);
|
||||
sch_type current_type = UNKNOWN;
|
||||
|
||||
switch(chunk_id) {
|
||||
case 0x494D5553: /* "IMUS" (TGE PC/Xbox only) */
|
||||
current_type = IMUS;
|
||||
break;
|
||||
|
||||
case 0x54534650:
|
||||
case 0x50465354: /* "PFST" */
|
||||
current_type = PFST;
|
||||
break;
|
||||
|
||||
case 0x4D534650:
|
||||
case 0x5046534D: /* "PFSM" */
|
||||
current_type = PFSM;
|
||||
break;
|
||||
|
||||
case 0x4B4E4142:
|
||||
case 0x42414E4B: /* "BANK" */
|
||||
/* unknown format (variable size), maybe config for entry numbers */
|
||||
break;
|
||||
case 0x424C4F4B: /* "BLOK" [Conflict: Desert Storm (Xbox)] */
|
||||
/* some ids or something? */
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("SCH: unknown chunk at %lx\n", chunk_offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (current_type != UNKNOWN)
|
||||
total_subsongs++;
|
||||
|
||||
if (total_subsongs == target_subsong && target_type == UNKNOWN) {
|
||||
target_type = current_type;
|
||||
target_offset = chunk_offset;
|
||||
target_size = 0x08 + chunk_padding + chunk_size;
|
||||
}
|
||||
|
||||
chunk_offset += 0x08 + chunk_padding + chunk_size;
|
||||
}
|
||||
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
if (target_size == 0) goto fail;
|
||||
|
||||
header_offset = target_offset + 0x08 + chunk_padding;
|
||||
|
||||
//;VGM_LOG("SCH: offset=%lx, size=%x\n",target_offset, target_size);
|
||||
|
||||
switch(target_type) {
|
||||
case IMUS: { /* external segmented track */
|
||||
STREAMFILE *psf_streamFile;
|
||||
uint8_t name_size;
|
||||
char name[255];
|
||||
|
||||
/* 0x00: config/size?
|
||||
* 0x04: name size
|
||||
* 0x05: segments
|
||||
* 0x06: ?
|
||||
* 0x08: relative path to .psf
|
||||
* 0xNN: segment table (same as .psf)
|
||||
*/
|
||||
|
||||
name_size = read_8bit(header_offset + 0x04, streamFile);
|
||||
read_string(name,name_size, header_offset + 0x08, streamFile);
|
||||
|
||||
/* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */
|
||||
if ((uint8_t)read_8bit(header_offset + 0x07, streamFile) == 0xCC) {
|
||||
external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd");
|
||||
if (!external_streamFile) goto fail;
|
||||
|
||||
subfile_offset = read_32bit(header_offset + 0x08 + name_size, streamFile);
|
||||
subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf");
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
psf_streamFile = temp_streamFile;
|
||||
}
|
||||
else {
|
||||
external_streamFile = open_streamfile_by_filename(streamFile, name);
|
||||
if (!external_streamFile) goto fail;
|
||||
|
||||
psf_streamFile = external_streamFile;
|
||||
}
|
||||
|
||||
vgmstream = init_vgmstream_psf_segmented(psf_streamFile);
|
||||
if (!vgmstream) {
|
||||
vgmstream = init_vgmstream_psf_single(psf_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
|
||||
snprintf(stream_name,sizeof(stream_name), "%s-%s" , "IMUS", name);
|
||||
break;
|
||||
}
|
||||
|
||||
case PFST: { /* external track */
|
||||
STREAMFILE *psf_streamFile;
|
||||
uint8_t name_size;
|
||||
char name[255];
|
||||
|
||||
if (chunk_padding == 0 && target_size > 0x08 + 0x0c) { /* TGE PC/Xbox version */
|
||||
/* 0x00: -1/0
|
||||
* 0x04: config/size?
|
||||
* 0x08: channel size
|
||||
* 0x0c: sample rate? (differs vs PSF)
|
||||
* 0x0e: 4?
|
||||
* 0x0f: name size
|
||||
* 0x10: name
|
||||
*/
|
||||
|
||||
/* later games have name but actually use bigfile [Conflict: Global Storm (Xbox)] */
|
||||
if ((read_32bitBE(header_offset + 0x14, streamFile) & 0x0000FFFF) == 0xCCCC) {
|
||||
name_size = read_8bit(header_offset + 0x13, streamFile);
|
||||
read_string(name,name_size, header_offset + 0x18, streamFile);
|
||||
|
||||
external_streamFile = open_streamfile_by_filename(streamFile, "Stream.swd");
|
||||
if (!external_streamFile) goto fail;
|
||||
|
||||
subfile_offset = read_32bit(header_offset + 0x0c, streamFile);
|
||||
subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf");
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
psf_streamFile = temp_streamFile;
|
||||
}
|
||||
else {
|
||||
name_size = read_8bit(header_offset + 0x0f, streamFile);
|
||||
read_string(name,name_size, header_offset + 0x10, streamFile);
|
||||
|
||||
external_streamFile = open_streamfile_by_filename(streamFile, name);
|
||||
if (!external_streamFile) goto fail;
|
||||
|
||||
psf_streamFile = external_streamFile;
|
||||
}
|
||||
}
|
||||
else if (chunk_padding) {
|
||||
strcpy(name, "STREAM.SWD"); /* fixed */
|
||||
|
||||
/* 0x00: -1
|
||||
* 0x04: config/size?
|
||||
* 0x08: .swd offset
|
||||
*/
|
||||
external_streamFile = open_streamfile_by_filename(streamFile, name);
|
||||
if (!external_streamFile) goto fail;
|
||||
|
||||
subfile_offset = read_32bit(header_offset + 0x24, streamFile);
|
||||
subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf");
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
psf_streamFile = temp_streamFile;
|
||||
}
|
||||
else { /* others */
|
||||
strcpy(name, "STREAM.SWD"); /* fixed */
|
||||
|
||||
/* 0x00: -1
|
||||
* 0x04: config/size?
|
||||
* 0x08: .swd offset
|
||||
*/
|
||||
external_streamFile = open_streamfile_by_filename(streamFile, name);
|
||||
if (!external_streamFile) goto fail;
|
||||
|
||||
subfile_offset = read_32bit(header_offset + 0x08, streamFile);
|
||||
subfile_size = get_streamfile_size(external_streamFile) - subfile_offset; /* not ok but meh */
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(external_streamFile, subfile_offset,subfile_size, "psf");
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
psf_streamFile = temp_streamFile;
|
||||
}
|
||||
|
||||
vgmstream = init_vgmstream_psf_segmented(psf_streamFile);
|
||||
if (!vgmstream) {
|
||||
vgmstream = init_vgmstream_psf_single(psf_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
}
|
||||
|
||||
snprintf(stream_name,sizeof(stream_name), "%s-%s" , "PFST", name);
|
||||
break;
|
||||
}
|
||||
|
||||
case PFSM:
|
||||
/* internal sound */
|
||||
|
||||
temp_streamFile = setup_subfile_streamfile(streamFile, target_offset,target_size, NULL);
|
||||
if (!temp_streamFile) goto fail;
|
||||
|
||||
vgmstream = init_vgmstream_psf_pfsm(temp_streamFile);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
snprintf(stream_name,sizeof(stream_name), "%s" , "PFSM");
|
||||
break;
|
||||
|
||||
default: /* target not found */
|
||||
goto fail;
|
||||
}
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
strcpy(vgmstream->stream_name, stream_name);
|
||||
|
||||
return vgmstream;
|
||||
fail:
|
||||
if (!vgmstream) free_layout_layered(data);
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(external_streamFile);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(temp_streamFile);
|
||||
close_streamfile(external_streamFile);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
@ -1,76 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* GMS
|
||||
|
||||
PSX GMS format has no recognition ID.
|
||||
This format was used essentially in Grandia Games but
|
||||
can be easily used by other header format as the format of the header is very simple
|
||||
|
||||
known extensions : GMS
|
||||
|
||||
2008-05-19 - Fastelbja : First version ...
|
||||
*/
|
||||
|
||||
VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag=0;
|
||||
int channel_count;
|
||||
off_t start_offset;
|
||||
int i;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("gms",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check loop */
|
||||
loop_flag = (read_32bitLE(0x20,streamFile)==0);
|
||||
|
||||
/* Always stereo files */
|
||||
channel_count=read_32bitLE(0x00,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->num_samples = read_32bitLE(0x1C,streamFile);
|
||||
|
||||
/* Get loop point values */
|
||||
if(vgmstream->loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile);
|
||||
}
|
||||
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x800;
|
||||
vgmstream->meta_type = meta_PSX_GMS;
|
||||
|
||||
start_offset = 0x800;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
(off_t)(start_offset+vgmstream->interleave_block_size*i);
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
53
src/meta/seb.c
Normal file
53
src/meta/seb.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "meta.h"
|
||||
|
||||
|
||||
/* .seb - Game Arts games [Grandia (PS1), Grandia II/III/X (PS2)] */
|
||||
VGMSTREAM * init_vgmstream_seb(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .seb: found in Grandia II (PS2) .idx */
|
||||
/* .gms: fake? (.stz+idx bigfile without names, except in Grandia II) */
|
||||
if (!check_extensions(streamFile, "seb,gms,"))
|
||||
goto fail;
|
||||
|
||||
channel_count = read_32bitLE(0x00,streamFile);
|
||||
if (channel_count > 2) goto fail; /* mono or stereo */
|
||||
/* 0x08/0c: unknown count, possibly related to looping */
|
||||
|
||||
start_offset = 0x800;
|
||||
|
||||
if (read_32bitLE(0x10,streamFile) > get_streamfile_size(streamFile) || /* loop start offset */
|
||||
read_32bitLE(0x18,streamFile) > get_streamfile_size(streamFile)) /* loop end offset */
|
||||
goto fail;
|
||||
/* in Grandia III sometimes there is a value at 0x24/34 */
|
||||
|
||||
loop_flag = (read_32bitLE(0x20,streamFile) == 0);
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_SEB;
|
||||
vgmstream->sample_rate = read_32bitLE(0x04,streamFile);
|
||||
|
||||
vgmstream->num_samples = read_32bitLE(0x1c,streamFile);
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile);
|
||||
vgmstream->loop_end_sample = read_32bitLE(0x1c,streamFile);
|
||||
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = 0x800;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -27,6 +27,7 @@ typedef enum {
|
||||
MACRO_LAYER,
|
||||
MACRO_CROSSTRACK,
|
||||
MACRO_CROSSLAYER,
|
||||
MACRO_DOWNMIX,
|
||||
|
||||
} txtp_mix_t;
|
||||
|
||||
@ -498,6 +499,7 @@ static void apply_config(VGMSTREAM *vgmstream, txtp_entry *current) {
|
||||
case MACRO_LAYER: mixing_macro_layer(vgmstream, mix.max, mix.mask, mix.mode); break;
|
||||
case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix.max); break;
|
||||
case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix.max, mix.mode); break;
|
||||
case MACRO_DOWNMIX: mixing_macro_downmix(vgmstream, mix.max); break;
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -1071,6 +1073,16 @@ static void parse_config(txtp_entry *cfg, char *config) {
|
||||
|
||||
add_mixing(cfg, &mix, type);
|
||||
}
|
||||
else if (strcmp(command,"@downmix") == 0) {
|
||||
txtp_mix_data mix = {0};
|
||||
|
||||
mix.max = 2; /* stereo only for now */
|
||||
//nm = get_int(config, &mix.max);
|
||||
//config += nm;
|
||||
//if (nm == 0) continue;
|
||||
|
||||
add_mixing(cfg, &mix, MACRO_DOWNMIX);
|
||||
}
|
||||
else if (config[nc] == ' ') {
|
||||
//;VGM_LOG("TXTP: comment\n");
|
||||
break; /* comment, ignore rest */
|
||||
@ -1305,29 +1317,12 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) {
|
||||
txtp->is_segmented = 1;
|
||||
|
||||
|
||||
/* empty file: use filename with config (ex. "song.ext#3.txtp") */
|
||||
if (get_streamfile_size(streamFile) == 0) {
|
||||
char filename[PATH_LIMIT] = {0};
|
||||
char* ext;
|
||||
get_streamfile_filename(streamFile, filename,PATH_LIMIT);
|
||||
|
||||
/* remove ".txtp" */
|
||||
ext = strrchr(filename,'.');
|
||||
if (!ext) goto fail; /* ??? */
|
||||
ext[0] = '\0';
|
||||
|
||||
if (!add_entry(txtp, filename, 0))
|
||||
goto fail;
|
||||
|
||||
return txtp;
|
||||
}
|
||||
|
||||
|
||||
/* skip BOM if needed */
|
||||
if ((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF)
|
||||
if (file_size > 0 &&
|
||||
((uint16_t)read_16bitLE(0x00, streamFile) == 0xFFFE || (uint16_t)read_16bitLE(0x00, streamFile) == 0xFEFF))
|
||||
txt_offset = 0x02;
|
||||
|
||||
/* normal file: read and parse lines */
|
||||
/* read and parse lines */
|
||||
while (txt_offset < file_size) {
|
||||
char line[TXTP_LINE_MAX] = {0};
|
||||
char key[TXTP_LINE_MAX] = {0}, val[TXTP_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */
|
||||
@ -1359,6 +1354,16 @@ static txtp_header* parse_txtp(STREAMFILE* streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3"
|
||||
* (it's possible to have default "commands" inside the .txtp plus filename+config) */
|
||||
if (txtp->entry_count == 0) {
|
||||
char filename[PATH_LIMIT] = {0};
|
||||
|
||||
get_streamfile_basename(streamFile, filename, sizeof(filename));
|
||||
|
||||
add_entry(txtp, filename, 0);
|
||||
}
|
||||
|
||||
|
||||
return txtp;
|
||||
fail:
|
||||
|
114
src/mixing.c
114
src/mixing.c
@ -925,6 +925,120 @@ void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode) {
|
||||
mixing_push_killmix(vgmstream, max);
|
||||
}
|
||||
|
||||
|
||||
typedef enum {
|
||||
pos_FL = 0,
|
||||
pos_FR = 1,
|
||||
pos_FC = 2,
|
||||
pos_LFE = 3,
|
||||
pos_BL = 4,
|
||||
pos_BR = 5,
|
||||
pos_FLC = 6,
|
||||
pos_FRC = 7,
|
||||
pos_BC = 8,
|
||||
pos_SL = 9,
|
||||
pos_SR = 10,
|
||||
} mixing_position_t;
|
||||
|
||||
void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/) {
|
||||
mixing_data *data = vgmstream->mixing_data;
|
||||
int ch, output_channels, mp_in, mp_out, ch_in, ch_out;
|
||||
mapping_t input_mapping, output_mapping;
|
||||
const double vol_max = 1.0;
|
||||
const double vol_sqrt = 1 / sqrt(2);
|
||||
const double vol_half = 1 / 2;
|
||||
double matrix[16][16] = {{0}};
|
||||
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
if (max <= 1 || data->output_channels <= max || max >= 8)
|
||||
return;
|
||||
|
||||
/* assume WAV defaults if not set */
|
||||
input_mapping = vgmstream->channel_layout;
|
||||
if (input_mapping == 0) {
|
||||
switch(data->output_channels) {
|
||||
case 1: input_mapping = mapping_MONO; break;
|
||||
case 2: input_mapping = mapping_STEREO; break;
|
||||
case 3: input_mapping = mapping_2POINT1; break;
|
||||
case 4: input_mapping = mapping_QUAD; break;
|
||||
case 5: input_mapping = mapping_5POINT0; break;
|
||||
case 6: input_mapping = mapping_5POINT1; break;
|
||||
case 7: input_mapping = mapping_7POINT0; break;
|
||||
case 8: input_mapping = mapping_7POINT1; break;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
/* build mapping matrix[input channel][output channel] = volume,
|
||||
* using standard WAV/AC3 downmix formulas
|
||||
* - https://www.audiokinetic.com/library/edge/?source=Help&id=downmix_tables
|
||||
* - https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations
|
||||
*/
|
||||
switch(max) {
|
||||
case 1:
|
||||
output_mapping = mapping_MONO;
|
||||
matrix[pos_FL][pos_FC] = vol_sqrt;
|
||||
matrix[pos_FR][pos_FC] = vol_sqrt;
|
||||
matrix[pos_FC][pos_FC] = vol_max;
|
||||
matrix[pos_SL][pos_FC] = vol_half;
|
||||
matrix[pos_SR][pos_FC] = vol_half;
|
||||
matrix[pos_BL][pos_FC] = vol_half;
|
||||
matrix[pos_BR][pos_FC] = vol_half;
|
||||
break;
|
||||
case 2:
|
||||
output_mapping = mapping_STEREO;
|
||||
matrix[pos_FL][pos_FL] = vol_max;
|
||||
matrix[pos_FR][pos_FR] = vol_max;
|
||||
matrix[pos_FC][pos_FL] = vol_sqrt;
|
||||
matrix[pos_FC][pos_FR] = vol_sqrt;
|
||||
matrix[pos_SL][pos_FL] = vol_sqrt;
|
||||
matrix[pos_SR][pos_FR] = vol_sqrt;
|
||||
matrix[pos_BL][pos_FL] = vol_sqrt;
|
||||
matrix[pos_BR][pos_FR] = vol_sqrt;
|
||||
break;
|
||||
default:
|
||||
/* not sure if +3ch would use FC/LFE, SL/BR and whatnot without passing extra config, so ignore for now */
|
||||
return;
|
||||
}
|
||||
|
||||
/* save and make N fake channels at the beginning for easier calcs */
|
||||
output_channels = data->output_channels;
|
||||
for (ch = 0; ch < max; ch++) {
|
||||
mixing_push_upmix(vgmstream, 0);
|
||||
}
|
||||
|
||||
/* downmix */
|
||||
ch_in = 0;
|
||||
for (mp_in = 0; mp_in < 16; mp_in++) {
|
||||
/* read input mapping (ex. 5.1) and find channel */
|
||||
if (!(input_mapping & (1<<mp_in)))
|
||||
continue;
|
||||
|
||||
ch_out = 0;
|
||||
for (mp_out = 0; mp_out < 16; mp_out++) {
|
||||
/* read output mapping (ex. 2.0) and find channel */
|
||||
if (!(output_mapping & (1<<mp_out)))
|
||||
continue;
|
||||
|
||||
VGM_LOG("i=%i,j=%i, ch_out=%i, ch_in=%i + %i, %f\n", mp_in,mp_out,ch_out, max, ch_in, matrix[mp_out][mp_in]);
|
||||
mixing_push_add(vgmstream, ch_out, max + ch_in, matrix[mp_in][mp_out]);
|
||||
|
||||
ch_out++;
|
||||
if (ch_out > max)
|
||||
break;
|
||||
}
|
||||
|
||||
ch_in++;
|
||||
if (ch_in >= output_channels)
|
||||
break;
|
||||
}
|
||||
|
||||
/* remove unneeded channels */
|
||||
mixing_push_killmix(vgmstream, max);
|
||||
}
|
||||
|
||||
/* ******************************************************************* */
|
||||
|
||||
void mixing_setup(VGMSTREAM * vgmstream, int32_t max_sample_count) {
|
||||
|
@ -36,6 +36,7 @@ void mixing_macro_track(VGMSTREAM* vgmstream, uint32_t mask);
|
||||
void mixing_macro_layer(VGMSTREAM* vgmstream, int max, uint32_t mask, char mode);
|
||||
void mixing_macro_crosstrack(VGMSTREAM* vgmstream, int max);
|
||||
void mixing_macro_crosslayer(VGMSTREAM* vgmstream, int max, char mode);
|
||||
void mixing_macro_downmix(VGMSTREAM* vgmstream, int max /*, mapping_t output_mapping*/);
|
||||
|
||||
|
||||
#endif /* _MIXING_H_ */
|
||||
|
@ -234,12 +234,14 @@ void vgmstream_mixing_autodownmix(VGMSTREAM *vgmstream, int max_channels) {
|
||||
if (max_channels <= 0)
|
||||
return;
|
||||
|
||||
/* guess mixing the best we can */
|
||||
//todo: could use standard downmixing for known max_channels <> vgmstream->channels combos:
|
||||
// https://www.audiokinetic.com/library/edge/?source=Help&id=downmix_tables#tbl_mono
|
||||
// https://www.audiokinetic.com/library/edge/?source=Help&id=standard_configurations
|
||||
|
||||
mixing_macro_layer(vgmstream, max_channels, 0, 'e');
|
||||
/* guess mixing the best we can, using standard downmixing if possible
|
||||
* (without mapping we can't be sure if format is using a standard layout) */
|
||||
if (vgmstream->channel_layout && max_channels <= 2) {
|
||||
mixing_macro_downmix(vgmstream, max_channels);
|
||||
}
|
||||
else {
|
||||
mixing_macro_layer(vgmstream, max_channels, 0, 'e');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -890,7 +890,7 @@ size_t read_string(char * buf, size_t maxsize, off_t offset, STREAMFILE *streamF
|
||||
if (buf) buf[pos] = '\0';
|
||||
return maxsize;
|
||||
}
|
||||
if (c < 0x20 || c > 0xA5)
|
||||
if (c < 0x20 || (uint8_t)c > 0xA5)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_ps2_mic,
|
||||
init_vgmstream_ngc_dsp_std_int,
|
||||
init_vgmstream_vag,
|
||||
init_vgmstream_psx_gms,
|
||||
init_vgmstream_seb,
|
||||
init_vgmstream_ps2_ild,
|
||||
init_vgmstream_ps2_pnb,
|
||||
init_vgmstream_xbox_wavm,
|
||||
@ -471,6 +471,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_psf_single,
|
||||
init_vgmstream_psf_segmented,
|
||||
init_vgmstream_dsp_itl,
|
||||
init_vgmstream_sch,
|
||||
init_vgmstream_ima,
|
||||
|
||||
/* 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 */
|
||||
|
@ -349,7 +349,7 @@ typedef enum {
|
||||
meta_PS2_VAGi, /* VAGi Interleaved File */
|
||||
meta_PS2_VAGp, /* VAGp Mono File */
|
||||
meta_PS2_pGAV, /* VAGp with Little Endian Header */
|
||||
meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */
|
||||
meta_SEB,
|
||||
meta_STR_WAV, /* Blitz Games STR+WAV files */
|
||||
meta_PS2_ILD, /* ILD File */
|
||||
meta_PS2_PNB, /* PsychoNauts Bgm File */
|
||||
@ -714,6 +714,7 @@ typedef enum {
|
||||
meta_XAVS,
|
||||
meta_PSF,
|
||||
meta_DSP_ITL_i,
|
||||
meta_IMA,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user