From df52f05761d5a7f263cc93e549b48d8996eaa729 Mon Sep 17 00:00:00 2001 From: Chris Moeller Date: Thu, 6 Aug 2015 06:31:49 +0000 Subject: [PATCH] Massive update to bring the XMPlay plugin up to working order --- xmp-vgmstream/.gitignore | 22 ++ xmp-vgmstream/DllMain.c | 400 +++++++++++++++++++++------- xmp-vgmstream/genversion.bat | 48 ++++ xmp-vgmstream/xmp-vgmstream.sln | 60 +++++ xmp-vgmstream/xmp-vgmstream.vcproj | 172 ------------ xmp-vgmstream/xmp-vgmstream.vcxproj | 124 +++++++++ xmp-vgmstream/xmp_in.h | 110 -------- xmp-vgmstream/xmpfunc.h | 177 ++++++++++++ xmp-vgmstream/xmpin.def | 2 + xmp-vgmstream/xmpin.h | 112 ++++++++ 10 files changed, 843 insertions(+), 384 deletions(-) create mode 100644 xmp-vgmstream/.gitignore create mode 100644 xmp-vgmstream/genversion.bat create mode 100644 xmp-vgmstream/xmp-vgmstream.sln delete mode 100644 xmp-vgmstream/xmp-vgmstream.vcproj create mode 100644 xmp-vgmstream/xmp-vgmstream.vcxproj delete mode 100644 xmp-vgmstream/xmp_in.h create mode 100644 xmp-vgmstream/xmpfunc.h create mode 100644 xmp-vgmstream/xmpin.def create mode 100644 xmp-vgmstream/xmpin.h diff --git a/xmp-vgmstream/.gitignore b/xmp-vgmstream/.gitignore new file mode 100644 index 00000000..1ec0581f --- /dev/null +++ b/xmp-vgmstream/.gitignore @@ -0,0 +1,22 @@ +# intellisense crap +ipch + +# more intellisense crap +xmp-vgmstream.sdf +xmp-vgmstream.opensdf +xmp-vgmstream.suo +xmp-vgmstream.v11.suo +xmp-vgmstream.v12.suo + +# output directories +Debug +Release + +# and some debugging symbol crap +*.pdb + +# new for Visual Studio 2015 +.vs + +# auto generated +version.h \ No newline at end of file diff --git a/xmp-vgmstream/DllMain.c b/xmp-vgmstream/DllMain.c index ee5f0acc..0b0febd4 100644 --- a/xmp-vgmstream/DllMain.c +++ b/xmp-vgmstream/DllMain.c @@ -13,176 +13,372 @@ #include "../src/vgmstream.h" #include "../src/util.h" -#include "xmp_in.h" +#include "xmpin.h" +#include "version.h" #ifndef VERSION #define VERSION #endif -VGMSTREAM * vgmstream = NULL; -int Decoder_Is_Using_Lame_Hack = 0; -double timestamp=0; +static XMPFUNC_IN *xmpfin; +static XMPFUNC_MISC *xmpfmisc; +static XMPFUNC_FILE *xmpffile; + +typedef struct _XMPSTREAMFILE { + STREAMFILE sf; + XMPFILE file; + off_t offset; + char name[PATH_LIMIT]; +} XMPSTREAMFILE; + +static void xmpsf_seek(XMPSTREAMFILE *this, off_t offset) { + if (xmpffile->Seek(this->file, offset)) + this->offset = offset; + else + this->offset = xmpffile->Tell(this->file); +} + +static off_t xmpsf_get_size(XMPSTREAMFILE *this) +{ + return xmpffile->GetSize(this->file); +} + +static off_t xmpsf_get_offset(XMPSTREAMFILE *this) +{ + return xmpffile->Tell(this->file); +} + +static void xmpsf_get_name(XMPSTREAMFILE *this, char *buffer, size_t length) +{ + strncpy(buffer, this->name, length); + buffer[length - 1] = '\0'; +} + +static size_t xmpsf_read(XMPSTREAMFILE *this, uint8_t *dest, off_t offset, size_t length) +{ + size_t read; + if (this->offset != offset) + xmpsf_seek(this, offset); + read = xmpffile->Read(this->file, dest, length); + if (read > 0) + this->offset += read; + return read; +} + +static void xmpsf_close(XMPSTREAMFILE *this) +{ + xmpffile->Close(this->file); + free(this); +} + +static STREAMFILE *xmpsf_create_from_path(const char *path); +static STREAMFILE *xmpsf_open(XMPSTREAMFILE *this, const char *const filename, size_t buffersize) +{ + if (!filename) return NULL; + return xmpsf_create_from_path(filename); +} + +static STREAMFILE *xmpsf_create(XMPFILE file, const char *path) +{ + XMPSTREAMFILE *streamfile = malloc(sizeof(XMPSTREAMFILE)); + + if (!streamfile) return NULL; + + memset(streamfile, 0, sizeof(XMPSTREAMFILE)); + streamfile->sf.read = (void*)xmpsf_read; + streamfile->sf.get_size = (void*)xmpsf_get_size; + streamfile->sf.get_offset = (void*)xmpsf_get_offset; + streamfile->sf.get_name = (void*)xmpsf_get_name; + streamfile->sf.get_realname = (void*)xmpsf_get_name; + streamfile->sf.open = (void*)xmpsf_open; + streamfile->sf.close = (void*)xmpsf_close; + streamfile->file = file; + streamfile->offset = 0; + strncpy(streamfile->name, path, sizeof(streamfile->name)); + + return &streamfile->sf; +} + +STREAMFILE *xmpsf_create_from_path(const char *path) +{ + XMPFILE file = xmpffile->Open(path); + if (!file) + return NULL; + + return xmpsf_create(file, path); +} + +VGMSTREAM *init_vgmstream_from_path(const char *path) +{ + STREAMFILE *sf; + VGMSTREAM *vgm; + + sf = xmpsf_create_from_path(path); + if (!sf) return NULL; + + vgm = init_vgmstream_from_STREAMFILE(sf); + if (!vgm) goto err1; + + return vgm; + +err1: + xmpsf_close((XMPSTREAMFILE *)sf); + return NULL; +} + +VGMSTREAM *init_vgmstream_from_xmpfile(XMPFILE file, const char *path) +{ + STREAMFILE *sf; + VGMSTREAM *vgm; + + sf = xmpsf_create(file, path); + if (!sf) return NULL; + + vgm = init_vgmstream_from_STREAMFILE(sf); + if (!vgm) goto err1; + + return vgm; + +err1: + xmpsf_close((XMPSTREAMFILE *)sf); + return NULL; +} + +VGMSTREAM * vgmstream = NULL; +int32_t totalFrames, framesDone, framesLength, framesFade; -#define DECODE_SIZE 1024 #define APP_NAME "vgmstream plugin" #define PLUGIN_DESCRIPTION "vgmstream plugin " VERSION " " __DATE__ -void __stdcall XMPAbout() { - MessageBox(NULL, +void __stdcall XMPAbout(HWND hwParent) { + MessageBox(hwParent, PLUGIN_DESCRIPTION "\n" - "by hcs, FastElbja, manakoAT, and bxaimc\n\n" - "http://sourceforge.net/projects/vgmstream" - ,"about in_vgmstream",MB_OK); + "by hcs, FastElbja, manakoAT, bxaimc, and kode54\n\n" + "https://gitlab.kode54.net/kode54/vgmstream" + ,"about xmp-vgmstream",MB_OK); } void __stdcall Stop() { close_vgmstream(vgmstream); } -int __stdcall XMP_CheckFile(char *filename, BYTE *buf, DWORD length) { - VGMSTREAM* fakevgmstream = init_vgmstream(filename); +BOOL __stdcall XMP_CheckFile(const char *filename, XMPFILE file) { + VGMSTREAM* thisvgmstream; + if (file) thisvgmstream = init_vgmstream_from_xmpfile(file, filename); + else thisvgmstream = init_vgmstream_from_path(filename); int ret; - if (!fakevgmstream) ret = 0; - else { ret = 1; close_vgmstream(fakevgmstream); } + if (!thisvgmstream) ret = FALSE; + else { ret = TRUE; close_vgmstream(thisvgmstream); } return ret; } -// part of our hugeass hack to stop things from crashing -static INT32 valid_freqs[5] = { - 11025, - 22050, - 32000, - 44100, - 48000, -}; +DWORD __stdcall XMP_GetFileInfo(const char *filename, XMPFILE file, float **length, char **tags) +{ + VGMSTREAM* thisvgmstream; + if (file) thisvgmstream = init_vgmstream_from_xmpfile(file, filename); + else thisvgmstream = init_vgmstream_from_path(filename); + if (!thisvgmstream) return 0; -int __stdcall LoadVgmStream(char *filename, XMPFILE file) { - int i; - vgmstream = init_vgmstream(filename); + if (length && thisvgmstream->sample_rate) + { + int totalFrames = get_vgmstream_play_samples(2.0, 10.0, 10.0, thisvgmstream); + float *lens = (float*)xmpfmisc->Alloc(sizeof(float)); + lens[0] = (float)totalFrames / (float)thisvgmstream->sample_rate; + *length = lens; + } + + close_vgmstream(thisvgmstream); + + return 1; +} + +DWORD __stdcall LoadVgmStream(const char *filename, XMPFILE file) { + if (file) vgmstream = init_vgmstream_from_xmpfile(file, filename); + else vgmstream = init_vgmstream_from_path(filename); if (!vgmstream) return 0; - // just loop forever till we have configuration done - Decoder_Is_Using_Lame_Hack = 0; + totalFrames = get_vgmstream_play_samples(2.0, 10.0, 10.0, vgmstream); + framesDone = 0; + framesFade = vgmstream->sample_rate * 10; + framesLength = totalFrames - framesFade; - for (i = 0; i < 5; i ++) { - if (vgmstream->sample_rate == valid_freqs[i]) { - Decoder_Is_Using_Lame_Hack = 1; - break; - } + if (totalFrames) + { + float length = (float)totalFrames / (float)vgmstream->sample_rate; + xmpfin->SetLength(length, TRUE); } return 1; } -int __stdcall XMP_Buffer(float* buffer, UINT32 bufsize) { - int i,x,y; - int adder = vgmstream->channels * (Decoder_Is_Using_Lame_Hack ? 2 : 1); +DWORD __stdcall XMP_Buffer(float* buffer, DWORD bufsize) { + INT16 buf[1024]; + UINT32 i, j, todo, done; - /* - ** Algorithm for correct playback. - ** This is in fact a huge-ass hack which is terrible and crashes tracks with sample rates - ** it doesn't like. We'll need to fix this later - */ + BOOL doLoop = xmpfin->GetLooping(); + float *sbuf = buffer; - for (i=0;ichannels; - render_vgmstream(buf,vgmstream->channels,vgmstream); - for (x=0;x bufsize) + samplesTodo = bufsize; + + done = 0; + while (done < samplesTodo) { + todo = 1024 / vgmstream->channels; + if (todo > samplesTodo - done) + todo = samplesTodo - done; + render_vgmstream(buf, todo, vgmstream); + for (i = 0, j = todo * vgmstream->channels; i < j; ++i) + { + *sbuf++ = buf[i] * 1.0f / 32768.0f; } + done += todo; } - return bufsize; + sbuf = buffer; + + if (!doLoop && framesDone + done > framesLength) + { + long fadeStart = (framesLength > framesDone) ? framesLength : framesDone; + long fadeEnd = (framesDone + done) > totalFrames ? totalFrames : (framesDone + done); + long fadePos; + + float fadeScale = (float)(totalFrames - fadeStart) / framesFade; + float fadeStep = 1.0f / framesFade; + sbuf += (fadeStart - framesDone) * vgmstream->channels; + j = vgmstream->channels; + for (fadePos = fadeStart; fadePos < fadeEnd; ++fadePos) + { + for (i = 0; i < j; ++i) + { + sbuf[i] = sbuf[i] * fadeScale; + } + sbuf += j; + fadeScale -= fadeStep; + if (fadeScale <= 0.0f) break; + } + done = (int)(fadePos - framesDone); + } + + framesDone += done; + + return done * vgmstream->channels; } -DWORD __stdcall XMP_GetFormat(DWORD *chan, DWORD *res) { - *(chan) = vgmstream->channels; +void __stdcall XMP_SetFormat(XMPFORMAT *form) { + form->res = 16 / 8; + form->chan = vgmstream->channels; + form->rate = vgmstream->sample_rate; +} - if (Decoder_Is_Using_Lame_Hack) return vgmstream->sample_rate; - else return vgmstream->sample_rate / 2; +char * __stdcall XMP_GetTags() +{ + return NULL; +} + +double __stdcall XMP_GetGranularity() +{ + return 0.001; +} + +double __stdcall XMP_SetPosition(DWORD pos) { + double cpos = (double)framesDone / (double)vgmstream->sample_rate; + double time = pos * XMP_GetGranularity(); + + if (time < cpos) + { + reset_vgmstream(vgmstream); + cpos = 0.0; + } + + while (cpos < time) + { + INT16 buffer[1024]; + long max_sample_count = 1024 / vgmstream->channels; + long samples_to_skip = (long)((time - cpos) * vgmstream->sample_rate); + if (samples_to_skip > max_sample_count) + samples_to_skip = max_sample_count; + if (!samples_to_skip) + break; + render_vgmstream(buffer, (int)samples_to_skip, vgmstream); + cpos += (double)samples_to_skip / (double)vgmstream->sample_rate; + } + + framesDone = (int32_t)(cpos * vgmstream->sample_rate); + + return cpos; } void __stdcall XMP_GetInfoText(char* txt, char* length) { - sprintf(txt,"vgmstream!"); + if (txt) + sprintf(txt,"vgmstream!"); } void __stdcall GetAdditionalFields(char* blerp) { sprintf(blerp,"oh god how did this get here i am not good with computers\n"); } - -double __stdcall GetDecodePosition() { - return timestamp; -} - -// Trap functions to catch stuff we haven't implemented. -#define TRAP_FCN(id) void* __stdcall trap_##id() {\ - cprintf("trap %d hit\n",id);\ - return NULL;\ -} -TRAP_FCN(1); -TRAP_FCN(2); -TRAP_FCN(3); -TRAP_FCN(4); -TRAP_FCN(5); -TRAP_FCN(6); -TRAP_FCN(7); -TRAP_FCN(8); -TRAP_FCN(9); -TRAP_FCN(10); -TRAP_FCN(11); -TRAP_FCN(12); -TRAP_FCN(13); -TRAP_FCN(14); -TRAP_FCN(15); -TRAP_FCN(16); -TRAP_FCN(17); -TRAP_FCN(18); -TRAP_FCN(19); -TRAP_FCN(20); -TRAP_FCN(21); -TRAP_FCN(22); - -int __stdcall XMP_GetSubSongs() { - return 1; // -} - XMPIN vgmstream_intf = { - 0, + XMPIN_FLAG_CANSTREAM, "vgmstream for XMplay", - "vgmstream files\0brstm", + "vgmstream files\0""2dx9/aaap/aax/acm/adp/adpcm/ads/adx/afc/agsc/ahx/aifc/aiff/aix/amts/as4/asd/asf/asr/ass/ast/aud/aus/baf/baka/bar/bcstm/bcwav/bfstm/bfwav/bfwavnsmbu/bg00/bgw/bh2pcm/bmdx/bns/bnsf/bo2/brstm/caf/capdsp/ccc/cfn/cnk/dcs/dcsw/ddsp/de2/dmsg/dsp/dvi/dxh/eam/emff/enth/fag/filp/fsb/fwav/gca/gcm/gcsw/gcw/genh/gms/gsp/hgc1/his/hps/hwas/idsp/idvi/ikm/ild/int/isd/ish/ivaud/ivb/joe/kces/kcey/khv/kraw/leg/logg/lps/lsf/lwav/matx/mcg/mi4/mib/mic/mihb/mpdsp/msa/mss/msvp/mus/musc/musx/mwv/myspd/ndp/npsf/nus3bank/nwa/omu/otm/p3d/pcm/pdt/pnb/pos/psh/psw/raw/rkv/rnd/rrds/rsd/rsf/rstm/rwar/rwav/rws/rwsd/rwx/rxw/s14/sab/sad/sap/sc/scd/sd9/sdt/seg/sfl/sfs/sl3/sli/smp/smpl/snd/sng/sns/spd/sps/spsd/spt/spw/ss2/ss7/ssm/sss/ster/sth/stm/stma/str/strm/sts/stx/svag/svs/swav/swd/tec/thp/tk5/tydsp/um3/vag/vas/vgs/vig/vjdsp/voi/vpk/vs/vsf/waa/wac/wad/wam/was/wavm/wb/wii/wp2/wsd/wsi/wvs/xa/xa2/xa30/xmu/xss/xvas/xwav/xwb/ydsp/ymf/zsd/zwdsp", XMPAbout, NULL, XMP_CheckFile, - NULL, + XMP_GetFileInfo, LoadVgmStream, Stop, - XMP_GetFormat, - NULL, NULL, + XMP_SetFormat, + XMP_GetTags, // Actually mandatory XMP_GetInfoText, GetAdditionalFields, NULL, + XMP_SetPosition, + XMP_GetGranularity, NULL, - GetDecodePosition, - NULL,//trap_18, XMP_Buffer, NULL, NULL, - XMP_GetSubSongs, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, }; +static int shownerror = 0; + +__declspec(dllexport) XMPIN* __stdcall XMPIN_GetInterface(UINT32 face, InterfaceProc faceproc) +{ + if (face != XMPIN_FACE) + { // unsupported version + if (face %1_temp + +if %version%==unknown goto :skipgenerate + +echo # static version string; update manually before and after every release.> "..\version.mk" +echo %version_mk%>> "..\version.mk" + +:skipgenerate + +echo n | comp %1_temp %1 > NUL 2> NUL + +if not errorlevel 1 goto exit + +copy /y %1_temp %1 > NUL 2> NUL + +:exit + +del %1_temp diff --git a/xmp-vgmstream/xmp-vgmstream.sln b/xmp-vgmstream/xmp-vgmstream.sln new file mode 100644 index 00000000..71ea75f7 --- /dev/null +++ b/xmp-vgmstream/xmp-vgmstream.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xmp-vgmstream", "xmp-vgmstream.vcxproj", "{49AF76F7-CBA0-4486-9DDF-51F30DF45F33}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ext_libs", "..\ext_libs\ext_libs.vcxproj", "{10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvgmstream", "..\src\libvgmstream.vcxproj", "{54A6AD11-5369-4895-A06F-E255ABB99B11}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4v2", "..\..\qaac\vcproject\mp4v2\mp4v2.vcxproj", "{86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fdk-aac", "..\..\fdk-aac\msvc\fdk-aac.vcxproj", "{308E2AD5-BE31-4770-9441-A8D50F56895C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {49AF76F7-CBA0-4486-9DDF-51F30DF45F33}.Debug|x64.ActiveCfg = Debug|Win32 + {49AF76F7-CBA0-4486-9DDF-51F30DF45F33}.Debug|x86.ActiveCfg = Debug|Win32 + {49AF76F7-CBA0-4486-9DDF-51F30DF45F33}.Debug|x86.Build.0 = Debug|Win32 + {49AF76F7-CBA0-4486-9DDF-51F30DF45F33}.Release|x64.ActiveCfg = Release|Win32 + {49AF76F7-CBA0-4486-9DDF-51F30DF45F33}.Release|x86.ActiveCfg = Release|Win32 + {49AF76F7-CBA0-4486-9DDF-51F30DF45F33}.Release|x86.Build.0 = Release|Win32 + {10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}.Debug|x64.ActiveCfg = Debug|Win32 + {10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}.Debug|x86.ActiveCfg = Debug|Win32 + {10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}.Debug|x86.Build.0 = Debug|Win32 + {10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}.Release|x64.ActiveCfg = Release|Win32 + {10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}.Release|x86.ActiveCfg = Release|Win32 + {10E6BFC6-1E5B-46E4-BA42-F04DFBD0ABFF}.Release|x86.Build.0 = Release|Win32 + {54A6AD11-5369-4895-A06F-E255ABB99B11}.Debug|x64.ActiveCfg = Debug|Win32 + {54A6AD11-5369-4895-A06F-E255ABB99B11}.Debug|x86.ActiveCfg = Debug|Win32 + {54A6AD11-5369-4895-A06F-E255ABB99B11}.Debug|x86.Build.0 = Debug|Win32 + {54A6AD11-5369-4895-A06F-E255ABB99B11}.Release|x64.ActiveCfg = Release|Win32 + {54A6AD11-5369-4895-A06F-E255ABB99B11}.Release|x86.ActiveCfg = Release|Win32 + {54A6AD11-5369-4895-A06F-E255ABB99B11}.Release|x86.Build.0 = Release|Win32 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Debug|x64.ActiveCfg = Debug|x64 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Debug|x64.Build.0 = Debug|x64 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Debug|x86.ActiveCfg = Debug|Win32 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Debug|x86.Build.0 = Debug|Win32 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Release|x64.ActiveCfg = Release|x64 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Release|x64.Build.0 = Release|x64 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Release|x86.ActiveCfg = Release|Win32 + {86A064E2-C81B-4EEE-8BE0-A39A2E7C7C76}.Release|x86.Build.0 = Release|Win32 + {308E2AD5-BE31-4770-9441-A8D50F56895C}.Debug|x64.ActiveCfg = Debug|Win32 + {308E2AD5-BE31-4770-9441-A8D50F56895C}.Debug|x86.ActiveCfg = Debug|Win32 + {308E2AD5-BE31-4770-9441-A8D50F56895C}.Debug|x86.Build.0 = Debug|Win32 + {308E2AD5-BE31-4770-9441-A8D50F56895C}.Release|x64.ActiveCfg = Release|Win32 + {308E2AD5-BE31-4770-9441-A8D50F56895C}.Release|x86.ActiveCfg = Release|Win32 + {308E2AD5-BE31-4770-9441-A8D50F56895C}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/xmp-vgmstream/xmp-vgmstream.vcproj b/xmp-vgmstream/xmp-vgmstream.vcproj deleted file mode 100644 index cce382d4..00000000 --- a/xmp-vgmstream/xmp-vgmstream.vcproj +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/xmp-vgmstream/xmp-vgmstream.vcxproj b/xmp-vgmstream/xmp-vgmstream.vcxproj new file mode 100644 index 00000000..a32e4185 --- /dev/null +++ b/xmp-vgmstream/xmp-vgmstream.vcxproj @@ -0,0 +1,124 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {49AF76F7-CBA0-4486-9DDF-51F30DF45F33} + xmpvgmstream + + + + DynamicLibrary + v140_xp + MultiByte + true + + + DynamicLibrary + v140_xp + MultiByte + + + + + + + + + + + + + <_ProjectFileVersion>14.0.23107.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + ../ext_includes;../../qaac/mp4v2/include;../../fdk-aac/libSYS/include;../../fdk-aac/libAACdec/include + true + EnableFastChecks + MultiThreadedDebug + Level3 + EditAndContinue + NoExtensions + + + ../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/at3plusdecoder.lib;%(AdditionalDependencies) + true + MachineX86 + Windows + xmpin.def + + + "$(ProjectDir)genversion.bat" "$(ProjectDir)version.h" VERSION + + + + + MaxSpeed + true + MultiThreaded + true + Level3 + ProgramDatabase + NoExtensions + ../ext_includes;../../qaac/mp4v2/include;../../fdk-aac/libSYS/include;../../fdk-aac/libAACdec/include + + + true + true + true + MachineX86 + ../ext_libs/libvorbis.lib;../ext_libs/libmpg123-0.lib;../ext_libs/libg7221_decode.lib;../ext_libs/libg719_decode.lib;../ext_libs/at3plusdecoder.lib;%(AdditionalDependencies) + Windows + xmpin.def + + + "$(ProjectDir)genversion.bat" "$(ProjectDir)version.h" VERSION + + + + + + + + {308e2ad5-be31-4770-9441-a8d50f56895c} + + + {86a064e2-c81b-4eee-8be0-a39a2e7c7c76} + + + {10e6bfc6-1e5b-46e4-ba42-f04dfbd0abff} + + + {54a6ad11-5369-4895-a06f-e255abb99b11} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/xmp-vgmstream/xmp_in.h b/xmp-vgmstream/xmp_in.h deleted file mode 100644 index 2eb9e54d..00000000 --- a/xmp-vgmstream/xmp_in.h +++ /dev/null @@ -1,110 +0,0 @@ -// XMPlay input plugin header (c) 2004-2005 Ian Luck -// new plugins can be submitted to plugins@xmplay.com - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef XMPIN_FACE -#define XMPIN_FACE 1 // "face" -#endif - -typedef void *XMPFILE; - -#define XMPIN_FLAG_CANSTREAM 1 // can stream files (play while downloading from the 'net) -#define XMPIN_FLAG_OWNFILE 2 // can process files without "XMPFILE" routines -#define XMPIN_FLAG_NOXMPFILE 4 // never use "XMPFILE" routines (implies XMPIN_FLAG_OWNFILE) - -// Note all texts are UTF-8 on WinNT based systems, and ANSI on Win9x -#define Utf2Uni(src,slen,dst,dlen) MultiByteToWideChar(CP_UTF8,0,src,slen,dst,dlen) // convert UTF-8 to Unicode - -typedef struct { - DWORD flags; // XMPIN_FLAG_xxx - char *name; // plugin name - char *exts; // supported file extensions (description\0ext1/ext2/etc...) - - void (WINAPI *About)(HWND win); // (OPTIONAL) - void (WINAPI *Config)(HWND win); // present config options to user (OPTIONAL) - BOOL (WINAPI *CheckFile)(char *filename, BYTE *buf, DWORD length); // verify file - BOOL (WINAPI *GetFileInfo)(char *filename, XMPFILE file, float *length, char *tags[7]); // get track info - //tags: 0=title,1=artist,2=album,3=year,4=track,5=genre,6=comment - - // playback stuff - DWORD (WINAPI *Open)(char *filename, XMPFILE file); // open a file - void (WINAPI *Close)(); // close file - DWORD (WINAPI *GetFormat)(DWORD *chan, DWORD *res); // return sample rate (OPTIONAL: mutually exclusive with SetFormat) - void (WINAPI *SetFormat)(DWORD rate, DWORD chan); // (OPTIONAL: mutually exclusive with GetFormat) - BOOL (WINAPI *GetTags)(char *tags[7]); // get title elements, return TRUE to delay (OPTIONAL) - void (WINAPI *GetInfoText)(char *format, char *length); // get main panel info text - void (WINAPI *GetGeneralInfo)(char *buf); // get General info window text - void (WINAPI *GetMessage)(char *buf); // get Message info text (OPTIONAL) - double (WINAPI *SetPosition)(DWORD pos); // seek (pos=0x800000nn=subsong nn) - double (WINAPI *GetGranularity)(); // seeking granularity - DWORD (WINAPI *GetBuffering)(); // get buffering progress (OPTIONAL) - DWORD (WINAPI *Process)(float *buf, DWORD count); // decode some sample data - BOOL (WINAPI *WriteFile)(char *filename); // write file to disk (OPTIONAL) - -#if XMPIN_FACE>=1 // "face 1" additions - void (WINAPI *GetSamples)(char *buf); // get Samples info text (OPTIONAL) - DWORD (WINAPI *GetSubSongs)(float *length); // get number (and total length) of sub-songs (OPTIONAL) -#endif -} XMPIN; - - -#define XMPFILE_TYPE_MEMORY 0 // file in memory -#define XMPFILE_TYPE_FILE 1 // local file -#define XMPFILE_TYPE_NETFILE 2 // file on the 'net -#define XMPFILE_TYPE_NETSTREAM 3 // 'net stream (indeterminate length) - -#define XMPCONFIG_NET_BUFFER 0 -#define XMPCONFIG_NET_RESTRICT 1 -#define XMPCONFIG_NET_RECONNECT 2 -#define XMPCONFIG_NET_NOPROXY 3 - -#define XMPINFO_MAIN 1 // main window info area -#define XMPINFO_GENERAL 2 // General info window -#define XMPINFO_MESSAGE 4 // Message info window -#define XMPINFO_SAMPLES 8 // Samples info window - -typedef struct { - struct { // file functions - DWORD (WINAPI *GetType)(XMPFILE file); // return XMPFILE_TYPE_xxx - DWORD (WINAPI *GetSize)(XMPFILE file); // file size - void *(WINAPI *GetMemory)(XMPFILE file); // memory location (XMPFILE_TYPE_MEMORY) - DWORD (WINAPI *Read)(XMPFILE file, void *buf, DWORD len); // read from file - BOOL (WINAPI *Seek)(XMPFILE file, DWORD pos); // seek in file - DWORD (WINAPI *Tell)(XMPFILE file); // get current file pos - // net-only stuff - void (WINAPI *NetSetRate)(XMPFILE file, DWORD rate); // set bitrate in bytes/sec (decides buffer size) - BOOL (WINAPI *NetIsActive)(XMPFILE file); // connection is still up? - BOOL (WINAPI *NetPreBuf)(XMPFILE file); // pre-buffer data - DWORD (WINAPI *NetAvailable)(XMPFILE file); // get amount of data ready to go -#if XMPIN_FACE>=1 - // additional file opening stuff - XMPFILE (WINAPI *Open)(char *filename); // open a file (local file/archive only, no 'net) - void (WINAPI *Close)(XMPFILE file); // close an opened file -#endif - } file; - - struct { // tag functions - return new strings in native form (UTF8/ANSI) - char *(WINAPI *Ansi)(char *tag, int len); // ANSI string (len=-1=null terminated) - char *(WINAPI *Unicode)(WCHAR *tag, int len); // Unicode string - char *(WINAPI *Utf8)(char *tag, int len); // UTF-8 string - } tag; - - DWORD (WINAPI *GetConfig)(DWORD option); // get a config (XMPCONFIG_xxx) value - BOOL (WINAPI *CheckCancel)(); // user wants to cancel? - void (WINAPI *SetLength)(float length, BOOL seekable); // set track length (-1=unchanged) and if it's seekable - void (WINAPI *SetGain)(DWORD mode, float gain); // set replaygain (mode 0=track, 1=album) - BOOL (WINAPI *UpdateTitle)(char *track); // set track title (NULL=refresh tags/title) -#if XMPIN_FACE>=1 - void (WINAPI *RefreshInfo)(DWORD mode); // refresh info displays (XMPINFO_xxx flags) -#endif -} XMPIN_FUNCS; - - -#ifdef __cplusplus -} -#endif diff --git a/xmp-vgmstream/xmpfunc.h b/xmp-vgmstream/xmpfunc.h new file mode 100644 index 00000000..8ac1759e --- /dev/null +++ b/xmp-vgmstream/xmpfunc.h @@ -0,0 +1,177 @@ +// XMPlay plugin functions header +// new plugins can be submitted to plugins@xmplay.com + +#pragma once + +#include + +typedef unsigned __int64 QWORD; + +#ifdef __cplusplus +extern "C" { +#endif + +// Note all texts are UTF-8 on WinNT based systems and ANSI on Win9x +#define Utf2Uni(src,slen,dst,dlen) MultiByteToWideChar(CP_UTF8,0,src,slen,dst,dlen) // convert UTF-8 to Windows Unicode/WideChar + +typedef void *(WINAPI *InterfaceProc)(DWORD face); // XMPlay interface retrieval function received by plugin + +#define XMPFUNC_MISC_FACE 0 // miscellaneous functions (XMPFUNC_MISC) +#define XMPFUNC_REGISTRY_FACE 1 // registry functions (XMPFUNC_REGISTRY) +#define XMPFUNC_FILE_FACE 2 // file functions (XMPFUNC_FILE) +#define XMPFUNC_TEXT_FACE 3 // text functions (XMPFUNC_TEXT) +#define XMPFUNC_STATUS_FACE 4 // playback status functions (XMPFUNC_STATUS) + +#define XMPCONFIG_NET_BUFFER 0 +#define XMPCONFIG_NET_RESTRICT 1 +#define XMPCONFIG_NET_RECONNECT 2 +#define XMPCONFIG_NET_PROXY 3 +#define XMPCONFIG_NET_PROXYCONF 4 // pointer to string +#define XMPCONFIG_NET_TIMEOUT 5 +#define XMPCONFIG_NET_PREBUF 6 +#define XMPCONFIG_OUTPUT 7 // pointer to XMPFORMAT (version 3.6) + +#define XMPINFO_TEXT_GENERAL 0 // General info text +#define XMPINFO_TEXT_MESSAGE 1 // Message info text +#define XMPINFO_TEXT_SAMPLES 2 // Samples info text + +#define XMPINFO_REFRESH_MAIN 1 // main window info area +#define XMPINFO_REFRESH_GENERAL 2 // General info window +#define XMPINFO_REFRESH_MESSAGE 4 // Message info window +#define XMPINFO_REFRESH_SAMPLES 8 // Samples info window + +typedef void *XMPFILE; + +#define XMPFILE_TYPE_MEMORY 0 // file in memory +#define XMPFILE_TYPE_FILE 1 // local file +#define XMPFILE_TYPE_NETFILE 2 // file on the 'net +#define XMPFILE_TYPE_NETSTREAM 3 // 'net stream (unknown length) + +typedef void (WINAPI *XMPSHORTCUTPROC)(); +typedef void (WINAPI *XMPSHORTCUTPROCEX)(DWORD id); + +typedef struct { + DWORD id; // must be unique and >=0x10000 + const char *text; // description + union { // handler + XMPSHORTCUTPROC proc; + XMPSHORTCUTPROCEX procex; // if id&0x80000000 + }; +} XMPSHORTCUT; + +typedef struct { + DWORD rate; // sample rate + DWORD chan; // channels + DWORD res; // bytes per sample (1=8-bit,2=16-bit,3=24-bit,4=float,0=undefined) +} XMPFORMAT; + +typedef struct { + float time; // cue position + const char *title; + const char *performer; +} XMPCUE; + +#define TAG_FORMATTED_TITLE (char*)-1 // formatted track title +#define TAG_FILENAME (char*)-2 // filename +#define TAG_TRACK_TITLE (char*)-3 // stream track (or CUE sheet) title +#define TAG_LENGTH (char*)-4 // length in seconds +#define TAG_SUBSONGS (char*)-5 // subsong count +#define TAG_SUBSONG (char*)-6 // separated subsong (number/total) +#define TAG_RATING (char*)-7 // user rating +#define TAG_TITLE (char*)0 // = "title" +#define TAG_ARTIST (char*)1 // = "artist" +#define TAG_ALBUM (char*)2 // = "album" +#define TAG_DATE (char*)3 // = "date" +#define TAG_TRACK (char*)4 // = "title" +#define TAG_GENRE (char*)5 // = "genre" +#define TAG_COMMENT (char*)6 // = "comment" +#define TAG_FILETYPE (char*)7 // = "filetype" + +/* + Non-"const" pointers returned by these functions should be freed via XMPFUNC_MISC:Free when done with them. +*/ + +typedef struct { // miscellaneous functions + DWORD (WINAPI *GetVersion)(); // get XMPlay version (eg. 0x03040001 = 3.4.0.1) + HWND (WINAPI *GetWindow)(); // get XMPlay window handle + void *(WINAPI *Alloc)(DWORD len); // allocate memory + void *(WINAPI *ReAlloc)(void *mem, DWORD len); // re-allocate memory + void (WINAPI *Free)(void *mem); // free allocated memory/text + BOOL (WINAPI *CheckCancel)(); // user wants to cancel? + DWORD (WINAPI *GetConfig)(DWORD option); // get a config (XMPCONFIG_xxx) value + const char *(WINAPI *GetSkinConfig)(const char *name); // get a skinconfig value + void (WINAPI *ShowBubble)(const char *text, DWORD time); // show a help bubble (time in ms, 0=default) + void (WINAPI *RefreshInfo)(DWORD mode); // refresh info displays (XMPINFO_REFRESH_xxx flags) + char *(WINAPI *GetInfoText)(DWORD mode); // get info window text (XMPINFO_TEXT_xxx) + char *(WINAPI *FormatInfoText)(char *buf, const char *name, const char *value); // format text for info window (tabs & new-lines) + char *(WINAPI *GetTag)(const char *tag); // get a current track's tag (tag name or TAG_xxx) + BOOL (WINAPI *RegisterShortcut)(const XMPSHORTCUT *cut); // add a shortcut + BOOL (WINAPI *PerformShortcut)(DWORD id); // perform a shortcut action +// version 3.4.0.14 + const XMPCUE *(WINAPI *GetCue)(DWORD cue); // get a cue entry (0=image, 1=1st track) +// version 3.8 + BOOL (WINAPI *DDE)(const char *command); // execute a DDE command without using DDE +} XMPFUNC_MISC; + +typedef struct { // "registry" functions + DWORD (WINAPI *Get)(const char *section, const char *key, void *data, DWORD size); // if data=NULL, required size is returned + DWORD (WINAPI *GetString)(const char *section, const char *key, char *data, DWORD size); // if data=NULL, required size is returned + BOOL (WINAPI *GetInt)(const char *section, const char *key, int *data); + BOOL (WINAPI *Set)(const char *section, const char *key, const void *data, DWORD size); // data=NULL = delete key + BOOL (WINAPI *SetString)(const char *section, const char *key, const char *data); + BOOL (WINAPI *SetInt)(const char *section, const char *key, const int *data); +} XMPFUNC_REGISTRY; + +typedef struct { // file functions + XMPFILE (WINAPI *Open)(const char *filename); // open a file + XMPFILE (WINAPI *OpenMemory)(const void *buf, DWORD len); // open a file from memory + void (WINAPI *Close)(XMPFILE file); // close an opened file + DWORD (WINAPI *GetType)(XMPFILE file); // return XMPFILE_TYPE_xxx + DWORD (WINAPI *GetSize)(XMPFILE file); // file size + const char *(WINAPI *GetFilename)(XMPFILE file); // filename + const void *(WINAPI *GetMemory)(XMPFILE file); // memory location (XMPFILE_TYPE_MEMORY) + DWORD (WINAPI *Read)(XMPFILE file, void *buf, DWORD len); // read from file + BOOL (WINAPI *Seek)(XMPFILE file, DWORD pos); // seek in file + DWORD (WINAPI *Tell)(XMPFILE file); // get current file pos + // net-only stuff + void (WINAPI *NetSetRate)(XMPFILE file, DWORD rate); // set bitrate in bytes/sec (decides buffer size) + BOOL (WINAPI *NetIsActive)(XMPFILE file); // connection is still up? + BOOL (WINAPI *NetPreBuf)(XMPFILE file); // pre-buffer data + DWORD (WINAPI *NetAvailable)(XMPFILE file); // get amount of data ready to go + + char *(WINAPI *ArchiveList)(XMPFILE file); // get archive contents (series of NULL-terminated entries) + XMPFILE (WINAPI *ArchiveExtract)(XMPFILE file, const char *entry, DWORD len); // decompress file from archive +} XMPFUNC_FILE; + +typedef struct { // text functions - return new string in native form (UTF-8/ANSI) + char *(WINAPI *Ansi)(const char *text, int len); // ANSI string (len=-1=null terminated) + char *(WINAPI *Unicode)(const WCHAR *text, int len); // Unicode string + char *(WINAPI *Utf8)(const char *text, int len); // UTF-8 string +} XMPFUNC_TEXT; + +typedef struct { // playback status functions + BOOL (WINAPI *IsPlaying)(); // playing? + double (WINAPI *GetTime)(); // track position in seconds + QWORD (WINAPI *GetWritten)(); // samples written to output + DWORD (WINAPI *GetLatency)(); // samples in output buffer + const XMPFORMAT *(WINAPI *GetFormat)(BOOL in); // get input/output sample format +} XMPFUNC_STATUS; + +// The following Winamp messages are also supported by XMPlay (see Winamp SDK for descriptions) +#define WM_WA_IPC WM_USER +#define IPC_DELETE 101 +#define IPC_STARTPLAY 102 +#define IPC_ISPLAYING 104 +#define IPC_GETOUTPUTTIME 105 +#define IPC_JUMPTOTIME 106 +#define IPC_SETPLAYLISTPOS 121 +#define IPC_SETVOLUME 122 +#define IPC_SETPANNING 123 +#define IPC_GETLISTLENGTH 124 +#define IPC_GETLISTPOS 125 +#define IPC_GETPLAYLISTFILE 211 +#define IPC_GETPLAYLISTTITLE 212 + +#ifdef __cplusplus +} +#endif diff --git a/xmp-vgmstream/xmpin.def b/xmp-vgmstream/xmpin.def new file mode 100644 index 00000000..c16eaf46 --- /dev/null +++ b/xmp-vgmstream/xmpin.def @@ -0,0 +1,2 @@ +EXPORTS + XMPIN_GetInterface \ No newline at end of file diff --git a/xmp-vgmstream/xmpin.h b/xmp-vgmstream/xmpin.h new file mode 100644 index 00000000..891debac --- /dev/null +++ b/xmp-vgmstream/xmpin.h @@ -0,0 +1,112 @@ +// XMPlay input plugin header +// new plugins can be submitted to plugins@xmplay.com + +#pragma once + +#include "xmpfunc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XMPIN_FACE +#define XMPIN_FACE 4 // "face" +#endif + +#define XMPIN_FLAG_CANSTREAM 1 // can stream files (play while downloading from the 'net) +#define XMPIN_FLAG_OWNFILE 2 // can process files without "XMPFILE" routines +#define XMPIN_FLAG_NOXMPFILE 4 // never use "XMPFILE" routines (implies XMPIN_FLAG_OWNFILE) +#define XMPIN_FLAG_LOOP 8 // custom looping +#define XMPIN_FLAG_TAIL 16 // output tail (decay/fadeout) +#define XMPIN_FLAG_CONFIG 64 // can save config +#define XMPIN_FLAG_LOOPSOUND 128 // allow "auto-loop any track ending with sound" with XMPIN_FLAG_LOOP + +// SetPosition special positions +#define XMPIN_POS_LOOP -1 // loop +#define XMPIN_POS_AUTOLOOP -2 // auto-loop +#define XMPIN_POS_TAIL -3 // output tail (decay/fadeout) +#define XMPIN_POS_SUBSONG 0x80000000 // subsong (LOWORD=subsong) +#define XMPIN_POS_SUBSONG1 0x40000000 // single subsong mode (don't show info on other subsongs), used with XMPIN_POS_SUBSONG + +// VisRender/DC flags +#define XMPIN_VIS_INIT 1 // DC/buffer is uninitialized +#define XMPIN_VIS_FULL 2 // fullscreen +#define XMPIN_VIS_MAIN 4 // main window + +// GetFileInfo return flags +#define XMPIN_INFO_NOSUBTAGS 0x10000 // subsongs don't have their own tags + +typedef struct { + DWORD flags; // XMPIN_FLAG_xxx + const char *name; // plugin name + const char *exts; // supported file extensions (description\0ext1/ext2/etc) + + void (WINAPI *About)(HWND win); // (OPTIONAL) + void (WINAPI *Config)(HWND win); // present config options to user (OPTIONAL) + BOOL (WINAPI *CheckFile)(const char *filename, XMPFILE file); // verify file +#if XMPIN_FACE==4 + DWORD (WINAPI *GetFileInfo)(const char *filename, XMPFILE file, float **length, char **tags); // get track info + // only the following tags are currently used by XMPlay (rest is ignored): title, artist, album, date, track/tracknumber, genre, comment, filetype, cuesheet +#else + BOOL (WINAPI *GetFileInfo)(const char *filename, XMPFILE file, float *length, char *tags[8]); // get track info +#endif + + // playback stuff + DWORD (WINAPI *Open)(const char *filename, XMPFILE file); // open a file + void (WINAPI *Close)(); // close file + void *reserved1; + void (WINAPI *SetFormat)(XMPFORMAT *form); // set sample format + +#if XMPIN_FACE==4 + char *(WINAPI *GetTags)(); // get tags, return NULL to delay (OPTIONAL) + // XMPlay uses the same tags as with GetFileInfo, but other tags are available via XMPFUNC_MISC:GetTag +#else + BOOL (WINAPI *GetTags)(char *tags[8]); // get title elements, return TRUE to delay (OPTIONAL) +#endif + void (WINAPI *GetInfoText)(char *format, char *length); // get main panel info text + void (WINAPI *GetGeneralInfo)(char *buf); // get General info window text (buf is ~40K) + void (WINAPI *GetMessage)(char *buf); // get Message info text (OPTIONAL) + double (WINAPI *SetPosition)(DWORD pos); // seek + double (WINAPI *GetGranularity)(); // seeking granularity (OPTIONAL, default=milliseconds) + DWORD (WINAPI *GetBuffering)(); // get buffering progress (OPTIONAL) + DWORD (WINAPI *Process)(float *buf, DWORD count); // decode some sample data + BOOL (WINAPI *WriteFile)(const char *filename); // write file to disk (OPTIONAL) + + void (WINAPI *GetSamples)(char *buf); // get Samples info text (OPTIONAL) + DWORD (WINAPI *GetSubSongs)(float *length); // get number (and total length) of sub-songs (OPTIONAL) +#if XMPIN_FACE==4 + void *reserved3; +#else + char *(WINAPI *GetCues)(); // get CUE sheet (OPTIONAL) +#endif + + float (WINAPI *GetDownloaded)(); // get download progress (OPTIONAL) + + // vis stuff (all OPTIONAL) + const char *visname; // visualisation name + BOOL (WINAPI *VisOpen)(DWORD colors[3]); // initialize vis + void (WINAPI *VisClose)(); // close vis + void (WINAPI *VisSize)(HDC dc, SIZE *size); // get ideal vis dimensions + BOOL (WINAPI *VisRender)(DWORD *buf, SIZE size, DWORD flags); // render vis + BOOL (WINAPI *VisRenderDC)(HDC dc, SIZE size, DWORD flags); // render vis + void (WINAPI *VisButton)(DWORD x, DWORD y); // mouse click in vis + + void *reserved2; + + DWORD (WINAPI *GetConfig)(void *config); // get config (return size of config data) (OPTIONAL) + void (WINAPI *SetConfig)(void *config, DWORD size); // apply config (OPTIONAL) +} XMPIN; + +#define XMPFUNC_IN_FACE 11 + +typedef struct { + void (WINAPI *SetLength)(float length, BOOL seekable); // set track length (-1=unchanged) and if it's seekable + void (WINAPI *SetGain)(DWORD mode, float gain); // set replaygain (mode 0=track, 1=album, 2=peak) + BOOL (WINAPI *UpdateTitle)(const char *track); // set track title (NULL=refresh tags/title) +// version 3.8 + BOOL (WINAPI *GetLooping)(); // looping is enabled? (TRUE=stop processing at loop point, FALSE=continue to end) +} XMPFUNC_IN; + +#ifdef __cplusplus +} +#endif