Merge pull request #595 from bnnm/wwise-winamp

wwise winamp
This commit is contained in:
bnnm 2020-04-12 20:43:57 +02:00 committed by GitHub
commit 14dc85664d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 214 additions and 108 deletions

View File

@ -5,7 +5,9 @@
### defs
# currently aimed to WIN32 builds but vgmstream_cli should work for others (or use autotools instead)
export TARGET_OS = $(OS)
#for Win builds with vgmstream123
LIBAO_DLL_PATH = ../libao/bin
### tools
RMF = rm -f
@ -49,6 +51,8 @@ export RMF SHELL CC AR STRIP WINDRES DLLTOOL
buildrelease: clean bin
buildrelease-ex: clean bin-ex
buildfullrelease: clean sourceball bin
sourceball:
@ -63,6 +67,10 @@ sourceball:
bin mingwbin: vgmstream_cli winamp xmplay
zip -FS -j "vgmstream-`./version.sh`-test.zip" COPYING README.md cli/test.exe winamp/in_vgmstream.dll xmplay/xmp-vgmstream.dll ext_libs/*.dll
#separate since vgmstream123 is kinda untested
bin-ex mingwbin-ex: vgmstream_cli winamp xmplay vgmstream123
zip -FS -j "vgmstream-`./version.sh`-test.zip" COPYING README.md cli/test.exe cli/vgmstream123.exe winamp/in_vgmstream.dll xmplay/xmp-vgmstream.dll ext_libs/*.dll $(LIBAO_DLL_PATH)/*.dll
vgmstream_cli mingw_test:
$(MAKE) -C cli vgmstream_cli

View File

@ -1,3 +1,4 @@
#include <math.h>
#include "coding.h"
/* Relic Codec decoder, a fairly simple mono-interleave DCT-based codec.

View File

@ -6,10 +6,10 @@
VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
off_t subfile_offset, didx_offset, data_offset, offset;
size_t subfile_size, didx_size;
uint32_t subfile_id;
int big_endian;
off_t subfile_offset, base_offset = 0;
size_t subfile_size;
uint32_t subfile_id, header_id;
int big_endian, version, is_dummy = 0;
uint32_t (*read_u32)(off_t,STREAMFILE*);
int total_subsongs, target_subsong = sf->stream_index;
@ -17,47 +17,115 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
/* checks */
if (!check_extensions(sf,"bnk"))
goto fail;
if (read_u32be(0x00, sf) != 0x424B4844) /* "BKHD" */
if (read_u32be(0x00, sf) == 0x414B424B) /* "AKBK" [Shadowrun (X360)] */
base_offset = 0x0c;
if (read_u32be(base_offset + 0x00, sf) != 0x424B4844) /* "BKHD" */
goto fail;
big_endian = guess_endianness32bit(0x04, sf);
big_endian = guess_endianness32bit(base_offset + 0x04, sf);
read_u32 = big_endian ? read_u32be : read_u32le;
/* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well
* as other chunks, and may have a DIDX index to memory .wem in DATA.
* as other chunks, and may have a DATA/DIDX index to memory .wem in DATA.
* We support the internal .wem mainly for quick tests, as the HIRC is
* complex and better handled with TXTP (some info from Nicknine's script) */
* complex and better handled with TXTP (some info from Nicknine's script).
* unlike RIFF, first chunk follows chunk rules */
/* unlike RIFF first chunk follows chunk rules */
if (!find_chunk(sf, 0x44494458, 0x00,0, &didx_offset, &didx_size, big_endian, 0)) /* "DIDX" */
goto fail;
if (!find_chunk(sf, 0x44415441, 0x00,0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
goto fail;
version = read_u32(base_offset + 0x08, sf);
if (version == 0 || version == 1) { /* early games */
version = read_u32(base_offset + 0x10, sf);
}
total_subsongs = didx_size / 0x0c;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
if (version <= 26) {
off_t data_offset, data_start, offset;
if (!find_chunk(sf, 0x44415441, base_offset, 0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
goto fail;
offset = didx_offset + (target_subsong - 1) * 0x0c;
subfile_id = read_u32(offset + 0x00, sf);
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
subfile_size = read_u32(offset + 0x08, sf);
/* index:
* 00: entries
* 04: null
* 08: entries size
* 0c: padding size after entries
* 10: data size
* 14: size?
* 18: data start
* 1c: data size
* per entry:
* 00: always -1
* 04: always 0
* 08: index number or -1
* 0c: 5 or -1?
* 0c: 5 or -1?
* 0c: 5 or -1?
* 10: stream offset (from data start) or -1 if none
* 14: stream size or 0 if none
*/
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wem");
if (!temp_sf) goto fail;
total_subsongs = read_u32(data_offset + 0x00, sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
subfile_id = read_u32(0x00, temp_sf);
if (subfile_id == 0x52494646 || subfile_id == 0x52494658) { /* "RIFF" / "RIFX" */
vgmstream = init_vgmstream_wwise(temp_sf);
if (!vgmstream) goto fail;
data_start = read_u32(data_offset + 0x18, sf);
offset = data_offset + 0x20 + (target_subsong - 1) * 0x18;
subfile_id = read_u32(offset + 0x08, sf);
subfile_offset = read_u32(offset + 0x10, sf) + data_offset + 0x20 + data_start;
subfile_size = read_u32(offset + 0x14, sf);
}
else {
vgmstream = init_vgmstream_bkhd_fx(temp_sf);
if (!vgmstream) goto fail;
off_t didx_offset, data_offset, offset;
size_t didx_size;
if (!find_chunk(sf, 0x44494458, 0x00,0, &didx_offset, &didx_size, big_endian, 0)) /* "DIDX" */
goto fail;
if (!find_chunk(sf, 0x44415441, 0x00,0, &data_offset, NULL, big_endian, 0)) /* "DATA" */
goto fail;
total_subsongs = didx_size / 0x0c;
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
offset = didx_offset + (target_subsong - 1) * 0x0c;
subfile_id = read_u32(offset + 0x00, sf);
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
subfile_size = read_u32(offset + 0x08, sf);
}
/* some indexes don't have that, but for now leave a dummy song for easier HIRC mapping */
if (subfile_offset <= 0 || subfile_size <= 0) {
//;VGM_LOG("BKHD: dummy entry");
temp_sf = setup_subfile_streamfile(sf, 0x00, 0x10, "raw");
if (!temp_sf) goto fail;
//todo make some better silent entry
vgmstream = init_vgmstream_raw_pcm(temp_sf);
if (!vgmstream) goto fail;
is_dummy = 1;
}
else {
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "wem");
if (!temp_sf) goto fail;
header_id = read_u32be(0x00, temp_sf);
if (header_id == 0x52494646 || header_id == 0x52494658) { /* "RIFF" / "RIFX" */
vgmstream = init_vgmstream_wwise(temp_sf);
if (!vgmstream) goto fail;
}
else {
vgmstream = init_vgmstream_bkhd_fx(temp_sf);
if (!vgmstream) goto fail;
}
}
vgmstream->num_streams = total_subsongs;
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id);
if (is_dummy)
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s", "dummy");
else if (subfile_id != 0xFFFFFFFF)
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id);
close_streamfile(temp_sf);
return vgmstream;
@ -69,7 +137,7 @@ fail:
}
/* BKHD mini format, probably from a generator plugin [Borderlands 2 (X360)] */
/* BKHD mini format, probably from a FX generator plugin [Borderlands 2 (X360)] */
VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset, data_size;
@ -93,8 +161,7 @@ VGMSTREAM* init_vgmstream_bkhd_fx(STREAMFILE* sf) {
/* 0x14/18: some float? */
entries = read_u32(0x1c, sf);
/* 0x20 data size / 0x10 */
if (read_u8(0x24, sf) != 4) /* bps */
goto fail;
/* 0x24 usually 4, sometimes higher values? */
/* 0x30: unknown table of 16b that goes up and down */
start_offset = 0x30 + align_size_to_block(entries * 0x02, 0x10);

View File

@ -6,7 +6,7 @@
#ifdef HCA_BRUTEFORCE
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey);
#endif
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode, uint16_t subkey);
static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey);
/* CRI HCA - streamed audio from CRI ADX2/Atom middleware */
@ -17,7 +17,6 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
VGMSTREAM * vgmstream = NULL;
hca_codec_data * hca_data = NULL;
unsigned long long keycode = 0;
/* checks */
@ -32,6 +31,7 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
/* find decryption key in external file or preloaded list */
if (hca_data->info.encryptionEnabled) {
uint64_t keycode = 0;
uint8_t keybuf[0x08+0x02];
size_t keysize;
@ -56,7 +56,7 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
find_hca_key(hca_data, &keycode, subkey);
}
clHCA_SetKey(hca_data->handle, keycode); //maybe should be done through hca_decoder.c?
clHCA_SetKey(hca_data->handle, (unsigned long long)keycode); //maybe should be done through hca_decoder.c?
}
@ -139,25 +139,25 @@ static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t su
}
/* try to find the decryption key from a list. */
static void find_hca_key(hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey) {
static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey) {
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
int best_score = -1;
int i,j;
*out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
*p_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
for (i = 0; i < keys_length; i++) {
uint64_t key = hcakey_list[i].key;
size_t subkeys_size = hcakey_list[i].subkeys_size;
const uint16_t *subkeys = hcakey_list[i].subkeys;
test_key(hca_data, key, subkey, &best_score, out_keycode);
test_key(hca_data, key, subkey, &best_score, p_keycode);
if (best_score == 1)
goto done;
if (subkeys_size > 0 && subkey == 0) {
for (j = 0; j < subkeys_size; j++) {
test_key(hca_data, key, subkeys[j], &best_score, out_keycode);
test_key(hca_data, key, subkeys[j], &best_score, p_keycode);
if (best_score == 1)
goto done;
}
@ -166,7 +166,7 @@ static void find_hca_key(hca_codec_data* hca_data, unsigned long long* out_keyco
done:
VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
(uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
}

View File

@ -20,7 +20,7 @@ VGMSTREAM * init_vgmstream_rsd(STREAMFILE *streamFile) {
loop_flag = 0;
codec = read_32bitBE(0x04,streamFile);
codec = (uint32_t)read_32bitBE(0x04,streamFile);
channel_count = read_32bitLE(0x08, streamFile);
/* 0x0c: always 16? */
sample_rate = read_32bitLE(0x10, streamFile);

View File

@ -47,20 +47,22 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
/* basic checks */
/* .wem (Wwise Encoded Media) is "newer Wwise", used after the 2011.2 SDK (~july)
* .wav (ex. Shadowrun X360) and .ogg (ex. KOF XII X360), .xma (ex. Tron Evolution X360) are used in older Wwise */
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma")) goto fail;
if ((read_32bitBE(0x00,streamFile) != 0x52494646) && /* "RIFF" (LE) */
(read_32bitBE(0x00,streamFile) != 0x52494658)) /* "RIFX" (BE) */
goto fail;
if ((read_32bitBE(0x08,streamFile) != 0x57415645) && /* "WAVE" */
(read_32bitBE(0x08,streamFile) != 0x58574D41)) /* "XWMA" */
/* checks */
/* .wem: newer "Wwise Encoded Media" used after the 2011.2 SDK (~july 2011)
* .wav: older ADPCM files [Punch Out!! (Wii)]
* .xma: older XMA files [Too Human (X360), Tron Evolution (X360)]
* .ogg: older Vorbis files [The King of Fighters XII (X360)] */
if (!check_extensions(streamFile,"wem,wav,lwav,ogg,logg,xma"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x52494646 && /* "RIFF" (LE) */
read_32bitBE(0x00,streamFile) != 0x52494658) /* "RIFX" (BE) */
goto fail;
if (read_32bitBE(0x08,streamFile) != 0x57415645 && /* "WAVE" */
read_32bitBE(0x08,streamFile) != 0x58574D41) /* "XWMA" */
goto fail;
ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658;/* RIFX */
ww.big_endian = read_32bitBE(0x00,streamFile) == 0x52494658; /* RIFX */
if (ww.big_endian) { /* Wwise honors machine's endianness (PC=RIFF, X360=RIFX --unlike XMA) */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
@ -94,30 +96,36 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */
if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */
goto fail; /* parsed elsewhere */
/* Wwise doesn't use "fact", though */
}
/* Wwise doesn't use "fact", though */
}
/* parse format (roughly spec-compliant but some massaging is needed) */
{
off_t loop_offset;
size_t loop_size;
/* find basic chunks */
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) goto fail; /*"fmt "*/
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) goto fail; /*"data"*/
if (read_32bitBE(0x0c, streamFile) == 0x584D4132) { /* "XMA2" with no "fmt" [Too Human (X360)] */
ww.format = 0x0165; /* signal for below */
}
else {
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &ww.fmt_offset,&ww.fmt_size, ww.big_endian, 0)) /* "fmt " */
goto fail;
if (ww.fmt_size < 0x12)
goto fail;
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
}
/* base fmt */
if (ww.fmt_size < 0x12) goto fail;
ww.format = (uint16_t)read_16bit(ww.fmt_offset+0x00,streamFile);
if (ww.format == 0x0165) { /* pseudo-XMA2WAVEFORMAT (always "fmt"+"XMA2", unlike .xma that may only have "XMA2") */
if (!find_chunk(streamFile, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0))
if (ww.format == 0x0165) {
/* pseudo-XMA2WAVEFORMAT ("fmt"+"XMA2" or just "XMA2) */
if (!find_chunk(streamFile, 0x584D4132,first_offset,0, &ww.chunk_offset,NULL, ww.big_endian, 0)) /* "XMA2" */
goto fail;
xma2_parse_xma2_chunk(streamFile, ww.chunk_offset,&ww.channels,&ww.sample_rate, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample);
}
else { /* pseudo-WAVEFORMATEX */
else {
/* pseudo-WAVEFORMATEX */
ww.channels = read_16bit(ww.fmt_offset+0x02,streamFile);
ww.sample_rate = read_32bit(ww.fmt_offset+0x04,streamFile);
ww.average_bps = read_32bit(ww.fmt_offset+0x08,streamFile);/* bytes per sec */
@ -128,7 +136,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
if (ww.extra_size >= 0x06) { /* always present (actual RIFFs only have it in WAVEFORMATEXTENSIBLE) */
/* mostly WAVEFORMATEXTENSIBLE's bitmask (see AkSpeakerConfig.h) */
ww.channel_layout = read_32bit(ww.fmt_offset+0x14,streamFile);
/* latest games have a pseudo-format instead to handle more cases:
/* later games (+2018?) have a pseudo-format instead to handle more cases:
* - 8b: uNumChannels
* - 4b: eConfigType (0=none, 1=standard, 2=ambisonic)
* - 19b: uChannelMask */
@ -138,40 +146,42 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
}
}
/* find loop info */
if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX */
/* find loop info ("XMA2" chunks already read them) */
if (ww.format == 0x0166) { /* XMA2WAVEFORMATEX in fmt */
ww.chunk_offset = ww.fmt_offset;
xma2_parse_fmt_chunk_extra(streamFile, ww.chunk_offset, &ww.loop_flag, &ww.num_samples, &ww.loop_start_sample, &ww.loop_end_sample, ww.big_endian);
}
else if (find_chunk(streamFile, 0x736D706C,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"smpl", common */
else if (find_chunk(streamFile, 0x736D706C,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /* "smpl", common */
if (loop_size >= 0x34
&& read_32bit(loop_offset+0x1c, streamFile)==1 /*loop count*/
&& read_32bit(loop_offset+0x1c, streamFile)==1 /* loop count */
&& read_32bit(loop_offset+0x24+4, streamFile)==0) {
ww.loop_flag = 1;
ww.loop_start_sample = read_32bit(loop_offset+0x24+0x8, streamFile);
ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc,streamFile);
//todo fix repeat looping
ww.loop_end_sample = read_32bit(loop_offset+0x24+0xc, streamFile) + 1; /* like standard RIFF */
}
}
//else if (find_chunk(streamFile, 0x4C495354,first_offset,0, &loop_offset,&loop_size, ww.big_endian, 0)) { /*"LIST", common */
// /* usually contains "cue"s with sample positions for events (ex. Platinum Games) but no real looping info */
//}
/* other Wwise specific: */
//"JUNK": optional padding for aligment (0-size JUNK exists too)
//"akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?)
/* other Wwise specific chunks:
* "JUNK": optional padding for aligment (0-size JUNK exists too)
* "akd ": seem to store extra info for Wwise editor (wave peaks/loudness/HDR envelope?)
*/
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &ww.data_offset,&ww.data_size, ww.big_endian, 0)) /* "data" */
goto fail;
}
/* format to codec */
switch(ww.format) {
case 0x0001: ww.codec = PCM; break; /* older Wwise */
case 0x0002: ww.codec = IMA; break; /* newer Wwise (conflicts with MSADPCM, probably means "platform's ADPCM") */
//case 0x0011: ww.codec = IMA; break; /* older Wwise (used?) */
case 0x0069: ww.codec = IMA; break; /* older Wwise (Spiderman Web of Shadows X360, LotR Conquest PC) */
case 0x0069: ww.codec = IMA; break; /* older Wwise [Spiderman Web of Shadows (X360), LotR Conquest (PC)] */
case 0x0161: ww.codec = XWMA; break; /* WMAv2 */
case 0x0162: ww.codec = XWMA; break; /* WMAPro */
case 0x0165: ww.codec = XMA2; break; /* always with the "XMA2" chunk, Wwise doesn't use XMA1 */
case 0x0166: ww.codec = XMA2; break;
case 0x0165: ww.codec = XMA2; break; /* XMA2-chunk XMA (Wwise doesn't use XMA1) */
case 0x0166: ww.codec = XMA2; break; /* fmt-chunk XMA */
case 0xAAC0: ww.codec = AAC; break;
case 0xFFF0: ww.codec = DSP; break;
case 0xFFFB: ww.codec = HEVAG; break;
@ -213,7 +223,7 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
goto fail;
}
if (ww.codec == PCM || ww.codec == IMA || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
if (ww.codec == PCM || ww.codec == IMA || ww.codec == DSP || ww.codec == VORBIS || ww.codec == XMA2 || ww.codec == OPUSNX || ww.codec == OPUS) {
ww.truncated = 1; /* only seen those, probably all exist */
} else {
VGM_LOG("WWISE: wrong size, maybe truncated\n");
@ -264,7 +274,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = ww.block_align / ww.channels;
vgmstream->codec_endian = ww.big_endian;
/* enough to get real samples */
if (ww.truncated) {
ww.data_size = ww.file_size - ww.data_offset;
}
@ -286,12 +295,12 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
if (ww.block_align != 0 || ww.bits_per_sample != 0) goto fail; /* always 0 for Worbis */
/* autodetect format (fields are mostly common, see the end of the file) */
if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /*"vorb"*/
if (find_chunk(streamFile, 0x766F7262,first_offset,0, &vorb_offset,&vorb_size, ww.big_endian, 0)) { /* "vorb" */
/* older Wwise (~<2012) */
switch(vorb_size) {
case 0x2C: /* earliest (~2009), [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)?] */
case 0x28: /* early (~2009) [The Lord of the Rings: Conquest (PC)] */
case 0x2C: /* earliest (~2009) [The Lord of the Rings: Conquest (PC)] */
case 0x28: /* early (~2009) [UFC Undisputed 2009 (PS3), some EVE Online Apocrypha (PC)] */
data_offsets = 0x18;
block_offsets = 0; /* no need, full headers are present */
cfg.header_type = WWV_TYPE_8;
@ -299,8 +308,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
cfg.setup_type = WWV_HEADER_TRIAD;
break;
case 0x34: /* common (2010~2011) */
case 0x32: /* very rare (mid 2011) [Saints Row the 3rd (PC)] */
case 0x34: /* common (2010~2011) [The King of Fighters XII (PS3), Assassin's Creed II (X360)] */
case 0x32: /* rare (mid 2011) [Saints Row the 3rd (PC)] */
data_offsets = 0x18;
block_offsets = 0x30;
cfg.header_type = WWV_TYPE_6;
@ -377,8 +386,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
cfg.setup_type = is_wem ? WWV_AOTUV603_CODEBOOKS : WWV_EXTERNAL_CODEBOOKS; /* aoTuV came along .wem */
break;
//case 0x2a: /* Rocksmith 2011 (X360)? */
//non mod packets? TYPE_06? (possibly detectable by checking setup's granule, should be 0)
default:
VGM_LOG("WWISE: unknown extra size 0x%x\n", vorb_size);
goto fail;
@ -449,6 +456,11 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
goto fail;
}
if (ww.truncated) {
ww.data_size = ww.file_size - ww.data_offset;
vgmstream->num_samples = dsp_bytes_to_samples(ww.data_size, ww.channels);
}
/* for some reason all(?) DSP .wem do full loops (even mono/jingles/etc) but
* several tracks do loop like this, so disable it for short-ish tracks */
if (ww.loop_flag && vgmstream->loop_start_sample == 0 &&
@ -457,7 +469,6 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
}
/* get coefs and default history */
dsp_read_coefs(vgmstream,streamFile,wiih_offset, 0x2e, ww.big_endian);
for (i=0; i < ww.channels; i++) {

View File

@ -978,29 +978,46 @@ static void apply_config(VGMSTREAM * vgmstream, winamp_song_config *current) {
static int winampGetExtendedFileInfo_common(in_char* filename, char *metadata, char* ret, int retlen);
static double get_album_gain_volume(const in_char *fn) {
char replaygain_gain[64], replaygain_peak[64];
char replaygain[64];
double gain = 0.0;
int had_replaygain = 0;
if (settings.gain_type != REPLAYGAIN_NONE) {
if (settings.gain_type == REPLAYGAIN_ALBUM && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_gain", replaygain_gain, sizeof(replaygain_gain))) {
gain = atof(replaygain_gain);
had_replaygain = 1;
}
if (!had_replaygain && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_gain", replaygain_gain, sizeof(replaygain_gain))) {
gain = atof(replaygain_gain);
had_replaygain = 1;
}
if (had_replaygain) {
double vol = pow(10.0, gain / 20.0), peak = 1.0;
if (settings.clip_type == REPLAYGAIN_ALBUM && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_peak", replaygain_peak, sizeof(replaygain_peak))) {
peak = atof(replaygain_peak);
}
else if (settings.clip_type != REPLAYGAIN_NONE && winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_peak", replaygain_peak, sizeof(replaygain_peak))) {
peak = atof(replaygain_peak);
}
return peak != 1.0 ? min(vol, 1.0 / peak) : vol;
}
if (settings.gain_type == REPLAYGAIN_NONE)
return 1.0;
replaygain[0] = '\0'; /* reset each time to make sure we read actual tags */
if (settings.gain_type == REPLAYGAIN_ALBUM
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_gain", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') {
gain = atof(replaygain);
had_replaygain = 1;
}
replaygain[0] = '\0';
if (!had_replaygain
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_gain", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') {
gain = atof(replaygain);
had_replaygain = 1;
}
if (had_replaygain) {
double vol = pow(10.0, gain / 20.0);
double peak = 1.0;
replaygain[0] = '\0';
if (settings.clip_type == REPLAYGAIN_ALBUM
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_album_peak", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') {
peak = atof(replaygain);
}
else if (settings.clip_type != REPLAYGAIN_NONE
&& winampGetExtendedFileInfo_common((in_char *)fn, "replaygain_track_peak", replaygain, sizeof(replaygain))
&& replaygain[0] != '\0') {
peak = atof(replaygain);
}
return peak != 1.0 ? min(vol, 1.0 / peak) : vol;
}
return 1.0;
}
@ -1648,6 +1665,8 @@ static int winampGetExtendedFileInfo_common(in_char* filename, char *metadata, c
return 1;
fail:
//TODO: is this always needed for Winamp to use replaygain?
//strcpy(ret, "1.0"); //should set some default value?
return strcasecmp(metadata, "replaygain_track_gain") == 0 ? 1 : 0;
}