1 /* MikMod sound library
\r
2 (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file
\r
3 AUTHORS for complete list.
\r
5 This library is free software; you can redistribute it and/or modify
\r
6 it under the terms of the GNU Library General Public License as
\r
7 published by the Free Software Foundation; either version 2 of
\r
8 the License, or (at your option) any later version.
\r
10 This program is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU Library General Public License for more details.
\r
15 You should have received a copy of the GNU Library General Public
\r
16 License along with this library; if not, write to the Free Software
\r
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
\r
21 /*==============================================================================
\r
23 $Id: mloader.c,v 1.1.1.1 2004/01/21 01:36:35 raph Exp $
\r
25 These routines are used to access the available module loaders
\r
27 ==============================================================================*/
\r
29 #ifdef HAVE_CONFIG_H
\r
33 #ifdef HAVE_UNISTD_H
\r
37 #ifdef HAVE_MEMORY_H
\r
42 #include "mikmod_internals.h"
\r
45 extern int fprintf(FILE *, const char *, ...);
\r
51 static MLOADER *firstloader=NULL;
\r
53 UWORD finetune[16]={
\r
54 8363,8413,8463,8529,8581,8651,8723,8757,
\r
55 7895,7941,7985,8046,8107,8169,8232,8280
\r
58 MIKMODAPI CHAR* MikMod_InfoLoader(void)
\r
65 /* compute size of buffer *
\r
66 for(l=firstloader;l;l=l->next) len+=1+(l->next?1:0)+strlen(l->version);
\r
69 if((list=_mm_malloc(len*sizeof(CHAR)))) {
\r
71 /* list all registered module loders *
\r
72 for(l=firstloader;l;l=l->next)
\r
73 sprintf(list,(l->next)?"%s%s\n":"%s%s",list,l->version);
\r
75 MUTEX_UNLOCK(lists);
\r
79 void _mm_registerloader(MLOADER* ldr)
\r
81 MLOADER *cruise=firstloader;
\r
84 while(cruise->next) cruise = cruise->next;
\r
90 MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr)
\r
92 /* if we try to register an invalid loader, or an already registered loader,
\r
93 ignore this attempt */
\r
94 if ((!ldr)||(ldr->next))
\r
98 _mm_registerloader(ldr);
\r
99 MUTEX_UNLOCK(lists);
\r
102 BOOL ReadComment(UWORD len)
\r
107 if(!(of.comment=(CHAR*)_mm_malloc(len+1))) return 0;
\r
108 _mm_read_UBYTES(of.comment,len,modreader);
\r
110 /* translate IT linefeeds */
\r
112 if(of.comment[i]=='\r') of.comment[i]='\n';
\r
114 of.comment[len]=0; /* just in case */
\r
116 if(!of.comment[0]) {
\r
123 BOOL ReadLinedComment(UWORD len,UWORD linelen)
\r
125 CHAR *tempcomment,*line,*storage;
\r
126 UWORD total=0,t,lines;
\r
129 lines = (len + linelen - 1) / linelen;
\r
131 if(!(tempcomment=(CHAR*)_mm_malloc(len+1))) return 0;
\r
132 if(!(storage=(CHAR*)_mm_malloc(linelen+1))) {
\r
136 memset(tempcomment, ' ', len);
\r
137 _mm_read_UBYTES(tempcomment,len,modreader);
\r
139 /* compute message length */
\r
140 for(line=tempcomment,total=t=0;t<lines;t++,line+=linelen) {
\r
141 for(i=linelen;(i>=0)&&(line[i]==' ');i--) line[i]=0;
\r
142 for(i=0;i<linelen;i++) if (!line[i]) break;
\r
147 if(!(of.comment=(CHAR*)_mm_malloc(total+1))) {
\r
153 /* convert message */
\r
154 for(line=tempcomment,t=0;t<lines;t++,line+=linelen) {
\r
155 for(i=0;i<linelen;i++) if(!(storage[i]=line[i])) break;
\r
156 storage[i]=0; /* if (i==linelen) */
\r
157 strcat(of.comment,storage);strcat(of.comment,"\r");
\r
166 BOOL AllocPositions(int total)
\r
169 _mm_errno=MMERR_NOT_A_MODULE;
\r
172 if(!(of.positions=_mm_calloc(total,sizeof(UWORD)))) return 0;
\r
176 BOOL AllocPatterns(void)
\r
178 int s,t,tracks = 0;
\r
180 if((!of.numpat)||(!of.numchn)) {
\r
181 _mm_errno=MMERR_NOT_A_MODULE;
\r
184 /* Allocate track sequencing array */
\r
185 if(!(of.patterns=(UWORD*)_mm_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0;
\r
186 if(!(of.pattrows=(UWORD*)_mm_calloc(of.numpat+1,sizeof(UWORD)))) return 0;
\r
188 for(t=0;t<=of.numpat;t++) {
\r
190 for(s=0;s<of.numchn;s++)
\r
191 of.patterns[(t*of.numchn)+s]=tracks++;
\r
197 BOOL AllocTracks(void)
\r
200 _mm_errno=MMERR_NOT_A_MODULE;
\r
203 if(!(of.tracks=(UBYTE **)_mm_calloc(of.numtrk,sizeof(UBYTE *)))) return 0;
\r
207 BOOL AllocInstruments(void)
\r
212 _mm_errno=MMERR_NOT_A_MODULE;
\r
215 if(!(of.instruments=(INSTRUMENT*)_mm_calloc(of.numins,sizeof(INSTRUMENT))))
\r
218 for(t=0;t<of.numins;t++) {
\r
219 for(n=0;n<INSTNOTES;n++) {
\r
220 /* Init note / sample lookup table */
\r
221 of.instruments[t].samplenote[n] = n;
\r
222 of.instruments[t].samplenumber[n] = t;
\r
224 of.instruments[t].globvol = 64;
\r
229 BOOL AllocSamples(void)
\r
234 _mm_errno=MMERR_NOT_A_MODULE;
\r
237 if(!(of.samples=(SAMPLE*)_mm_calloc(of.numsmp,sizeof(SAMPLE)))) return 0;
\r
239 for(u=0;u<of.numsmp;u++) {
\r
240 of.samples[u].panning = 128; /* center */
\r
241 of.samples[u].handle = -1;
\r
242 of.samples[u].globvol = 64;
\r
243 of.samples[u].volume = 64;
\r
248 static BOOL ML_LoadSamples(void)
\r
253 for(u=of.numsmp,s=of.samples;u;u--,s++)
\r
254 if(s->length) SL_RegisterSample(s,MD_MUSIC,modreader);
\r
259 /* Creates a CSTR out of a character buffer of 'len' bytes, but strips any
\r
260 terminating non-printing characters like 0, spaces etc. */
\r
261 CHAR *DupStr(CHAR* s,UWORD len,BOOL strict)
\r
266 /* Scan for last printing char in buffer [includes high ascii up to 254] */
\r
268 if(s[len-1]>0x20) break;
\r
272 /* Scan forward for possible NULL character */
\r
274 for(t=0;t<len;t++) if (!s[t]) break;
\r
278 /* When the buffer wasn't completely empty, allocate a cstring and copy the
\r
279 buffer into that string, except for any control-chars */
\r
280 if((d=(CHAR*)_mm_malloc(sizeof(CHAR)*(len+1)))) {
\r
281 for(t=0;t<len;t++) d[t]=(s[t]<32)?'.':s[t];
\r
287 static void ML_XFreeSample(SAMPLE *s)
\r
290 MD_SampleUnload(s->handle);
\r
291 if(s->samplename) free(s->samplename);
\r
294 static void ML_XFreeInstrument(INSTRUMENT *i)
\r
296 if(i->insname) free(i->insname);
\r
299 static void ML_FreeEx(MODULE *mf)
\r
303 if(mf->songname) free(mf->songname);
\r
304 if(mf->comment) free(mf->comment);
\r
306 if(mf->modtype) free(mf->modtype);
\r
307 if(mf->positions) free(mf->positions);
\r
308 if(mf->patterns) free(mf->patterns);
\r
309 if(mf->pattrows) free(mf->pattrows);
\r
312 for(t=0;t<mf->numtrk;t++)
\r
313 if(mf->tracks[t]) free(mf->tracks[t]);
\r
316 if(mf->instruments) {
\r
317 for(t=0;t<mf->numins;t++)
\r
318 ML_XFreeInstrument(&mf->instruments[t]);
\r
319 free(mf->instruments);
\r
322 for(t=0;t<mf->numsmp;t++)
\r
323 if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]);
\r
326 memset(mf,0,sizeof(MODULE));
\r
327 if(mf!=&of) free(mf);
\r
330 static MODULE *ML_AllocUniMod(void)
\r
334 return (mf=_mm_malloc(sizeof(MODULE)));
\r
337 void Player_Free_internal(MODULE *mf)
\r
340 Player_Exit_internal(mf);
\r
345 MIKMODAPI void Player_Free(MODULE *mf)
\r
348 Player_Free_internal(mf);
\r
349 MUTEX_UNLOCK(vars);
\r
352 static CHAR* Player_LoadTitle_internal(MREADER *reader)
\r
359 _mm_iobase_setcur(modreader);
\r
361 /* Try to find a loader that recognizes the module */
\r
362 for(l=firstloader;l;l=l->next) {
\r
363 _mm_rewind(modreader);
\r
364 if(l->Test()) break;
\r
368 _mm_errno = MMERR_NOT_A_MODULE;
\r
369 if(_mm_errorhandler) _mm_errorhandler();
\r
373 return l->LoadTitle();
\r
376 MIKMODAPI CHAR* Player_LoadTitleFP(int fp)
\r
381 if(fp && (reader=_mm_new_file_reader(fp))) {
\r
383 result=Player_LoadTitle_internal(reader);
\r
384 MUTEX_UNLOCK(lists);
\r
385 _mm_delete_file_reader(reader);
\r
390 MIKMODAPI CHAR* Player_LoadTitle(CHAR* filename)
\r
396 if((fp=_mm_fopen(filename,O_RDONLY))) {
\r
397 if((reader=_mm_new_file_reader(fp))) {
\r
399 result=Player_LoadTitle_internal(reader);
\r
400 MUTEX_UNLOCK(lists);
\r
401 _mm_delete_file_reader(reader);
\r
408 /* Loads a module given an reader */
\r
409 MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,BOOL curious)
\r
416 modreader = reader;
\r
419 _mm_iobase_setcur(modreader);
\r
421 /* Try to find a loader that recognizes the module */
\r
422 for(l=firstloader;l;l=l->next) {
\r
423 _mm_rewind(modreader);
\r
424 if(l->Test()) break;
\r
428 _mm_errno = MMERR_NOT_A_MODULE;
\r
429 if(_mm_errorhandler) _mm_errorhandler();
\r
430 _mm_rewind(modreader);_mm_iobase_revert();
\r
434 /* init unitrk routines */
\r
436 if(_mm_errorhandler) _mm_errorhandler();
\r
437 _mm_rewind(modreader);_mm_iobase_revert();
\r
441 /* init the module structure with vanilla settings */
\r
442 memset(&of,0,sizeof(MODULE));
\r
444 of.initvolume = 128;
\r
445 for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64;
\r
446 for (t = 0; t < UF_MAXCHAN; t++)
\r
447 of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT;
\r
449 /* init module loader and load the header / patterns */
\r
450 if (!l->Init || l->Init()) {
\r
451 _mm_rewind(modreader);
\r
452 ok = l->Load(curious);
\r
453 /* propagate inflags=flags for in-module samples */
\r
454 for (t = 0; t < of.numsmp; t++)
\r
455 if (of.samples[t].inflags == 0)
\r
456 of.samples[t].inflags = of.samples[t].flags;
\r
460 /* free loader and unitrk allocations */
\r
461 if (l->Cleanup) l->Cleanup();
\r
466 if(_mm_errorhandler) _mm_errorhandler();
\r
467 _mm_rewind(modreader);_mm_iobase_revert();
\r
471 if(!ML_LoadSamples()) {
\r
473 if(_mm_errorhandler) _mm_errorhandler();
\r
474 _mm_rewind(modreader);_mm_iobase_revert();
\r
478 if(!(mf=ML_AllocUniMod())) {
\r
480 _mm_rewind(modreader);_mm_iobase_revert();
\r
481 if(_mm_errorhandler) _mm_errorhandler();
\r
485 /* If the module doesn't have any specific panning, create a
\r
486 MOD-like panning, with the channels half-separated. */
\r
487 if (!(of.flags & UF_PANNING))
\r
488 for (t = 0; t < of.numchn; t++)
\r
489 of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT;
\r
491 /* Copy the static MODULE contents into the dynamic MODULE struct. */
\r
492 memcpy(mf,&of,sizeof(MODULE));
\r
495 if(!(mf->flags&UF_NNA)&&(mf->numchn<maxchan))
\r
496 maxchan = mf->numchn;
\r
498 if((mf->numvoices)&&(mf->numvoices<maxchan))
\r
499 maxchan = mf->numvoices;
\r
501 if(maxchan<mf->numchn) mf->flags |= UF_NNA;
\r
503 if(MikMod_SetNumVoices_internal(maxchan,-1)) {
\r
504 _mm_iobase_revert();
\r
509 if(SL_LoadSamples()) {
\r
510 _mm_iobase_revert();
\r
511 Player_Free_internal(mf);
\r
514 if(Player_Init(mf)) {
\r
515 _mm_iobase_revert();
\r
516 Player_Free_internal(mf);
\r
519 _mm_iobase_revert();
\r
523 MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,BOOL curious)
\r
529 result=Player_LoadGeneric_internal(reader,maxchan,curious);
\r
530 MUTEX_UNLOCK(lists);
\r
531 MUTEX_UNLOCK(vars);
\r
536 /* Loads a module given a file pointer.
\r
537 File is loaded from the current file seek position. */
\r
538 MIKMODAPI MODULE* Player_LoadFP(int fp,int maxchan,BOOL curious)
\r
540 MODULE* result=NULL;
\r
541 struct MREADER* reader=_mm_new_file_reader (fp);
\r
544 result=Player_LoadGeneric(reader,maxchan,curious);
\r
545 _mm_delete_file_reader(reader);
\r
550 /* Open a module via its filename. The loader will initialize the specified
\r
551 song-player 'player'. */
\r
552 MIKMODAPI MODULE* Player_Load(CHAR* filename,int maxchan,BOOL curious)
\r
557 if((fp=_mm_fopen(filename,O_RDONLY))) {
\r
558 mf=Player_LoadFP(fp,maxchan,curious);
\r