From 275e4e9c61354b1be65c0f1e05bb06dc6ca54c73 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Thu, 30 Aug 2012 05:35:12 -0700 Subject: [PATCH] Implemented MP4 AAC support for iOS AKB format --- fb2k/foo_input_vgmstream.vcxproj | 10 ++- fb2k/in_vgmstream.cpp | 2 + fb2k/version.h | 2 +- src/coding/coding.h | 2 + src/coding/mp4_aac_decoder.c | 69 ++++++++++++++ src/libvgmstream.vcxproj | 7 +- src/libvgmstream.vcxproj.filters | 9 ++ src/meta/akb.c | 30 +++++++ src/meta/meta.h | 6 ++ src/meta/mp4.c | 149 +++++++++++++++++++++++++++++++ src/vgmstream.c | 39 ++++++++ src/vgmstream.h | 28 ++++++ 12 files changed, 348 insertions(+), 5 deletions(-) create mode 100644 src/coding/mp4_aac_decoder.c create mode 100644 src/meta/akb.c create mode 100644 src/meta/mp4.c diff --git a/fb2k/foo_input_vgmstream.vcxproj b/fb2k/foo_input_vgmstream.vcxproj index 030acd5d..ec4caf19 100644 --- a/fb2k/foo_input_vgmstream.vcxproj +++ b/fb2k/foo_input_vgmstream.vcxproj @@ -62,7 +62,7 @@ Disabled - ../ext_includes;..\..\foobar\foobar2000\SDK;..\..\foobar\foobar2000\helpers;..\..\foobar\foobar2000\ATLHelpers;..\..\foobar\foobar2000\shared;%(AdditionalIncludeDirectories) + ../ext_includes;..\..\foobar\foobar2000\SDK;..\..\foobar\foobar2000\helpers;..\..\foobar\foobar2000\ATLHelpers;..\..\foobar\foobar2000\shared;../../qaac/mp4v2/include;../../fdk-aac/libSYS/include;../../fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) WIN32;VGM_USE_G7221;_DEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions) true EnableFastChecks @@ -81,7 +81,7 @@ - ../ext_includes;..\..\foobar\foobar2000\SDK;..\..\foobar\foobar2000\helpers;..\..\foobar\foobar2000\ATLHelpers;..\..\foobar\foobar2000\shared;%(AdditionalIncludeDirectories) + ../ext_includes;..\..\foobar\foobar2000\SDK;..\..\foobar\foobar2000\helpers;..\..\foobar\foobar2000\ATLHelpers;..\..\foobar\foobar2000\shared;../../qaac/mp4v2/include;../../fdk-aac/libSYS/include;../../fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) WIN32;VGM_USE_G7221;NDEBUG;_WINDOWS;_USRDLL;IN_VGMSTREAM_EXPORTS;%(PreprocessorDefinitions) MultiThreaded @@ -114,6 +114,9 @@ + + {308e2ad5-be31-4770-9441-a8d50f56895c} + {622e8b19-8109-4717-bd4d-9657aa78363e} @@ -129,6 +132,9 @@ {ebfffb4e-261d-44d3-b89c-957b31a0bf9c} + + {86a064e2-c81b-4eee-8be0-a39a2e7c7c76} + {10e6bfc6-1e5b-46e4-ba42-f04dfbd0abff} diff --git a/fb2k/in_vgmstream.cpp b/fb2k/in_vgmstream.cpp index bce8cda8..5d13b518 100644 --- a/fb2k/in_vgmstream.cpp +++ b/fb2k/in_vgmstream.cpp @@ -281,6 +281,7 @@ bool input_vgmstream::g_is_our_path(const char * p_path,const char * p_extension if(!stricmp_utf8(p_extension,"ahx")) return 1; if(!stricmp_utf8(p_extension,"aic")) return 1; if(!stricmp_utf8(p_extension,"aix")) return 1; + if(!stricmp_utf8(p_extension,"akb")) return 1; if(!stricmp_utf8(p_extension,"amts")) return 1; if(!stricmp_utf8(p_extension,"as4")) return 1; if(!stricmp_utf8(p_extension,"asd")) return 1; @@ -599,6 +600,7 @@ DECLARE_MULTIPLE_FILE_TYPE("AGSC Audio File (*.AGSC)", agsc); DECLARE_MULTIPLE_FILE_TYPE("AHX Audio File (*.AHX)", ahx); DECLARE_MULTIPLE_FILE_TYPE("AIFC Audio File (*.AIFC)", aifc); DECLARE_MULTIPLE_FILE_TYPE("AIX Audio File (*.AIX)", aix); +DECLARE_MULTIPLE_FILE_TYPE("AKB Audio File (*.AKB)", akb); DECLARE_MULTIPLE_FILE_TYPE("AMTS Audio File (*.AMTS)", amts); DECLARE_MULTIPLE_FILE_TYPE("AS4 Audio File (*.AS4)", as4); DECLARE_MULTIPLE_FILE_TYPE("ASD Audio File (*.ASD)", asd); diff --git a/fb2k/version.h b/fb2k/version.h index 02846dab..e97786e2 100755 --- a/fb2k/version.h +++ b/fb2k/version.h @@ -1 +1 @@ -#define VERSION "r995" +#define VERSION "r995-1" diff --git a/src/coding/coding.h b/src/coding/coding.h index 3bf4a74f..d779ffdf 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -71,6 +71,8 @@ void decode_maxis_adpcm(VGMSTREAM * vgmstream, sample * outbuf, int channelspaci void decode_ogg_vorbis(ogg_vorbis_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); #endif +void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); + void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_cbd2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); diff --git a/src/coding/mp4_aac_decoder.c b/src/coding/mp4_aac_decoder.c new file mode 100644 index 00000000..4a3a6aff --- /dev/null +++ b/src/coding/mp4_aac_decoder.c @@ -0,0 +1,69 @@ +#include "../vgmstream.h" + +void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { + int samples_done = 0; + + uint8_t * buffer = NULL; + uint32_t buffer_size; + UINT ubuffer_size, bytes_valid; + + CStreamInfo * stream_info = aacDecoder_GetStreamInfo( data->h_aacdecoder ); + + int32_t samples_remain = data->samples_per_frame - data->sample_ptr; + + if ( data->samples_discard ) { + if ( samples_remain <= data->samples_discard ) { + data->samples_discard -= samples_remain; + samples_remain = 0; + } + else { + samples_remain -= data->samples_discard; + data->sample_ptr += data->samples_discard; + data->samples_discard = 0; + } + } + + if ( samples_remain > samples_to_do ) samples_remain = samples_to_do; + + memcpy( outbuf, data->sample_buffer + data->sample_ptr * stream_info->numChannels, samples_remain * stream_info->numChannels * sizeof(short) ); + + outbuf += samples_remain * stream_info->numChannels; + + data->sample_ptr += samples_remain; + + samples_done += samples_remain; + + while ( samples_done < samples_to_do ) { + if (!MP4ReadSample( data->h_mp4file, data->track_id, ++data->sampleId, (uint8_t**)(&buffer), (uint32_t*)(&buffer_size), 0, 0, 0, 0)) return; + ubuffer_size = buffer_size; + bytes_valid = buffer_size; + if ( aacDecoder_Fill( data->h_aacdecoder, &buffer, &ubuffer_size, &bytes_valid ) || bytes_valid ) { + free( buffer ); + return; + } + if ( aacDecoder_DecodeFrame( data->h_aacdecoder, data->sample_buffer, ( (6) * (2048)*4 ), 0 ) ) { + free( buffer ); + return; + } + free( buffer ); buffer = NULL; + stream_info = aacDecoder_GetStreamInfo( data->h_aacdecoder ); + samples_remain = data->samples_per_frame = stream_info->frameSize; + data->sample_ptr = 0; + if ( data->samples_discard ) { + if ( samples_remain <= data->samples_discard ) { + data->samples_discard -= samples_remain; + samples_remain = 0; + } + else { + samples_remain -= data->samples_discard; + data->sample_ptr = data->samples_discard; + data->samples_discard = 0; + } + } + if ( samples_remain > samples_to_do - samples_done ) samples_remain = samples_to_do - samples_done; + memcpy( outbuf, data->sample_buffer + data->sample_ptr * stream_info->numChannels, samples_remain * stream_info->numChannels * sizeof(short) ); + samples_done += samples_remain; + outbuf += samples_remain * stream_info->numChannels; + data->sample_ptr = samples_remain; + } +} \ No newline at end of file diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 0a39ac75..0795d756 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -53,7 +53,7 @@ Disabled - ../ext_includes;%(AdditionalIncludeDirectories) + ../ext_includes;../../qaac/mp4v2/include;../../fdk-aac/libSYS/include;../../fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) WIN32;VGM_USE_G7221;_DEBUG;_LIB;%(PreprocessorDefinitions) true EnableFastChecks @@ -67,7 +67,7 @@ - ../ext_includes;%(AdditionalIncludeDirectories) + ../ext_includes;../../qaac/mp4v2/include;../../fdk-aac/libSYS/include;../../fdk-aac/libAACdec/include;%(AdditionalIncludeDirectories) WIN32;VGM_USE_G7221;NDEBUG;_LIB;%(PreprocessorDefinitions) MultiThreaded @@ -90,16 +90,19 @@ + + + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index c945a4eb..3a31a0ad 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -937,5 +937,14 @@ meta\Source Files + + meta\Source Files + + + coding\Source Files + + + meta\Source Files + \ No newline at end of file diff --git a/src/meta/akb.c b/src/meta/akb.c new file mode 100644 index 00000000..297c74fc --- /dev/null +++ b/src/meta/akb.c @@ -0,0 +1,30 @@ +#include "../vgmstream.h" +#include "meta.h" + +VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { + VGMSTREAM * vgmstream = NULL; + + size_t filesize; + uint32_t loop_start, loop_end; + + if ((uint32_t)read_32bitBE(0, streamFile) != 0x414b4220) goto fail; + + loop_start = read_32bitLE(0x14, streamFile); + loop_end = read_32bitLE(0x18, streamFile); + + filesize = get_streamfile_size( streamFile ); + + vgmstream = init_vgmstream_mp4_aac_offset( streamFile, 0x20, filesize - 0x20 ); + if ( !vgmstream ) goto fail; + + if ( loop_start || loop_end ) { + vgmstream->loop_flag = 1; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + } + + return vgmstream; + +fail: + return NULL; +} \ No newline at end of file diff --git a/src/meta/meta.h b/src/meta/meta.h index 7be5b688..4039dc34 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -107,6 +107,12 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile); +VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile); + +VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); + +VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile); + VGMSTREAM * init_vgmstream_sfl(STREAMFILE * streamFile); #endif diff --git a/src/meta/mp4.c b/src/meta/mp4.c new file mode 100644 index 00000000..eb51fad6 --- /dev/null +++ b/src/meta/mp4.c @@ -0,0 +1,149 @@ +#include "../vgmstream.h" +#include "meta.h" +#include "../util.h" + +void* mp4_file_open( const char* name, MP4FileMode mode ) +{ + char * endptr; +#ifdef _MSC_VER + unsigned __int64 ptr = _strtoui64( name, &endptr, 16 ); +#else + unsigned long ptr = strtoul( name, &endptr, 16 ); +#endif + return (void*) ptr; +} + +int mp4_file_seek( void* handle, int64_t pos ) +{ + mp4_streamfile * file = ( mp4_streamfile * ) handle; + if ( pos > file->size ) pos = file->size; + pos += file->start; + file->offset = pos; + return 0; +} + +int mp4_file_get_size( void* handle, int64_t* size ) +{ + mp4_streamfile * file = ( mp4_streamfile * ) handle; + *size = file->size; + return 0; +} + +int mp4_file_read( void* handle, void* buffer, int64_t size, int64_t* nin, int64_t maxChunkSize ) +{ + mp4_streamfile * file = ( mp4_streamfile * ) handle; + int64_t max_size = file->size - file->offset - file->start; + if ( size > max_size ) size = max_size; + *nin = read_streamfile( (uint8_t *) buffer, file->offset, size, file->streamfile ); + file->offset += *nin; + return 0; +} + +int mp4_file_write( void* handle, const void* buffer, int64_t size, int64_t* nout, int64_t maxChunkSize ) +{ + return 1; +} + +int mp4_file_close( void* handle ) +{ + return 0; +} + +MP4FileProvider mp4_file_provider = { mp4_file_open, mp4_file_seek, mp4_file_get_size, mp4_file_read, mp4_file_write, mp4_file_close }; + +VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); + +VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE *streamFile) { + return init_vgmstream_mp4_aac_offset( streamFile, 0, streamFile->get_size(streamFile) ); +} + +VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) { + VGMSTREAM * vgmstream = NULL; + + char filename[260]; + + mp4_aac_codec_data * aac_file = ( mp4_aac_codec_data * ) calloc(1, sizeof(mp4_aac_codec_data)); + + CStreamInfo * stream_info; + + uint8_t * buffer = NULL; + uint32_t buffer_size; + UINT ubuffer_size, bytes_valid; + + if ( !aac_file ) goto fail; + + aac_file->if_file.streamfile = streamFile; + aac_file->if_file.start = start; + aac_file->if_file.offset = start; + aac_file->if_file.size = size; + + /* Big ol' kludge! */ + sprintf( filename, "%p", &aac_file->if_file ); + aac_file->h_mp4file = MP4ReadProvider( filename, &mp4_file_provider ); + if ( !aac_file->h_mp4file ) goto fail; + + if ( MP4GetNumberOfTracks(aac_file->h_mp4file, MP4_AUDIO_TRACK_TYPE, '\000') != 1 ) goto fail; + + aac_file->track_id = MP4FindTrackId( aac_file->h_mp4file, 0, MP4_AUDIO_TRACK_TYPE, '\000' ); + + aac_file->h_aacdecoder = aacDecoder_Open( TT_MP4_RAW, 1 ); + if ( !aac_file->h_aacdecoder ) goto fail; + + aacDecoder_SetParam( aac_file->h_aacdecoder, AAC_PCM_OUTPUT_CHANNELS, 2 ); + + MP4GetTrackESConfiguration( aac_file->h_mp4file, aac_file->track_id, (uint8_t**)(&aac_file->codec_init_data), (uint32_t*)(&aac_file->codec_init_data_size)); + + if ( aacDecoder_ConfigRaw( aac_file->h_aacdecoder, &aac_file->codec_init_data, &aac_file->codec_init_data_size ) ) goto fail; + + aac_file->sampleId = 1; + aac_file->numSamples = MP4GetTrackNumberOfSamples( aac_file->h_mp4file, aac_file->track_id ); + + if (!MP4ReadSample(aac_file->h_mp4file, aac_file->track_id, aac_file->sampleId, (uint8_t**)(&buffer), (uint32_t*)(&buffer_size), 0, 0, 0, 0)) goto fail; + + ubuffer_size = buffer_size; + bytes_valid = buffer_size; + if ( aacDecoder_Fill( aac_file->h_aacdecoder, &buffer, &ubuffer_size, &bytes_valid ) || bytes_valid ) goto fail; + if ( aacDecoder_DecodeFrame( aac_file->h_aacdecoder, aac_file->sample_buffer, ( (6) * (2048)*4 ), 0 ) ) goto fail; + + free( buffer ); buffer = NULL; + + aac_file->sample_ptr = 0; + + stream_info = aacDecoder_GetStreamInfo( aac_file->h_aacdecoder ); + + aac_file->samples_per_frame = stream_info->frameSize; + aac_file->samples_discard = 0; + + streamFile->get_name( streamFile, filename, sizeof(filename) ); + + aac_file->if_file.streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); + if (!aac_file->if_file.streamfile) goto fail; + + vgmstream = allocate_vgmstream( stream_info->numChannels, 1 ); + if (!vgmstream) goto fail; + + vgmstream->loop_flag = 0; + + vgmstream->codec_data = aac_file; + + vgmstream->channels = stream_info->numChannels; + vgmstream->sample_rate = stream_info->sampleRate; + + vgmstream->num_samples = stream_info->frameSize * aac_file->numSamples; + + vgmstream->coding_type = coding_MP4_AAC; + vgmstream->layout_type = layout_none; + vgmstream->meta_type = meta_MP4; + + return vgmstream; + +fail: + if ( buffer ) free( buffer ); + if ( aac_file ) { + if ( aac_file->h_aacdecoder ) aacDecoder_Close( aac_file->h_aacdecoder ); + if ( aac_file->h_mp4file ) MP4Close( aac_file->h_mp4file, 0 ); + if ( aac_file->codec_init_data ) free( aac_file->codec_init_data ); + free( aac_file ); + } + return NULL; +} \ No newline at end of file diff --git a/src/vgmstream.c b/src/vgmstream.c index ce7edf75..9d02af41 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -59,6 +59,10 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { init_vgmstream_sli_ogg, init_vgmstream_sfl, #endif +#if 0 + init_vgmstream_mp4_aac, +#endif + init_vgmstream_akb, init_vgmstream_sadb, init_vgmstream_ps2_bmdx, init_vgmstream_wsi, @@ -414,6 +418,12 @@ void reset_vgmstream(VGMSTREAM * vgmstream) { ov_pcm_seek(ogg_vorbis_file, 0); } #endif + if (vgmstream->coding_type==coding_MP4_AAC) { + mp4_aac_codec_data *data = vgmstream->codec_data; + data->sampleId = 1; + data->sample_ptr = data->samples_per_frame; + data->samples_discard = 0; + } #ifdef VGM_USE_MPEG if (vgmstream->layout_type==layout_mpeg || vgmstream->layout_type==layout_fake_mpeg) { @@ -576,6 +586,18 @@ void close_vgmstream(VGMSTREAM * vgmstream) { } #endif + if (vgmstream->coding_type==coding_MP4_AAC) { + mp4_aac_codec_data *data = vgmstream->codec_data; + if (vgmstream->codec_data) { + if (data->h_aacdecoder) aacDecoder_Close(data->h_aacdecoder); + if (data->h_mp4file) MP4Close(data->h_mp4file, 0); + if (data->if_file.streamfile) close_streamfile(data->if_file.streamfile); + if (data->codec_init_data) free(data->codec_init_data); + free(vgmstream->codec_data); + vgmstream->codec_data = NULL; + } + } + #ifdef VGM_USE_MPEG if (vgmstream->layout_type==layout_fake_mpeg|| vgmstream->layout_type==layout_mpeg) { @@ -922,6 +944,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { return 54; case coding_MTAF: return 0x80*2; + case coding_MP4_AAC: + return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame; default: return 0; } @@ -1289,6 +1313,11 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to vgmstream->channels); break; #endif + case coding_MP4_AAC: + decode_mp4_aac(vgmstream->codec_data, + buffer+samples_written*vgmstream->channels,samples_to_do, + vgmstream->channels); + break; case coding_SDX2: for (chan=0;chanchannels;chan++) { decode_sdx2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, @@ -1546,6 +1575,16 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { ov_pcm_seek_lap(ogg_vorbis_file, vgmstream->loop_sample); } #endif + if (vgmstream->coding_type==coding_MP4_AAC) { + mp4_aac_codec_data *data = (mp4_aac_codec_data *)(vgmstream->codec_data); + data->sampleId = 0; + data->sample_ptr = data->samples_per_frame; + data->samples_discard = vgmstream->loop_sample; + aacDecoder_Close(data->h_aacdecoder); + data->h_aacdecoder = aacDecoder_Open( TT_MP4_RAW, 1 ); + aacDecoder_SetParam( data->h_aacdecoder, AAC_PCM_OUTPUT_CHANNELS, 2 ); + aacDecoder_ConfigRaw( data->h_aacdecoder, &data->codec_init_data, &data->codec_init_data_size ); + } #ifdef VGM_USE_MPEG /* won't work for fake MPEG */ if (vgmstream->layout_type==layout_mpeg) { diff --git a/src/vgmstream.h b/src/vgmstream.h index af197975..7f9caf46 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -26,6 +26,12 @@ #ifdef VGM_USE_G7221 #include "g7221.h" #endif + +#define MP4V2_NO_STDINT_DEFS +#include + +#include + #include "coding/acm_decoder.h" #include "coding/nwa_decoder.h" @@ -126,6 +132,8 @@ typedef enum { coding_PCM16LE_XOR_int, /* sample-level xor */ coding_LSF, /* lsf ADPCM */ coding_MTAF, /* Konami IMA-derived MTAF ADPCM */ + + coding_MP4_AAC, } coding_t; /* The layout type specifies how the sound data is laid out in the file */ @@ -537,6 +545,7 @@ typedef enum { meta_PS2_HSF, // Lowrider (PS2) meta_PS3_IVAG, // Interleaved VAG files (PS3) meta_PS2_2PFS, // Mahoromatic: Moetto - KiraKira Maid-San (PS2) + meta_MP4, } meta_t; typedef struct { @@ -751,6 +760,25 @@ typedef struct { STREAMFILE **intfiles; } scd_int_codec_data; +typedef struct { + STREAMFILE *streamfile; + uint64_t start; + uint64_t offset; + uint64_t size; +} mp4_streamfile; + +typedef struct { + mp4_streamfile if_file; + MP4FileHandle h_mp4file; + MP4TrackId track_id; + unsigned long sampleId, numSamples; + uint8_t * codec_init_data; + UINT codec_init_data_size; + HANDLE_AACDECODER h_aacdecoder; + unsigned int sample_ptr, samples_per_frame, samples_discard; + INT_PCM sample_buffer[( (6) * (2048)*4 )]; +} mp4_aac_codec_data; + /* do format detection, return pointer to a usable VGMSTREAM, or NULL on failure */ VGMSTREAM * init_vgmstream(const char * const filename);