diff --git a/fb2k/in_vgmstream.cpp b/fb2k/in_vgmstream.cpp index 4ad0ae2e..3937d1bf 100644 --- a/fb2k/in_vgmstream.cpp +++ b/fb2k/in_vgmstream.cpp @@ -359,6 +359,7 @@ bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension if(!stricmp_utf8(p_extension,"logg")) return 1; if(!stricmp_utf8(p_extension,"lpcm")) return 1; if(!stricmp_utf8(p_extension,"lps")) return 1; + if(!stricmp_utf8(p_extension,"lsf")) return 1; if(!stricmp_utf8(p_extension,"lwav")) return 1; if(!stricmp_utf8(p_extension,"matx")) return 1; @@ -657,6 +658,7 @@ DECLARE_MULTIPLE_FILE_TYPE("LEG Audio File (*.LEG)", leg); DECLARE_MULTIPLE_FILE_TYPE("LOGG Audio File (*.LOGG)", logg); DECLARE_MULTIPLE_FILE_TYPE("LPCM Audio File (*.LPCM)", lpcm); DECLARE_MULTIPLE_FILE_TYPE("LPS Audio File (*.LPS)", lps); +DECLARE_MULTIPLE_FILE_TYPE("LSF Audio File (*.LSF)", lsf); DECLARE_MULTIPLE_FILE_TYPE("LWAV Audio File (*.LWAV)", lwav); DECLARE_MULTIPLE_FILE_TYPE("MATX Audio File (*.MATX)", matx); diff --git a/readme.txt b/readme.txt index 7d6f1c7b..290fffe7 100644 --- a/readme.txt +++ b/readme.txt @@ -240,11 +240,12 @@ etc: - .ahx (MPEG-2 Layer II) - .aix (CRI ADX ADPCM) - .baf (Blur ADPCM) +- .bgw (FFXI PS-like ADPCM) - .bnsf (G.722.1) - .caf (Apple IMA4 ADPCM) -- .bgw (FFXI PS-like ADPCM) - .de2 (MS ADPCM) - .kcey (EACS IMA ADPCM) +- .lsf (LSF ADPCM) - .mwv (Level-5 0x555 ADPCM) - .ogg, .logg (Ogg Vorbis) - .p3d (Radical ADPCM) diff --git a/src/Makefile b/src/Makefile index 1827be92..7db8c557 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,7 +19,8 @@ CODING_OBJS=coding/adx_decoder.o \ coding/nds_procyon_decoder.o \ coding/l5_555_decoder.o \ coding/SASSC_decoder.o \ - coding/g7221_decoder.o + coding/g7221_decoder.o \ + coding/lsf_decoder.o LAYOUT_OBJS=layout/ast_blocked.o \ layout/blocked.o \ @@ -270,7 +271,8 @@ META_OBJS=meta/adx_header.o \ meta/ps2_mtaf.o \ meta/x360_tra.o \ meta/ps2_iab.o \ - meta/ps2_strlr.o + meta/ps2_strlr.o \ + meta/lsf.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) diff --git a/src/coding/Makefile.unix.am b/src/coding/Makefile.unix.am index 0ae36c00..fa8a171d 100644 --- a/src/coding/Makefile.unix.am +++ b/src/coding/Makefile.unix.am @@ -27,5 +27,6 @@ libcoding_la_SOURCES += nds_procyon_decoder.c libcoding_la_SOURCES += l5_555_decoder.c libcoding_la_SOURCES += SASSC_decoder.c libcoding_la_SOURCES += g7221_decoder.c +libcoding_la_SOURCES += lsf_decoder.c EXTRA_DIST = coding.h g72x_state.h diff --git a/src/coding/coding.h b/src/coding/coding.h index 8cbed5bc..d028c70d 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -110,4 +110,6 @@ void decode_l5_555(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin void decode_SASSC(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + #endif diff --git a/src/coding/lsf_decoder.c b/src/coding/lsf_decoder.c new file mode 100644 index 00000000..3bed7901 --- /dev/null +++ b/src/coding/lsf_decoder.c @@ -0,0 +1,52 @@ +#include "coding.h" +#include "../util.h" + +/* lsf ADPCM, as seen in Fastlane Street Racing */ + +static const short lsf_coefs[5][2] = { + {0x73, -0x34}, + {0, 0}, + {0x62, -0x37}, + {0x3C, 0}, + {0x7A, -0x3c} +}; + +void decode_lsf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i=first_sample; + int32_t sample_count; + const int bytes_per_frame = 0x1c; + const int samples_per_frame = (bytes_per_frame-1)*2; + + int framesin = first_sample/samples_per_frame; + + uint8_t q = 0xFF - read_8bit(framesin*bytes_per_frame + stream->offset,stream->streamfile); + int scale = (q&0xF0)>>4; + int coef_idx = q&0x0F; + int32_t hist1 = stream->adpcm_history1_16; + int32_t hist2 = stream->adpcm_history2_16; + + first_sample = first_sample%samples_per_frame; + + for (i=first_sample,sample_count=0; ioffset+1+i/2,stream->streamfile); + + long prediction = + (hist1 * lsf_coefs[coef_idx][0] + + hist2 * lsf_coefs[coef_idx][1]) / 0x40; + + prediction += (i&1? + get_high_nibble_signed(sample_byte): + get_low_nibble_signed(sample_byte) + ) * (1 << (12-scale)); + + prediction = clamp16(prediction); + + hist2 = hist1; + hist1 = prediction; + + outbuf[sample_count] = prediction; + } + + stream->adpcm_history1_16 = hist1; + stream->adpcm_history2_16 = hist2; +} diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 8c798a4e..2e0c64d4 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -380,6 +380,10 @@ RelativePath=".\meta\kraw.c" > + + @@ -1134,6 +1138,10 @@ RelativePath=".\coding\l5_555_decoder.c" > + + diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index d7989508..d6fbfed2 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -219,5 +219,6 @@ libmeta_la_SOURCES += ps2_mtaf.c libmeta_la_SOURCES += x360_tra.c libmeta_la_SOURCES += ps2_iab.c libmeta_la_SOURCES += ps2_strlr.c +libmeta_la_SOURCES += lsf.c EXTRA_DIST = meta.h diff --git a/src/meta/lsf.c b/src/meta/lsf.c new file mode 100644 index 00000000..4d4cd306 --- /dev/null +++ b/src/meta/lsf.c @@ -0,0 +1,59 @@ +#include "meta.h" +#include "../util.h" + +/* .lsf - Fastlane Street Racing (iPhone) */ +/* "!n1nj4n" */ + +VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[260]; + + size_t file_size; + off_t start_offset; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("lsf",filename_extension(filename))) goto fail; + + /* check header */ + if (read_32bitBE(0x0, streamFile) != 0x216E316E || // "!n1n" + read_32bitBE(0x4, streamFile) != 0x6A346E00) // "j4n\0" + goto fail; + + /* check size */ + file_size = get_streamfile_size(streamFile); + if (read_32bitLE(0xC, streamFile) + 0x10 != file_size) + goto fail; + + start_offset = 0x10; + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(1,0); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->num_samples = (file_size-0x10)/0x1c*0x1b*2; + vgmstream->sample_rate = read_32bitLE(0x8, streamFile); + + vgmstream->coding_type = coding_LSF; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_LSF_N1NJ4N; + + /* open the file for reading */ + { + vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + + if (!vgmstream->ch[0].streamfile) goto fail; + + vgmstream->ch[0].channel_start_offset= + vgmstream->ch[0].offset=start_offset; + } + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} + diff --git a/src/meta/meta.h b/src/meta/meta.h index f7c94808..e9f7d087 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -553,4 +553,6 @@ VGMSTREAM * init_vgmstream_ps2_iab(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_ps2_strlr(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile); + #endif diff --git a/src/vgmstream.c b/src/vgmstream.c index 10120541..dc29965c 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -298,6 +298,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_x360_tra, init_vgmstream_ps2_iab, init_vgmstream_ps2_strlr, + init_vgmstream_lsf_n1nj4n, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -857,6 +858,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_G7221: return 16000/50; #endif + case coding_LSF: + return 54; default: return 0; } @@ -944,6 +947,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { return 34; case coding_BAF_ADPCM: return 33; + case coding_LSF: + return 28; #ifdef VGM_USE_G7221 case coding_G7221C: case coding_G7221: @@ -1376,6 +1381,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do); } + break; + case coding_LSF: + for (chan=0;chanchannels;chan++) { + decode_lsf(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } break; } } @@ -1745,11 +1757,14 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE,"Procyon Studio Digital Sound Elements NDS 4-bit APDCM"); break; case coding_L5_555: - snprintf(temp,TEMPSIZE,"Level-5 0x555 ADPCM"); + snprintf(temp,TEMPSIZE,"Level-5 0x555 4-bit ADPCM"); break; case coding_SASSC: snprintf(temp,TEMPSIZE,"Activision / EXAKT SASSC 8-bit DPCM"); break; + case coding_LSF: + snprintf(temp,TEMPSIZE,"lsf 4-bit ADPCM"); + break; default: snprintf(temp,TEMPSIZE,"CANNOT DECODE"); } @@ -2761,6 +2776,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { break; case meta_PS2_STRLR: snprintf(temp,TEMPSIZE,"STR L/R header"); + break; + case meta_LSF_N1NJ4N: + snprintf(temp,TEMPSIZE,".lsf !n1nj4n header"); break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); diff --git a/src/vgmstream.h b/src/vgmstream.h index 2e405d97..30e886b9 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -122,6 +122,7 @@ typedef enum { coding_L5_555, /* Level-5 0x555 */ coding_SASSC, /* Activision EXAKT SASSC DPCM */ coding_PCM16LE_XOR_int, /* sample-level xor */ + coding_LSF, /* lsf ADPCM */ } coding_t; /* The layout type specifies how the sound data is laid out in the file */ @@ -508,6 +509,7 @@ typedef enum { meta_PS2_VGS, // Princess Soft PS2 games meta_PS2_IAB, // Ueki no Housoku - Taosu ze Robert Juudan!! (PS2) meta_PS2_STRLR, + meta_LSF_N1NJ4N, /* .lsf n1nj4n Fastlane Street Racing (iPhone) */ } meta_t; typedef struct { diff --git a/unix/data.c b/unix/data.c index 5b6a234a..2960f5e2 100644 --- a/unix/data.c +++ b/unix/data.c @@ -116,6 +116,7 @@ gchar *vgmstream_exts [] = { "logg", "lpcm", "lps", + "lsf", "lwav", "matx", diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c index 096c13b2..7558b183 100644 --- a/winamp/in_vgmstream.c +++ b/winamp/in_vgmstream.c @@ -182,6 +182,7 @@ char * extension_list[] = { "logg\0LOGG Audio File (*.LOGG)\0", "lpcm\0LPCM Audio File (*.LPCM)\0", "lps\0LPS Audio File (*.LPS)\0", + "lsf\0LSF Audio File (*.LSF)\0", "lwav\0LWAV Audio File (*.LWAV)\0", "matx\0MATX Audio File (*.MATX)\0",