diff --git a/fb2k/in_vgmstream.cpp b/fb2k/in_vgmstream.cpp index 2b0cb411..b9bde3d1 100644 --- a/fb2k/in_vgmstream.cpp +++ b/fb2k/in_vgmstream.cpp @@ -367,6 +367,7 @@ bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension if(!stricmp_utf8(p_extension,"omu")) return 1; if(!stricmp_utf8(p_extension,"p2bt")) return 1; + if(!stricmp_utf8(p_extension,"p3d")) return 1; if(!stricmp_utf8(p_extension,"pcm")) return 1; if(!stricmp_utf8(p_extension,"pdt")) return 1; if(!stricmp_utf8(p_extension,"pnb")) return 1; @@ -630,6 +631,7 @@ DECLARE_MULTIPLE_FILE_TYPE("NWA Audio File (*.NWA)", nwa); DECLARE_MULTIPLE_FILE_TYPE("OMU Audio File (*.OMU)", omu); DECLARE_MULTIPLE_FILE_TYPE("P2BT Audio File (*.P2BT)", p2bt); +DECLARE_MULTIPLE_FILE_TYPE("P3D Audio File (*.P3D)", p3d); DECLARE_MULTIPLE_FILE_TYPE("PCM Audio File (*.PCM)", pcm); DECLARE_MULTIPLE_FILE_TYPE("PDT Audio File (*.PDT)", pdt); DECLARE_MULTIPLE_FILE_TYPE("PNB Audio File (*.PNB)", pnb); diff --git a/readme.txt b/readme.txt index faff3ccb..e732d760 100644 --- a/readme.txt +++ b/readme.txt @@ -212,7 +212,7 @@ multi: - .psw (PSX ADPCM, GC DSP ADPCM) - .rwar, .rwav (GC DSP ADPCM, 8/16 bit PCM) - .rwsd (GC DSP ADPCM, 8/16 bit PCM) -- .rsd (PSX ADPCM, 16 bit PCM, GC DSP ADPCM, Xbox IMA ADPCM) +- .rsd (PSX ADPCM, 16 bit PCM, GC DSP ADPCM, Xbox IMA ADPCM, Radical ADPCM) - .rrds (NDS IMA ADPCM) - .sad (GC DSP ADPCM, NDS IMA ADPCM, Procyon Studios NDS ADPCM) - .sng, .asf, .str, .eam (EA/XA ADPCM or PSX ADPCM) @@ -238,6 +238,7 @@ etc: - .kcey (EACS IMA ADPCM) - .mwv (Level-5 0x555 ADPCM) - .ogg, .logg (Ogg Vorbis) +- .p3d (Radical ADPCM) - .rsf (CCITT G.721 ADPCM) - .sab (Worms 4 soundpacks) - .s14/.sss (G.722.1) diff --git a/src/Makefile b/src/Makefile index 4903369a..54430acb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -231,7 +231,8 @@ META_OBJS=meta/adx_header.o \ meta/ps2_gcm.o \ meta/ps2_smpl.o \ meta/ps2_msa.o \ - meta/pc_smp.o + meta/pc_smp.o \ + meta/p3d.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) diff --git a/src/coding/coding.h b/src/coding/coding.h index f1528634..44c90488 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -24,6 +24,8 @@ void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * out void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); +void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); + void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel); diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index d784d3d8..ab7980fe 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -219,6 +219,64 @@ void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou stream->adpcm_step_index=step_index; } +// "Radical ADPCM", mono form (for use with interleave layout) +// I think this is exactly equivalent to mono MS APDCM, but I need a +// channel-agnostic version to use within an interleave. +void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + int i=first_sample; + int sample_nibble; + int sample_decoded; + int delta; + int block_samples = 0x14 * 2; + + int32_t sample_count=0; + int32_t hist1=stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + off_t offset=stream->offset; + + first_sample = first_sample % block_samples; + + if (first_sample == 0) { + + hist1 = read_16bitLE(offset+2,stream->streamfile); + step_index = read_16bitLE(offset,stream->streamfile); + + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + } + + for (i=first_sample,sample_count=0; ioffset + 4 + (i/2); + + sample_nibble = (read_8bit(offset,stream->streamfile) >> (i&1?4:0))&0xf; + + sample_decoded=hist1; + + delta = step >> 3; + if (sample_nibble & 1) delta += step >> 2; + if (sample_nibble & 2) delta += step >> 1; + if (sample_nibble & 4) delta += step; + if (sample_nibble & 8) + sample_decoded -= delta; + else + sample_decoded += delta; + + hist1=clamp16(sample_decoded); + + step_index += IMA_IndexTable[sample_nibble]; + if (step_index < 0) step_index=0; + if (step_index > 88) step_index=88; + + outbuf[sample_count]=(short)(hist1); + + } + + stream->adpcm_history1_32=hist1; + stream->adpcm_step_index=step_index; +} + void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) { int i=first_sample; int sample_nibble; diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj index 5881158c..50e40e8d 100644 --- a/src/libvgmstream.vcproj +++ b/src/libvgmstream.vcproj @@ -502,6 +502,10 @@ RelativePath=".\meta\ogg_vorbis_file.c" > + + diff --git a/src/meta/Makefile.unix.am b/src/meta/Makefile.unix.am index d997e594..549b5445 100644 --- a/src/meta/Makefile.unix.am +++ b/src/meta/Makefile.unix.am @@ -188,5 +188,6 @@ libmeta_la_SOURCES += ps2_gcm.c libmeta_la_SOURCES += ps2_smpl.c libmeta_la_SOURCES += ps2_msa.c libmeta_la_SOURCES += pc_smp.c +libmeta_la_SOURCES += p3d.c EXTRA_DIST = meta.h diff --git a/src/meta/meta.h b/src/meta/meta.h index 169451c8..e3a3fb1a 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -461,4 +461,6 @@ VGMSTREAM * init_vgmstream_ps2_msa(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_pc_smp(STREAMFILE* streamFile); +VGMSTREAM * init_vgmstream_p3d(STREAMFILE* streamFile); + #endif diff --git a/src/meta/p3d.c b/src/meta/p3d.c new file mode 100644 index 00000000..8df38cfb --- /dev/null +++ b/src/meta/p3d.c @@ -0,0 +1,118 @@ +#include "meta.h" +#include "../util.h" + +/* P3D, with Radical ADPCM, from Prototype */ + +VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + char filename[260]; + off_t parse_offset; + off_t start_offset; + size_t file_size; + + uint32_t header_size; + uint32_t sample_rate; + uint32_t body_bytes; + int loop_flag; + int channel_count; + const int interleave = 0x14; + + /* check extension, case insensitive */ + streamFile->get_name(streamFile,filename,sizeof(filename)); + if (strcasecmp("p3d",filename_extension(filename))) goto fail; + + /* check header */ + if (read_32bitBE(0x0,streamFile) != 0x503344FF) /* P3D\xFF */ + goto fail; + header_size = read_32bitLE(0x4,streamFile); + if (0xC != header_size) goto fail; + file_size = get_streamfile_size(streamFile); + if (read_32bitLE(0x8,streamFile) != file_size) goto fail; + if (read_32bitBE(0xC,streamFile) != 0xFE) goto fail; + /* body size twice? */ + if (read_32bitLE(0x10,streamFile) + header_size != file_size) goto fail; + if (read_32bitLE(0x14,streamFile) + header_size != file_size) goto fail; + + /* mysterious 10! */ + if (read_32bitLE(0x18,streamFile) != 10) goto fail; + + /* parse header text */ + parse_offset = 0x1C; + { + int text_len = read_32bitLE(parse_offset,streamFile); + if (9 != text_len) goto fail; + /* AudioFile */ + if (read_32bitBE(parse_offset+4,streamFile) != 0x41756469 || + read_32bitBE(parse_offset+8,streamFile) != 0x6F46696C || + read_16bitBE(parse_offset+12,streamFile) != 0x6500) goto fail; + parse_offset += 4 + text_len + 1; + } + channel_count = read_32bitLE(parse_offset,streamFile); + parse_offset += 4; + { + int i; + /* channel names? */ + for (i = 0; i < channel_count; i++) + { + int text_len = read_32bitLE(parse_offset,streamFile); + parse_offset += 4 + text_len + 1; + } + } + /* info count? */ + if (1 != read_32bitLE(parse_offset,streamFile)) goto fail; + parse_offset += 4; + { + int text_len = read_32bitLE(parse_offset,streamFile); + if (4 != text_len) goto fail; + /* radp */ + if (read_32bitBE(parse_offset+4,streamFile) != 0x72616470 || + read_8bit(parse_offset+8,streamFile) != 0) goto fail; + parse_offset += 4 + text_len + 1; + } + + /* real RADP header */ + if (0x52414450 != read_32bitBE(parse_offset,streamFile)) goto fail; + if (read_32bitLE(parse_offset+4,streamFile) != channel_count) goto fail; + sample_rate = read_32bitLE(parse_offset+8,streamFile); + /* codec id? */ + if (9 != read_32bitLE(parse_offset+0xC,streamFile)) goto fail; + body_bytes = read_32bitLE(parse_offset+0x10,streamFile); + start_offset = parse_offset+0x14; + printf("start = %x\n", start_offset); + if (start_offset + body_bytes != file_size) goto fail; + + loop_flag = 0; + + /* 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 = sample_rate; + vgmstream->coding_type = coding_RAD_IMA_mono; + vgmstream->interleave_block_size = interleave; + vgmstream->num_samples = body_bytes / interleave / channel_count * 32; + vgmstream->layout_type = layout_interleave; + vgmstream->meta_type = meta_P3D; + + /* open the file for reading */ + { + int i; + STREAMFILE * file; + file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!file) goto fail; + for (i=0;ich[i].streamfile = file; + + vgmstream->ch[i].offset=vgmstream->ch[i].channel_start_offset=start_offset+interleave*i; + } + } + + return vgmstream; + +fail: + /* clean up anything we may have opened */ + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/vgmstream.c b/src/vgmstream.c index 14b271e5..ba342302 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -253,6 +253,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_ps2_smpl, init_vgmstream_ps2_msa, init_vgmstream_pc_smp, + init_vgmstream_p3d, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) @@ -789,6 +790,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { case coding_MS_IMA: case coding_RAD_IMA: return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels; + case coding_RAD_IMA_mono: + return 32; case coding_NDS_PROCYON: return 30; #ifdef VGM_USE_G7221 @@ -843,6 +846,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { case coding_NDS_IMA: case coding_DAT4_IMA: return vgmstream->interleave_block_size; + case coding_RAD_IMA_mono: + return 0x14; case coding_NGC_DTK: return 32; case coding_EACS_IMA: @@ -1037,6 +1042,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to samples_to_do,chan); } break; + case coding_RAD_IMA_mono: + for (chan=0;chanchannels;chan++) { + decode_rad_ima_mono(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, + vgmstream->channels,vgmstream->samples_into_block, + samples_to_do); + } + break; case coding_NGC_DTK: for (chan=0;chanchannels;chan++) { decode_ngc_dtk(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1545,6 +1557,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case coding_RAD_IMA: snprintf(temp,TEMPSIZE,"\"Radical\" 4-bit IMA ADPCM"); break; + case coding_RAD_IMA_mono: + snprintf(temp,TEMPSIZE,"\"Radical\" 4-bit IMA ADPCM (mono)"); + break; case coding_APPLE_IMA4: snprintf(temp,TEMPSIZE,"Apple Quicktime 4-bit IMA ADPCM"); break; @@ -2504,6 +2519,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { case meta_PC_SMP: snprintf(temp,TEMPSIZE,".smp Header"); break; + case meta_P3D: + snprintf(temp,TEMPSIZE,"P3D Header"); + break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); } diff --git a/src/vgmstream.h b/src/vgmstream.h index 92a0b257..1f4f6ec0 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -81,6 +81,7 @@ typedef enum { coding_INT_IMA, /* */ coding_MS_IMA, /* Microsoft IMA */ coding_RAD_IMA, /* "Radical ADPCM" IMA */ + coding_RAD_IMA_mono, /* "Radical ADPCM" IMA, mono (for interleave) */ coding_APPLE_IMA4, /* Apple Quicktime IMA4 */ coding_DAT4_IMA, /* Eurocom 'DAT4' IMA ADPCM */ coding_WS, /* Westwood Studios' custom VBR ADPCM */ @@ -451,6 +452,7 @@ typedef enum { meta_PS2_SMPL, /* Homura */ meta_PS2_MSA, /* Psyvariar -Complete Edition- */ meta_PC_SMP, /* unknown PC game .smp */ + meta_P3D, /* Prototype P3D */ } meta_t; typedef struct { diff --git a/unix/data.c b/unix/data.c index acf26227..ea7ea02e 100644 --- a/unix/data.c +++ b/unix/data.c @@ -128,6 +128,7 @@ gchar *vgmstream_exts [] = { "omu", "p2bt", + "p3d", "pcm", "pdt", "pnb", diff --git a/winamp/in_vgmstream.c b/winamp/in_vgmstream.c index ce812381..c2f1281b 100644 --- a/winamp/in_vgmstream.c +++ b/winamp/in_vgmstream.c @@ -195,6 +195,7 @@ char * extension_list[] = { "omu\0OMU Audio File (*.OMU)\0", "p2bt\0P2BT Audio File (*.P2BT)\0", + "p3d\0P3D Audio File (*.P3D)\0", "pcm\0PCM Audio File (*.PCM)\0", "pdt\0PDT Audio File (*.PDT)\0", "pnb\0PNB Audio File (*.PNB)\0",