b4e70422a3
* Get rid of the non-functional GT2 loader * Add the UMX loader * Add HQ mixer routines (and make it configurable) * Allow samplerate to be configured at run/playtime * Support >64KHz mixing/playback * Correctly restore non-boost status (The diff to upstream is much smaller now too!) Change-Id: Iaa4ac901ba9cd4123bb225656976e78271353a72
692 lines
14 KiB
C
692 lines
14 KiB
C
/* MikMod sound library
|
|
(c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
|
|
AUTHORS for complete list.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Library General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
*/
|
|
|
|
/*==============================================================================
|
|
|
|
$Id$
|
|
|
|
These routines are used to access the available module loaders
|
|
|
|
==============================================================================*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_MEMORY_H
|
|
#include <memory.h>
|
|
#endif
|
|
#include <string.h>
|
|
|
|
#include "mikmod_internals.h"
|
|
|
|
#ifdef SUNOS
|
|
extern int fprintf(FILE *, const char *, ...);
|
|
#endif
|
|
|
|
MREADER *modreader;
|
|
MODULE of;
|
|
|
|
static MLOADER *firstloader=NULL;
|
|
|
|
#ifndef NO_DEPACKERS
|
|
static MUNPACKER unpackers[] = {
|
|
PP20_Unpack,
|
|
MMCMP_Unpack,
|
|
XPK_Unpack,
|
|
S404_Unpack,
|
|
NULL
|
|
};
|
|
#endif
|
|
|
|
const UWORD finetune[16] = {
|
|
8363,8413,8463,8529,8581,8651,8723,8757,
|
|
7895,7941,7985,8046,8107,8169,8232,8280
|
|
};
|
|
|
|
MIKMODAPI CHAR* MikMod_InfoLoader(void)
|
|
{
|
|
int len=0;
|
|
MLOADER *l;
|
|
CHAR *list=NULL;
|
|
|
|
MUTEX_LOCK(lists);
|
|
/* compute size of buffer */
|
|
for(l = firstloader; l; l = l->next)
|
|
len += 1 + (l->next ? 1 : 0) + strlen(l->version);
|
|
|
|
if(len)
|
|
if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) {
|
|
CHAR *list_end = list;
|
|
list[0] = 0;
|
|
/* list all registered module loders */
|
|
for(l = firstloader; l; l = l->next) {
|
|
list_end += sprintf(list_end, "%s%s", l->version, (l->next) ? "\n" : "");
|
|
}
|
|
}
|
|
MUTEX_UNLOCK(lists);
|
|
return list;
|
|
}
|
|
|
|
void _mm_registerloader(MLOADER* ldr)
|
|
{
|
|
MLOADER *cruise=firstloader;
|
|
|
|
if(cruise) {
|
|
while(cruise->next) cruise = cruise->next;
|
|
cruise->next=ldr;
|
|
} else
|
|
firstloader=ldr;
|
|
}
|
|
|
|
MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr)
|
|
{
|
|
/* if we try to register an invalid loader, or an already registered loader,
|
|
ignore this attempt */
|
|
if ((!ldr)||(ldr->next))
|
|
return;
|
|
|
|
MUTEX_LOCK(lists);
|
|
_mm_registerloader(ldr);
|
|
MUTEX_UNLOCK(lists);
|
|
}
|
|
|
|
int ReadComment(UWORD len)
|
|
{
|
|
if(len) {
|
|
int i;
|
|
|
|
if(!(of.comment=(CHAR*)MikMod_malloc(len+1))) return 0;
|
|
_mm_read_UBYTES(of.comment,len,modreader);
|
|
|
|
/* translate IT linefeeds */
|
|
for(i=0;i<len;i++)
|
|
if(of.comment[i]=='\r') of.comment[i]='\n';
|
|
|
|
of.comment[len]=0; /* just in case */
|
|
}
|
|
if(!of.comment[0]) {
|
|
MikMod_free(of.comment);
|
|
of.comment=NULL;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int ReadLinedComment(UWORD len,UWORD linelen)
|
|
{
|
|
/* Adapted from the OpenMPT project, C'ified. */
|
|
CHAR *buf, *storage, *p;
|
|
size_t numlines, line, fpos, cpos, lpos, cnt;
|
|
|
|
if (!linelen) return 0;
|
|
if (!len) return 1;
|
|
|
|
if (!(buf = (CHAR *) MikMod_malloc(len))) return 0;
|
|
numlines = (len + linelen - 1) / linelen;
|
|
cnt = (linelen + 1) * numlines;
|
|
if (!(storage = (CHAR *) MikMod_malloc(cnt + 1))) {
|
|
MikMod_free(buf);
|
|
return 0;
|
|
}
|
|
|
|
_mm_read_UBYTES(buf,len,modreader);
|
|
storage[cnt] = 0;
|
|
for (line = 0, fpos = 0, cpos = 0; line < numlines; line++, fpos += linelen, cpos += (linelen + 1))
|
|
{
|
|
cnt = len - fpos;
|
|
if (cnt > linelen) cnt = linelen;
|
|
p = storage + cpos;
|
|
memcpy(p, buf + fpos, cnt);
|
|
p[cnt] = '\r';
|
|
/* fix weird chars */
|
|
for (lpos = 0; lpos < linelen; lpos++, p++) {
|
|
switch (*p) {
|
|
case '\0':
|
|
case '\n':
|
|
case '\r':
|
|
*p = ' ';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
of.comment = storage;
|
|
MikMod_free(buf);
|
|
return 1;
|
|
}
|
|
|
|
int AllocPositions(int total)
|
|
{
|
|
if(!total) {
|
|
_mm_errno=MMERR_NOT_A_MODULE;
|
|
return 0;
|
|
}
|
|
if(!(of.positions=(UWORD*)MikMod_calloc(total,sizeof(UWORD)))) return 0;
|
|
return 1;
|
|
}
|
|
|
|
int AllocPatterns(void)
|
|
{
|
|
int s,t,tracks = 0;
|
|
|
|
if((!of.numpat)||(!of.numchn)) {
|
|
_mm_errno=MMERR_NOT_A_MODULE;
|
|
return 0;
|
|
}
|
|
/* Allocate track sequencing array */
|
|
if(!(of.patterns=(UWORD*)MikMod_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0;
|
|
if(!(of.pattrows=(UWORD*)MikMod_calloc(of.numpat+1,sizeof(UWORD)))) return 0;
|
|
|
|
for(t=0;t<=of.numpat;t++) {
|
|
of.pattrows[t]=64;
|
|
for(s=0;s<of.numchn;s++)
|
|
of.patterns[(t*of.numchn)+s]=tracks++;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int AllocTracks(void)
|
|
{
|
|
if(!of.numtrk) {
|
|
_mm_errno=MMERR_NOT_A_MODULE;
|
|
return 0;
|
|
}
|
|
if(!(of.tracks=(UBYTE **)MikMod_calloc(of.numtrk,sizeof(UBYTE *)))) return 0;
|
|
return 1;
|
|
}
|
|
|
|
int AllocInstruments(void)
|
|
{
|
|
int t,n;
|
|
|
|
if(!of.numins) {
|
|
_mm_errno=MMERR_NOT_A_MODULE;
|
|
return 0;
|
|
}
|
|
if(!(of.instruments=(INSTRUMENT*)MikMod_calloc(of.numins,sizeof(INSTRUMENT))))
|
|
return 0;
|
|
|
|
for(t=0;t<of.numins;t++) {
|
|
for(n=0;n<INSTNOTES;n++) {
|
|
/* Init note / sample lookup table */
|
|
of.instruments[t].samplenote[n] = n;
|
|
of.instruments[t].samplenumber[n] = t;
|
|
}
|
|
of.instruments[t].globvol = 64;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int AllocSamples(void)
|
|
{
|
|
UWORD u;
|
|
|
|
if(!of.numsmp) {
|
|
_mm_errno=MMERR_NOT_A_MODULE;
|
|
return 0;
|
|
}
|
|
if(!(of.samples=(SAMPLE*)MikMod_calloc(of.numsmp,sizeof(SAMPLE)))) return 0;
|
|
|
|
for(u=0;u<of.numsmp;u++) {
|
|
of.samples[u].panning = 128; /* center */
|
|
of.samples[u].handle = -1;
|
|
of.samples[u].globvol = 64;
|
|
of.samples[u].volume = 64;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int ML_LoadSamples(void)
|
|
{
|
|
SAMPLE *s;
|
|
int u;
|
|
|
|
for(u=of.numsmp,s=of.samples;u;u--,s++)
|
|
if(s->length) SL_RegisterSample(s,MD_MUSIC,modreader);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Creates a CSTR out of a character buffer of 'len' bytes, but strips any
|
|
terminating non-printing characters like 0, spaces etc. */
|
|
CHAR *DupStr(const CHAR* s, UWORD len, int strict)
|
|
{
|
|
UWORD t;
|
|
CHAR *d=NULL;
|
|
|
|
/* Scan for last printing char in buffer [includes high ascii up to 254] */
|
|
while(len) {
|
|
if(s[len-1]>0x20) break;
|
|
len--;
|
|
}
|
|
|
|
/* Scan forward for possible NULL character */
|
|
if(strict) {
|
|
for(t=0;t<len;t++) if (!s[t]) break;
|
|
if (t<len) len=t;
|
|
}
|
|
|
|
/* When the buffer wasn't completely empty, allocate a cstring and copy the
|
|
buffer into that string, except for any control-chars */
|
|
if((d=(CHAR*)MikMod_malloc(sizeof(CHAR)*(len+1))) != NULL) {
|
|
for(t=0;t<len;t++) d[t]=(s[t]<32)?'.':s[t];
|
|
d[len]=0;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
static void ML_XFreeSample(SAMPLE *s)
|
|
{
|
|
if(s->handle>=0)
|
|
MD_SampleUnload(s->handle);
|
|
|
|
/* moved samplename freeing to our caller ML_FreeEx(),
|
|
* because we are called conditionally. */
|
|
}
|
|
|
|
static void ML_XFreeInstrument(INSTRUMENT *i)
|
|
{
|
|
MikMod_free(i->insname);
|
|
}
|
|
|
|
static void ML_FreeEx(MODULE *mf)
|
|
{
|
|
UWORD t;
|
|
|
|
MikMod_free(mf->songname);
|
|
MikMod_free(mf->comment);
|
|
|
|
MikMod_free(mf->modtype);
|
|
MikMod_free(mf->positions);
|
|
MikMod_free(mf->patterns);
|
|
MikMod_free(mf->pattrows);
|
|
|
|
if(mf->tracks) {
|
|
for(t=0;t<mf->numtrk;t++)
|
|
MikMod_free(mf->tracks[t]);
|
|
MikMod_free(mf->tracks);
|
|
}
|
|
if(mf->instruments) {
|
|
for(t=0;t<mf->numins;t++)
|
|
ML_XFreeInstrument(&mf->instruments[t]);
|
|
MikMod_free(mf->instruments);
|
|
}
|
|
if(mf->samples) {
|
|
for(t=0;t<mf->numsmp;t++) {
|
|
MikMod_free(mf->samples[t].samplename);
|
|
if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]);
|
|
}
|
|
MikMod_free(mf->samples);
|
|
}
|
|
memset(mf,0,sizeof(MODULE));
|
|
if(mf!=&of) MikMod_free(mf);
|
|
}
|
|
|
|
static MODULE *ML_AllocUniMod(void)
|
|
{
|
|
return (MODULE *) MikMod_malloc(sizeof(MODULE));
|
|
}
|
|
|
|
#ifndef NO_DEPACKERS
|
|
static int ML_TryUnpack(MREADER *reader,void **out,long *outlen)
|
|
{
|
|
int i;
|
|
|
|
*out = NULL;
|
|
*outlen = 0;
|
|
|
|
for(i=0;unpackers[i]!=NULL;++i) {
|
|
_mm_rewind(reader);
|
|
if(unpackers[i](reader,out,outlen)) return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static void Player_Free_internal(MODULE *mf)
|
|
{
|
|
if(mf) {
|
|
Player_Exit_internal(mf);
|
|
ML_FreeEx(mf);
|
|
}
|
|
}
|
|
|
|
MIKMODAPI void Player_Free(MODULE *mf)
|
|
{
|
|
MUTEX_LOCK(vars);
|
|
Player_Free_internal(mf);
|
|
MUTEX_UNLOCK(vars);
|
|
}
|
|
|
|
static CHAR* Player_LoadTitle_internal(MREADER *reader)
|
|
{
|
|
MLOADER *l;
|
|
CHAR *title;
|
|
#ifndef NO_DEPACKERS
|
|
void *unpk;
|
|
long newlen;
|
|
#endif
|
|
|
|
modreader=reader;
|
|
_mm_errno = 0;
|
|
_mm_critical = 0;
|
|
_mm_iobase_setcur(modreader);
|
|
|
|
#ifndef NO_DEPACKERS
|
|
if(ML_TryUnpack(modreader,&unpk,&newlen)) {
|
|
if(!(modreader=_mm_new_mem_reader(unpk,newlen))) {
|
|
modreader=reader;
|
|
MikMod_free(unpk);
|
|
return NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Try to find a loader that recognizes the module */
|
|
for(l=firstloader;l;l=l->next) {
|
|
_mm_rewind(modreader);
|
|
if(l->Test()) break;
|
|
}
|
|
|
|
if(l) {
|
|
title = l->LoadTitle();
|
|
}
|
|
else {
|
|
_mm_errno = MMERR_NOT_A_MODULE;
|
|
if(_mm_errorhandler) _mm_errorhandler();
|
|
title = NULL;
|
|
}
|
|
|
|
#ifndef NO_DEPACKERS
|
|
if (modreader!=reader) {
|
|
_mm_delete_mem_reader(modreader);
|
|
modreader=reader;
|
|
MikMod_free(unpk);
|
|
}
|
|
#endif
|
|
return title;
|
|
}
|
|
|
|
MIKMODAPI CHAR* Player_LoadTitleFP(int fp)
|
|
{
|
|
CHAR* result=NULL;
|
|
MREADER* reader;
|
|
|
|
if(fp && (reader=_mm_new_file_reader(fp)) != NULL) {
|
|
MUTEX_LOCK(lists);
|
|
result=Player_LoadTitle_internal(reader);
|
|
MUTEX_UNLOCK(lists);
|
|
_mm_delete_file_reader(reader);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
MIKMODAPI CHAR* Player_LoadTitleMem(const char *buffer,int len)
|
|
{
|
|
CHAR *result=NULL;
|
|
MREADER* reader;
|
|
|
|
if (!buffer || len <= 0) return NULL;
|
|
if ((reader=_mm_new_mem_reader(buffer,len)) != NULL)
|
|
{
|
|
MUTEX_LOCK(lists);
|
|
result=Player_LoadTitle_internal(reader);
|
|
MUTEX_UNLOCK(lists);
|
|
_mm_delete_mem_reader(reader);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MIKMODAPI CHAR* Player_LoadTitleGeneric(MREADER *reader)
|
|
{
|
|
CHAR *result=NULL;
|
|
|
|
if (reader) {
|
|
MUTEX_LOCK(lists);
|
|
result=Player_LoadTitle_internal(reader);
|
|
MUTEX_UNLOCK(lists);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
MIKMODAPI CHAR* Player_LoadTitle(const CHAR* filename)
|
|
{
|
|
CHAR* result=NULL;
|
|
int fp;
|
|
MREADER* reader;
|
|
|
|
if((fp=_mm_fopen(filename,"rb")) >= 0) {
|
|
if((reader=_mm_new_file_reader(fp)) != NULL) {
|
|
MUTEX_LOCK(lists);
|
|
result=Player_LoadTitle_internal(reader);
|
|
MUTEX_UNLOCK(lists);
|
|
_mm_delete_file_reader(reader);
|
|
}
|
|
_mm_fclose(fp);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Loads a module given an reader */
|
|
static MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,int curious)
|
|
{
|
|
int t;
|
|
MLOADER *l;
|
|
int ok;
|
|
MODULE *mf;
|
|
#ifndef NO_DEPACKERS
|
|
void *unpk;
|
|
long newlen;
|
|
#endif
|
|
|
|
modreader = reader;
|
|
_mm_errno = 0;
|
|
_mm_critical = 0;
|
|
_mm_iobase_setcur(modreader);
|
|
|
|
#ifndef NO_DEPACKERS
|
|
if(ML_TryUnpack(modreader,&unpk,&newlen)) {
|
|
if(!(modreader=_mm_new_mem_reader(unpk,newlen))) {
|
|
modreader=reader;
|
|
MikMod_free(unpk);
|
|
return NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Try to find a loader that recognizes the module */
|
|
for(l=firstloader;l;l=l->next) {
|
|
_mm_rewind(modreader);
|
|
if(l->Test()) break;
|
|
}
|
|
|
|
if(!l) {
|
|
_mm_errno = MMERR_NOT_A_MODULE;
|
|
#ifndef NO_DEPACKERS
|
|
if(modreader!=reader) {
|
|
_mm_delete_mem_reader(modreader);
|
|
modreader=reader;
|
|
MikMod_free(unpk);
|
|
}
|
|
#endif
|
|
if(_mm_errorhandler) _mm_errorhandler();
|
|
_mm_rewind(modreader);
|
|
_mm_iobase_revert(modreader);
|
|
return NULL;
|
|
}
|
|
|
|
/* init unitrk routines */
|
|
if(!UniInit()) {
|
|
#ifndef NO_DEPACKERS
|
|
if(modreader!=reader) {
|
|
_mm_delete_mem_reader(modreader);
|
|
modreader=reader;
|
|
MikMod_free(unpk);
|
|
}
|
|
#endif
|
|
if(_mm_errorhandler) _mm_errorhandler();
|
|
_mm_rewind(modreader);
|
|
_mm_iobase_revert(modreader);
|
|
return NULL;
|
|
}
|
|
|
|
/* init the module structure with vanilla settings */
|
|
memset(&of,0,sizeof(MODULE));
|
|
of.bpmlimit = 33;
|
|
of.initvolume = 128;
|
|
for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64;
|
|
for (t = 0; t < UF_MAXCHAN; t++)
|
|
of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT;
|
|
|
|
/* init module loader and load the header / patterns */
|
|
if (!l->Init || l->Init()) {
|
|
_mm_rewind(modreader);
|
|
ok = l->Load(curious);
|
|
if (ok) {
|
|
/* propagate inflags=flags for in-module samples */
|
|
for (t = 0; t < of.numsmp; t++)
|
|
if (of.samples[t].inflags == 0)
|
|
of.samples[t].inflags = of.samples[t].flags;
|
|
}
|
|
} else
|
|
ok = 0;
|
|
|
|
/* free loader and unitrk allocations */
|
|
if (l->Cleanup) l->Cleanup();
|
|
UniCleanup();
|
|
|
|
if(ok) ok = ML_LoadSamples();
|
|
if(ok) ok = ((mf=ML_AllocUniMod()) != NULL);
|
|
if(!ok) {
|
|
ML_FreeEx(&of);
|
|
#ifndef NO_DEPACKERS
|
|
if(modreader!=reader) {
|
|
_mm_delete_mem_reader(modreader);
|
|
modreader=reader;
|
|
MikMod_free(unpk);
|
|
}
|
|
#endif
|
|
if(_mm_errorhandler) _mm_errorhandler();
|
|
_mm_rewind(modreader);
|
|
_mm_iobase_revert(modreader);
|
|
return NULL;
|
|
}
|
|
|
|
/* If the module doesn't have any specific panning, create a
|
|
MOD-like panning, with the channels half-separated. */
|
|
if (!(of.flags & UF_PANNING))
|
|
for (t = 0; t < of.numchn; t++)
|
|
of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT;
|
|
|
|
/* Copy the static MODULE contents into the dynamic MODULE struct. */
|
|
memcpy(mf,&of,sizeof(MODULE));
|
|
|
|
if(maxchan>0) {
|
|
if(!(mf->flags&UF_NNA)&&(mf->numchn<maxchan))
|
|
maxchan = mf->numchn;
|
|
else
|
|
if((mf->numvoices)&&(mf->numvoices<maxchan))
|
|
maxchan = mf->numvoices;
|
|
|
|
if(maxchan<mf->numchn) mf->flags |= UF_NNA;
|
|
|
|
ok = !MikMod_SetNumVoices_internal(maxchan,-1);
|
|
}
|
|
|
|
if(ok) ok = !SL_LoadSamples();
|
|
if(ok) ok = !Player_Init(mf);
|
|
|
|
#ifndef NO_DEPACKERS
|
|
if(modreader!=reader) {
|
|
_mm_delete_mem_reader(modreader);
|
|
modreader=reader;
|
|
MikMod_free(unpk);
|
|
}
|
|
#endif
|
|
_mm_iobase_revert(modreader);
|
|
|
|
if(!ok) {
|
|
Player_Free_internal(mf);
|
|
return NULL;
|
|
}
|
|
return mf;
|
|
}
|
|
|
|
MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,int curious)
|
|
{
|
|
MODULE* result;
|
|
|
|
MUTEX_LOCK(vars);
|
|
MUTEX_LOCK(lists);
|
|
result=Player_LoadGeneric_internal(reader,maxchan,curious);
|
|
MUTEX_UNLOCK(lists);
|
|
MUTEX_UNLOCK(vars);
|
|
|
|
return result;
|
|
}
|
|
|
|
MIKMODAPI MODULE* Player_LoadMem(const char *buffer,int len,int maxchan,int curious)
|
|
{
|
|
MODULE* result=NULL;
|
|
MREADER* reader;
|
|
|
|
if (!buffer || len <= 0) return NULL;
|
|
if ((reader=_mm_new_mem_reader(buffer, len)) != NULL) {
|
|
result=Player_LoadGeneric(reader,maxchan,curious);
|
|
_mm_delete_mem_reader(reader);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Loads a module given a file pointer.
|
|
File is loaded from the current file seek position. */
|
|
MIKMODAPI MODULE* Player_LoadFP(int fp,int maxchan,int curious)
|
|
{
|
|
MODULE* result=NULL;
|
|
struct MREADER* reader;
|
|
|
|
if (fp && (reader=_mm_new_file_reader(fp)) != NULL) {
|
|
result=Player_LoadGeneric(reader,maxchan,curious);
|
|
_mm_delete_file_reader(reader);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* Open a module via its filename. The loader will initialize the specified
|
|
song-player 'player'. */
|
|
MIKMODAPI MODULE* Player_Load(const CHAR* filename,int maxchan,int curious)
|
|
{
|
|
int fp;
|
|
MODULE *mf=NULL;
|
|
|
|
if((fp=_mm_fopen(filename,"rb")) >= 0) {
|
|
mf=Player_LoadFP(fp,maxchan,curious);
|
|
_mm_fclose(fp);
|
|
}
|
|
return mf;
|
|
}
|
|
|
|
/* ex:set ts=4: */
|