mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 06:50:20 +01:00
plugin cleanup in preparation of future changes; fix FILE handle leaks
This commit is contained in:
parent
321c617bfa
commit
68d3f43868
@ -23,147 +23,199 @@
|
|||||||
#define VERSION "(unknown version)"
|
#define VERSION "(unknown version)"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* ************************************* */
|
||||||
|
|
||||||
|
/* XMPlay function library */
|
||||||
static XMPFUNC_IN *xmpfin;
|
static XMPFUNC_IN *xmpfin;
|
||||||
static XMPFUNC_MISC *xmpfmisc;
|
static XMPFUNC_MISC *xmpfmisc;
|
||||||
static XMPFUNC_FILE *xmpffile;
|
static XMPFUNC_FILE *xmpffile;
|
||||||
|
|
||||||
/* ************************************* */
|
/* XMPlay extension list, only needed to associate extensions in Windows */
|
||||||
|
/* todo: as of v3.8.2.17, any more than ~1000 will crash XMplay's file list screen (but not using the non-native Winamp plugin...) */
|
||||||
|
#define EXTENSION_LIST_SIZE 1000 /*VGM_EXTENSION_LIST_CHAR_SIZE * 2*/
|
||||||
|
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
||||||
|
|
||||||
typedef struct _XMPSTREAMFILE {
|
/* plugin config */
|
||||||
STREAMFILE sf;
|
double fade_seconds = 10.0;
|
||||||
XMPFILE file;
|
double fade_delay_seconds = 10.0;
|
||||||
off_t offset;
|
double loop_count = 2.0;
|
||||||
char name[PATH_LIMIT];
|
|
||||||
} XMPSTREAMFILE;
|
|
||||||
|
|
||||||
static void xmpsf_seek(XMPSTREAMFILE *this, off_t offset) {
|
/* plugin state */
|
||||||
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)
|
|
||||||
{
|
|
||||||
// The line below is what Causes this Plugin to Crash. Credits to Ian Luck to Finding this issue.
|
|
||||||
// This closes the internal XMPFILE, which must be done by XMPlay instead.
|
|
||||||
// However vgmtream sometimes opens its own files, so it may be leaking handles.
|
|
||||||
//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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Probably not needed at all.
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************* */
|
|
||||||
|
|
||||||
/* internal state */
|
|
||||||
VGMSTREAM * vgmstream = NULL;
|
VGMSTREAM * vgmstream = NULL;
|
||||||
int32_t totalFrames, framesDone, framesLength, framesFade;
|
int framesDone, framesLength;
|
||||||
|
int stream_length_samples = 0;
|
||||||
|
int fade_samples = 0;
|
||||||
|
|
||||||
#define APP_NAME "vgmstream plugin" //unused?
|
static int shownerror = 0; /* init error */
|
||||||
|
|
||||||
void __stdcall XMP_About(HWND hwParent) {
|
/* ************************************* */
|
||||||
MessageBox(hwParent,
|
|
||||||
|
/* a STREAMFILE that operates via XMPlay's XMPFUNC_FILE+XMPFILE */
|
||||||
|
typedef struct _XMPLAY_STREAMFILE {
|
||||||
|
STREAMFILE sf; /* callbacks */
|
||||||
|
XMPFILE infile; /* actual FILE */
|
||||||
|
char name[PATH_LIMIT];
|
||||||
|
off_t offset; /* current offset */
|
||||||
|
int internal_xmpfile; /* infile was not supplied externally and can be closed */
|
||||||
|
} XMPLAY_STREAMFILE;
|
||||||
|
|
||||||
|
static STREAMFILE *open_xmplay_streamfile_by_xmpfile(XMPFILE file, const char *path, int internal);
|
||||||
|
|
||||||
|
static size_t xmpsf_read(XMPLAY_STREAMFILE *this, uint8_t *dest, off_t offset, size_t length) {
|
||||||
|
size_t read;
|
||||||
|
|
||||||
|
if (this->offset != offset) {
|
||||||
|
if (xmpffile->Seek(this->infile, offset))
|
||||||
|
this->offset = offset;
|
||||||
|
else
|
||||||
|
this->offset = xmpffile->Tell(this->infile);
|
||||||
|
}
|
||||||
|
|
||||||
|
read = xmpffile->Read(this->infile, dest, length);
|
||||||
|
if (read > 0)
|
||||||
|
this->offset += read;
|
||||||
|
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
static off_t xmpsf_get_size(XMPLAY_STREAMFILE *this) {
|
||||||
|
return xmpffile->GetSize(this->infile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static off_t xmpsf_get_offset(XMPLAY_STREAMFILE *this) {
|
||||||
|
return xmpffile->Tell(this->infile);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xmpsf_get_name(XMPLAY_STREAMFILE *this, char *buffer, size_t length) {
|
||||||
|
strncpy(buffer, this->name, length);
|
||||||
|
buffer[length-1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static STREAMFILE *xmpsf_open(XMPLAY_STREAMFILE *this, const char *const filename, size_t buffersize) {
|
||||||
|
XMPFILE newfile;
|
||||||
|
|
||||||
|
if (!filename)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
newfile = xmpffile->Open(filename);
|
||||||
|
if (!newfile) return NULL;
|
||||||
|
|
||||||
|
return open_xmplay_streamfile_by_xmpfile(newfile, filename, 1); /* internal XMPFILE */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xmpsf_close(XMPLAY_STREAMFILE *this) {
|
||||||
|
/* Close XMPFILE, but only if we opened it (ex. for subfiles inside metas).
|
||||||
|
* Otherwise must be left open as other parts of XMPlay need it and would crash. */
|
||||||
|
if (this->internal_xmpfile) {
|
||||||
|
xmpffile->Close(this->infile);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STREAMFILE *open_xmplay_streamfile_by_xmpfile(XMPFILE infile, const char *path, int internal) {
|
||||||
|
XMPLAY_STREAMFILE *streamfile = calloc(1,sizeof(XMPLAY_STREAMFILE));
|
||||||
|
if (!streamfile) return NULL;
|
||||||
|
|
||||||
|
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->infile = infile;
|
||||||
|
streamfile->offset = 0;
|
||||||
|
strncpy(streamfile->name, path, sizeof(streamfile->name));
|
||||||
|
|
||||||
|
streamfile->internal_xmpfile = internal;
|
||||||
|
|
||||||
|
return &streamfile->sf; /* pointer to STREAMFILE start = rest of the custom data follows */
|
||||||
|
}
|
||||||
|
|
||||||
|
VGMSTREAM *init_vgmstream_xmplay(XMPFILE file, const char *path) {
|
||||||
|
STREAMFILE *streamfile = NULL;
|
||||||
|
VGMSTREAM *vgmstream = NULL;
|
||||||
|
|
||||||
|
streamfile = open_xmplay_streamfile_by_xmpfile(file, path, 0); /* external XMPFILE */
|
||||||
|
if (!streamfile) return NULL;
|
||||||
|
|
||||||
|
vgmstream = init_vgmstream_from_STREAMFILE(streamfile);
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
|
return vgmstream;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
xmpsf_close((XMPLAY_STREAMFILE *)streamfile);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************* */
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/* get the tags as an array of "key\0value\0", NULL-terminated */
|
||||||
|
static char *get_tags(VGMSTREAM * infostream) {
|
||||||
|
char *tags;
|
||||||
|
size_t tag_number = 20; // ?
|
||||||
|
|
||||||
|
tags = (char*)xmpfmisc->Alloc(tag_number+1);
|
||||||
|
|
||||||
|
for (...) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[tag_number]=0; // terminating NULL
|
||||||
|
return tags; /* assuming XMPlay free()s this, since it Alloc()s it */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Adds ext to XMPlay's extension list. */
|
||||||
|
static int add_extension(int length, char * dst, const char * ext) {
|
||||||
|
int ext_len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (length <= 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ext_len = strlen(ext);
|
||||||
|
|
||||||
|
/* check if end reached or not enough room to add */
|
||||||
|
if (ext_len+2 > length-2) {
|
||||||
|
dst[0]='\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy new extension + null terminate */
|
||||||
|
for (i=0; i < ext_len; i++)
|
||||||
|
dst[i] = ext[i];
|
||||||
|
dst[i]='/';
|
||||||
|
dst[i+1]='\0';
|
||||||
|
return i+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Creates XMPlay's extension list, a single string with 2 nulls.
|
||||||
|
* Extensions must be in this format: "Description\0extension1/.../extensionN" */
|
||||||
|
static void build_extension_list() {
|
||||||
|
const char ** ext_list;
|
||||||
|
int ext_list_len;
|
||||||
|
int i, written;
|
||||||
|
|
||||||
|
written = sprintf(working_extension_list, "%s%c", "vgmstream files",'\0');
|
||||||
|
|
||||||
|
ext_list = vgmstream_get_formats();
|
||||||
|
ext_list_len = vgmstream_get_formats_length();
|
||||||
|
|
||||||
|
for (i=0; i < ext_list_len; i++) {
|
||||||
|
written += add_extension(EXTENSION_LIST_SIZE-written, working_extension_list + written, ext_list[i]);
|
||||||
|
}
|
||||||
|
working_extension_list[written-1] = '\0'; /* remove last "/" */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************* */
|
||||||
|
|
||||||
|
/* info for the "about" button in plugin options */
|
||||||
|
void WINAPI xmplay_About(HWND win) {
|
||||||
|
MessageBox(win,
|
||||||
"vgmstream plugin " VERSION " " __DATE__ "\n"
|
"vgmstream plugin " VERSION " " __DATE__ "\n"
|
||||||
"by hcs, FastElbja, manakoAT, bxaimc, snakemeat, soneek, kode54, bnnm and many others\n"
|
"by hcs, FastElbja, manakoAT, bxaimc, snakemeat, soneek, kode54, bnnm and many others\n"
|
||||||
"\n"
|
"\n"
|
||||||
@ -174,177 +226,102 @@ void __stdcall XMP_About(HWND hwParent) {
|
|||||||
,"about xmp-vgmstream",MB_OK);
|
,"about xmp-vgmstream",MB_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __stdcall XMP_Close() {
|
#if 0
|
||||||
close_vgmstream(vgmstream);
|
/* present config options to user (OPTIONAL) */
|
||||||
vgmstream = NULL;
|
void WINAPI xmplay_Config(HWND win) {
|
||||||
|
/* defined in resource.rc */
|
||||||
|
DialogBox(input_module.hDllInstance, (const char *)IDD_CONFIG, win, configDlgProc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* check if a file is playable by this plugin */
|
||||||
|
BOOL WINAPI xmplay_CheckFile(const char *filename, XMPFILE file) {
|
||||||
|
VGMSTREAM* infostream = NULL;
|
||||||
|
if (file)
|
||||||
|
infostream = init_vgmstream_xmplay(file, filename);
|
||||||
|
else
|
||||||
|
infostream = init_vgmstream(filename); //TODO: unicode problems?
|
||||||
|
if (!infostream)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
close_vgmstream(infostream);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL __stdcall XMP_CheckFile(const char *filename, XMPFILE file) {
|
/* update info from a file, returning the number of subsongs */
|
||||||
VGMSTREAM* thisvgmstream;
|
DWORD WINAPI xmplay_GetFileInfo(const char *filename, XMPFILE file, float **length, char **tags) {
|
||||||
int ret;
|
VGMSTREAM* infostream;
|
||||||
if (file) thisvgmstream = init_vgmstream_from_xmpfile(file, filename);
|
if (file)
|
||||||
else thisvgmstream = init_vgmstream(filename);
|
infostream = init_vgmstream_xmplay(file, filename);
|
||||||
|
else
|
||||||
|
infostream = init_vgmstream(filename); //TODO: unicode problems?
|
||||||
|
if (!infostream)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!thisvgmstream) ret = FALSE;
|
if (length && infostream->sample_rate) {
|
||||||
else { ret = TRUE; close_vgmstream(thisvgmstream); }
|
int stream_length_samples = get_vgmstream_play_samples(loop_count, fade_seconds, fade_delay_seconds, infostream);
|
||||||
|
float *lens = (float*)xmpfmisc->Alloc(sizeof(float));
|
||||||
|
lens[0] = (float)stream_length_samples / (float)infostream->sample_rate;
|
||||||
|
*length = lens;
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
close_vgmstream(infostream);
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __stdcall XMP_GetFileInfo(const char *filename, XMPFILE file, float **length, char **tags)
|
/* open a file for playback, returning: 0=failed, 1=success, 2=success and XMPlay can close the file */
|
||||||
{
|
DWORD WINAPI xmplay_Open(const char *filename, XMPFILE file) {
|
||||||
VGMSTREAM* thisvgmstream;
|
if (file)
|
||||||
if (file) thisvgmstream = init_vgmstream_from_xmpfile(file, filename);
|
vgmstream = init_vgmstream_xmplay(file, filename);
|
||||||
else thisvgmstream = init_vgmstream(filename);
|
else
|
||||||
if (!thisvgmstream) return 0;
|
vgmstream = init_vgmstream(filename);
|
||||||
|
if (!vgmstream)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (length && thisvgmstream->sample_rate)
|
framesDone = 0;
|
||||||
{
|
stream_length_samples = get_vgmstream_play_samples(loop_count, fade_seconds, fade_delay_seconds, vgmstream);
|
||||||
int totalFrames = get_vgmstream_play_samples(2.0, 10.0, 10.0, thisvgmstream);
|
fade_samples = (int)(fade_seconds * vgmstream->sample_rate);
|
||||||
float *lens = (float*)xmpfmisc->Alloc(sizeof(float));
|
framesLength = stream_length_samples - fade_samples;
|
||||||
lens[0] = (float)totalFrames / (float)thisvgmstream->sample_rate;
|
|
||||||
*length = lens;
|
|
||||||
}
|
|
||||||
|
|
||||||
close_vgmstream(thisvgmstream);
|
if (stream_length_samples) {
|
||||||
|
float length = (float)stream_length_samples / (float)vgmstream->sample_rate;
|
||||||
|
xmpfin->SetLength(length, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __stdcall XMP_Open(const char *filename, XMPFILE file) {
|
/* close the playback file */
|
||||||
if (file) vgmstream = init_vgmstream_from_xmpfile(file, filename);
|
void WINAPI xmplay_Close() {
|
||||||
else vgmstream = init_vgmstream(filename);
|
close_vgmstream(vgmstream);
|
||||||
|
vgmstream = NULL;
|
||||||
if (!vgmstream) return 0;
|
|
||||||
|
|
||||||
totalFrames = get_vgmstream_play_samples(2.0, 10.0, 10.0, vgmstream);
|
|
||||||
framesDone = 0;
|
|
||||||
framesFade = vgmstream->sample_rate * 10;
|
|
||||||
framesLength = totalFrames - framesFade;
|
|
||||||
|
|
||||||
if (totalFrames)
|
|
||||||
{
|
|
||||||
float length = (float)totalFrames / (float)vgmstream->sample_rate;
|
|
||||||
xmpfin->SetLength(length, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD __stdcall XMP_Process(float* buffer, DWORD bufsize) {
|
/* set the sample format */
|
||||||
INT16 buf[1024];
|
void WINAPI xmplay_SetFormat(XMPFORMAT *form) {
|
||||||
UINT32 i, j, todo, done;
|
form->res = 16 / 8; /* PCM 16 */
|
||||||
|
form->chan = vgmstream->channels;
|
||||||
BOOL doLoop = xmpfin->GetLooping();
|
form->rate = vgmstream->sample_rate;
|
||||||
|
|
||||||
float *sbuf = buffer;
|
|
||||||
|
|
||||||
UINT32 samplesTodo;
|
|
||||||
|
|
||||||
bufsize /= vgmstream->channels;
|
|
||||||
|
|
||||||
samplesTodo = doLoop ? bufsize : totalFrames - framesDone;
|
|
||||||
if (samplesTodo > 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __stdcall XMP_SetFormat(XMPFORMAT *form) {
|
/* get tags, return NULL to delay title update (OPTIONAL) */
|
||||||
form->res = 16 / 8;
|
char * WINAPI xmplay_GetTags() {
|
||||||
form->chan = vgmstream->channels;
|
return NULL; //get_tags(vgmstream);
|
||||||
form->rate = vgmstream->sample_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* main panel info text (short file info) */
|
/* main panel info text (short file info) */
|
||||||
void __stdcall XMP_GetInfoText(char* format, char* length) {
|
void WINAPI xmplay_GetInfoText(char* format, char* length) {
|
||||||
if (!format)
|
if (!format)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
sprintf(format,"vgmstream");
|
sprintf(format,"vgmstream");
|
||||||
/* length is the file time */
|
/* length is the file time */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* info for the "General" window/tab (buf is ~40K) */
|
/* info for the "General" window/tab (buf is ~40K) */
|
||||||
void __stdcall XMP_GetGeneralInfo(char* buf) {
|
void WINAPI xmplay_GetGeneralInfo(char* buf) {
|
||||||
int i;
|
int i;
|
||||||
char description[1024];
|
char description[1024];
|
||||||
|
|
||||||
@ -378,121 +355,187 @@ void __stdcall XMP_GetGeneralInfo(char* buf) {
|
|||||||
sprintf(buf,"vgmstream\t\r%s\r", description);
|
sprintf(buf,"vgmstream\t\r%s\r", description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* get the seeking granularity in seconds */
|
||||||
|
double WINAPI xmplay_GetGranularity() {
|
||||||
|
return 0.001; /* can seek in milliseconds */
|
||||||
|
}
|
||||||
|
|
||||||
static int add_extension(int length, char * dst, const char * src);
|
/* seek to a position (in granularity units), return new position or -1 = failed */
|
||||||
static void build_extension_list();
|
double WINAPI xmplay_SetPosition(DWORD pos) {
|
||||||
|
double cpos = (double)framesDone / (double)vgmstream->sample_rate;
|
||||||
|
double time = pos * xmplay_GetGranularity();
|
||||||
|
|
||||||
/* XMPlay extension list, only needed to associate extensions in Windows */
|
#if 0
|
||||||
/* todo: as of v3.8.2.17, any more than ~1000 will crash XMplay's file list screen (but not using the non-native Winamp plugin...) */
|
/* set a subsong */
|
||||||
#define EXTENSION_LIST_SIZE 1000 /*VGM_EXTENSION_LIST_CHAR_SIZE * 2*/
|
if (pos & XMPIN_POS_SUBSONG) {
|
||||||
char working_extension_list[EXTENSION_LIST_SIZE] = {0};
|
int subsong = LOWORD(pos);
|
||||||
|
|
||||||
/* plugin defs, see xmpin.h */
|
/* "single subsong mode (don't show info on other subsongs)" */
|
||||||
XMPIN vgmstream_intf = {
|
if (pos & XMPIN_POS_SUBSONG1) {
|
||||||
XMPIN_FLAG_CANSTREAM,
|
// ???
|
||||||
"vgmstream for XMPlay",
|
}
|
||||||
working_extension_list,
|
|
||||||
XMP_About,
|
// todo reopen vgmstream based on current with new subsong index
|
||||||
NULL,//XMP_Config
|
cpos = 0.0;
|
||||||
XMP_CheckFile,
|
}
|
||||||
XMP_GetFileInfo,
|
#endif
|
||||||
XMP_Open,
|
|
||||||
XMP_Close,
|
if (time < cpos) {
|
||||||
NULL,
|
reset_vgmstream(vgmstream);
|
||||||
XMP_SetFormat,
|
cpos = 0.0;
|
||||||
XMP_GetTags, //(OPTIONAL) --actually mandatory
|
}
|
||||||
XMP_GetInfoText,
|
|
||||||
XMP_GetGeneralInfo,
|
while (cpos < time) {
|
||||||
NULL,//GetMessage - text for the "Message" tab window/tab (OPTIONAL)
|
INT16 buffer[1024];
|
||||||
XMP_SetPosition,
|
long max_sample_count = 1024 / vgmstream->channels;
|
||||||
XMP_GetGranularity,
|
long samples_to_skip = (long)((time - cpos) * vgmstream->sample_rate);
|
||||||
NULL,
|
if (samples_to_skip > max_sample_count)
|
||||||
XMP_Process,
|
samples_to_skip = max_sample_count;
|
||||||
NULL,
|
if (!samples_to_skip)
|
||||||
NULL,
|
break;
|
||||||
NULL,
|
render_vgmstream(buffer, (int)samples_to_skip, vgmstream);
|
||||||
NULL,
|
cpos += (double)samples_to_skip / (double)vgmstream->sample_rate;
|
||||||
NULL,
|
}
|
||||||
NULL,
|
|
||||||
NULL,
|
framesDone = (int32_t)(cpos * vgmstream->sample_rate);
|
||||||
NULL,
|
|
||||||
NULL,
|
return cpos;
|
||||||
NULL,
|
}
|
||||||
NULL,
|
|
||||||
NULL,
|
/* decode some sample data */
|
||||||
NULL,
|
DWORD WINAPI xmplay_Process(float* buf, DWORD bufsize) {
|
||||||
NULL,
|
INT16 sample_buffer[1024];
|
||||||
NULL,
|
UINT32 i, j, todo, done;
|
||||||
|
|
||||||
|
BOOL doLoop = xmpfin->GetLooping();
|
||||||
|
float *sbuf = buf;
|
||||||
|
UINT32 samplesTodo;
|
||||||
|
|
||||||
|
bufsize /= vgmstream->channels;
|
||||||
|
|
||||||
|
samplesTodo = doLoop ? bufsize : stream_length_samples - framesDone;
|
||||||
|
if (samplesTodo > bufsize)
|
||||||
|
samplesTodo = bufsize;
|
||||||
|
|
||||||
|
/* decode */
|
||||||
|
done = 0;
|
||||||
|
while (done < samplesTodo) {
|
||||||
|
todo = 1024 / vgmstream->channels;
|
||||||
|
if (todo > samplesTodo - done)
|
||||||
|
todo = samplesTodo - done;
|
||||||
|
|
||||||
|
render_vgmstream(sample_buffer, todo, vgmstream);
|
||||||
|
|
||||||
|
for (i = 0, j = todo * vgmstream->channels; i < j; ++i) {
|
||||||
|
*sbuf++ = sample_buffer[i] * 1.0f / 32768.0f;
|
||||||
|
}
|
||||||
|
done += todo;
|
||||||
|
}
|
||||||
|
|
||||||
|
sbuf = buf;
|
||||||
|
|
||||||
|
/* fade */
|
||||||
|
if (!doLoop && framesDone + done > framesLength) {
|
||||||
|
long fadeStart = (framesLength > framesDone) ? framesLength : framesDone;
|
||||||
|
long fadeEnd = (framesDone + done) > stream_length_samples ? stream_length_samples : (framesDone + done);
|
||||||
|
long fadePos;
|
||||||
|
|
||||||
|
float fadeScale = (float)(stream_length_samples - fadeStart) / fade_samples;
|
||||||
|
float fadeStep = 1.0f / fade_samples;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* *********************************** */
|
||||||
|
|
||||||
|
/* main plugin def, see xmpin.h */
|
||||||
|
XMPIN vgmstream_xmpin = {
|
||||||
|
XMPIN_FLAG_CANSTREAM,
|
||||||
|
"vgmstream for XMPlay",
|
||||||
|
working_extension_list,
|
||||||
|
xmplay_About,
|
||||||
|
NULL,//XMP_Config
|
||||||
|
xmplay_CheckFile,
|
||||||
|
xmplay_GetFileInfo,
|
||||||
|
xmplay_Open,
|
||||||
|
xmplay_Close,
|
||||||
|
NULL,
|
||||||
|
xmplay_SetFormat,
|
||||||
|
xmplay_GetTags, //(OPTIONAL) --actually mandatory
|
||||||
|
xmplay_GetInfoText,
|
||||||
|
xmplay_GetGeneralInfo,
|
||||||
|
NULL,//GetMessage - text for the "Message" tab window/tab (OPTIONAL)
|
||||||
|
xmplay_SetPosition,
|
||||||
|
xmplay_GetGranularity,
|
||||||
|
NULL,
|
||||||
|
xmplay_Process,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int shownerror = 0;
|
/* get the plugin's XMPIN interface */
|
||||||
|
__declspec(dllexport) XMPIN* WINAPI XMPIN_GetInterface(UINT32 face, InterfaceProc faceproc) {
|
||||||
|
if (face != XMPIN_FACE) {
|
||||||
|
// unsupported version
|
||||||
|
if (face < XMPIN_FACE && !shownerror) {
|
||||||
|
MessageBox(0,
|
||||||
|
"The xmp-vgmstream plugin requires XMPlay 3.8 or above.\n\n"
|
||||||
|
"Please update at:\n"
|
||||||
|
"http://www.un4seen.com/xmplay.html\n"
|
||||||
|
"http://www.un4seen.com/stuff/xmplay.exe", 0, MB_ICONEXCLAMATION);
|
||||||
|
shownerror = 1;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
__declspec(dllexport) XMPIN* __stdcall XMPIN_GetInterface(UINT32 face, InterfaceProc faceproc)
|
xmpfin = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
|
||||||
{
|
xmpfmisc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
|
||||||
if (face != XMPIN_FACE)
|
xmpffile = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
|
||||||
{ // unsupported version
|
|
||||||
if (face<XMPIN_FACE && !shownerror)
|
|
||||||
{
|
|
||||||
//Replaced the message box below with a better one.
|
|
||||||
//MessageBox(0, "The XMP-vgmstream plugin requires XMPlay 3.8 or above", 0, MB_ICONEXCLAMATION);
|
|
||||||
MessageBox(0, "The xmp-vgmstream plugin requires XMPlay 3.8 or above. Please update at.\n\n http://www.un4seen.com/xmplay.html \n or at\n http://www.un4seen.com/stuff/xmplay.exe.", 0, MB_ICONEXCLAMATION);
|
|
||||||
shownerror = 1;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
xmpfin = (XMPFUNC_IN*)faceproc(XMPFUNC_IN_FACE);
|
|
||||||
xmpfmisc = (XMPFUNC_MISC*)faceproc(XMPFUNC_MISC_FACE);
|
|
||||||
xmpffile = (XMPFUNC_FILE*)faceproc(XMPFUNC_FILE_FACE);
|
|
||||||
|
|
||||||
build_extension_list();
|
build_extension_list();
|
||||||
|
|
||||||
return &vgmstream_intf;
|
return &vgmstream_xmpin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/**
|
// needed?
|
||||||
* Creates XMPlay's extension list, a single string with 2 nulls.
|
BOOL WINAPI DllMain(HINSTANCE hDLL, DWORD reason, LPVOID reserved) {
|
||||||
* Extensions must be in this format: "Description\0extension1/.../extensionN"
|
switch (reason) {
|
||||||
*/
|
case DLL_PROCESS_ATTACH:
|
||||||
static void build_extension_list() {
|
DisableThreadLibraryCalls(hDLL);
|
||||||
const char ** ext_list;
|
break;
|
||||||
int ext_list_len;
|
|
||||||
int i, written;
|
|
||||||
|
|
||||||
written = sprintf(working_extension_list, "%s%c", "vgmstream files",'\0');
|
|
||||||
|
|
||||||
ext_list = vgmstream_get_formats();
|
|
||||||
ext_list_len = vgmstream_get_formats_length();
|
|
||||||
|
|
||||||
for (i=0; i < ext_list_len; i++) {
|
|
||||||
written += add_extension(EXTENSION_LIST_SIZE-written, working_extension_list + written, ext_list[i]);
|
|
||||||
}
|
}
|
||||||
working_extension_list[written-1] = '\0'; /* remove last "/" */
|
return TRUE;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds ext to XMPlay's extension list.
|
|
||||||
*/
|
|
||||||
static int add_extension(int length, char * dst, const char * ext) {
|
|
||||||
int ext_len;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (length <= 1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ext_len = strlen(ext);
|
|
||||||
|
|
||||||
/* check if end reached or not enough room to add */
|
|
||||||
if (ext_len+2 > length-2) {
|
|
||||||
dst[0]='\0';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* copy new extension + null terminate */
|
|
||||||
for (i=0; i < ext_len; i++)
|
|
||||||
dst[i] = ext[i];
|
|
||||||
dst[i]='/';
|
|
||||||
dst[i+1]='\0';
|
|
||||||
return i+1;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user