A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 695 lines 15 kB view raw
1/* MikMod sound library 2 (c) 1998, 1999, 2000, 2001, 2002 Miodrag Vallat and others - see file 3 AUTHORS for complete list. 4 5 This library is free software; you can redistribute it and/or modify 6 it under the terms of the GNU Library General Public License as 7 published by the Free Software Foundation; either version 2 of 8 the License, or (at your option) any later version. 9 10 This program is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU Library General Public License for more details. 14 15 You should have received a copy of the GNU Library General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 18 02111-1307, USA. 19*/ 20 21/*============================================================================== 22 23 $Id$ 24 25 These routines are used to access the available module loaders 26 27==============================================================================*/ 28 29#ifdef HAVE_CONFIG_H 30#include "config.h" 31#endif 32 33#ifdef HAVE_UNISTD_H 34#include <unistd.h> 35#endif 36 37#ifdef HAVE_MEMORY_H 38#include <memory.h> 39#endif 40#include <string.h> 41 42#include "mikmod_internals.h" 43 44#ifdef SUNOS 45extern int fprintf(FILE *, const char *, ...); 46#endif 47 48MREADER *modreader; 49MODULE of; 50 51static MLOADER *firstloader=NULL; 52 53#ifndef NO_DEPACKERS 54static MUNPACKER unpackers[] = { 55 PP20_Unpack, 56 MMCMP_Unpack, 57 XPK_Unpack, 58 S404_Unpack, 59 NULL 60}; 61#endif 62 63const UWORD finetune[16] = { 64 8363,8413,8463,8529,8581,8651,8723,8757, 65 7895,7941,7985,8046,8107,8169,8232,8280 66}; 67 68MIKMODAPI CHAR* MikMod_InfoLoader(void) 69{ 70 int len=0; 71 MLOADER *l; 72 CHAR *list=NULL; 73 74 MUTEX_LOCK(lists); 75 /* compute size of buffer */ 76 for(l = firstloader; l; l = l->next) 77 len += 1 + (l->next ? 1 : 0) + strlen(l->version); 78 79 if(len) 80 if((list=(CHAR*)MikMod_malloc(len*sizeof(CHAR))) != NULL) { 81 CHAR *list_end = list; 82 list[0] = 0; 83 /* list all registered module loders */ 84 for(l = firstloader; l; l = l->next) { 85 list_end += sprintf(list_end, "%s%s", l->version, (l->next) ? "\n" : ""); 86 } 87 } 88 MUTEX_UNLOCK(lists); 89 return list; 90} 91 92void _mm_registerloader(MLOADER* ldr) 93{ 94 MLOADER *cruise=firstloader; 95 96 if(cruise) { 97 while(cruise->next) cruise = cruise->next; 98 cruise->next=ldr; 99 } else 100 firstloader=ldr; 101} 102 103MIKMODAPI void MikMod_RegisterLoader(struct MLOADER* ldr) 104{ 105 /* if we try to register an invalid loader, or an already registered loader, 106 ignore this attempt */ 107 if ((!ldr)||(ldr->next)) 108 return; 109 110 MUTEX_LOCK(lists); 111 _mm_registerloader(ldr); 112 MUTEX_UNLOCK(lists); 113} 114 115int ReadComment(UWORD len) 116{ 117 if(len) { 118 CHAR *ptr; 119 120 of.comment=(CHAR*)MikMod_calloc(1,len+1); 121 if(!of.comment) return 0; 122 _mm_read_UBYTES(of.comment,len,modreader); 123 124 /* translate IT linefeeds */ 125 ptr=of.comment; 126 while(*ptr) { 127 if(*ptr=='\r') *ptr='\n'; 128 ++ptr; 129 } 130 } 131 if(of.comment && !of.comment[0]) { 132 MikMod_free(of.comment); 133 of.comment=NULL; 134 } 135 return 1; 136} 137 138int ReadLinedComment(UWORD len,UWORD linelen) 139{ 140 /* Adapted from the OpenMPT project, C'ified. */ 141 CHAR *buf, *storage, *p; 142 size_t numlines, line, fpos, cpos, lpos, cnt; 143 144 if (!linelen) return 0; 145 if (!len) return 1; 146 147 numlines = (len + linelen - 1) / linelen; 148 cnt = (linelen + 1) * numlines; 149 buf = (CHAR *) MikMod_calloc(1, len); 150 if (!buf) return 0; 151 storage = (CHAR *) MikMod_calloc(1, cnt + 1); 152 if (!storage) { 153 MikMod_free(buf); 154 return 0; 155 } 156 157 _mm_read_UBYTES(buf,len,modreader); 158 for (line = 0, fpos = 0, cpos = 0; line < numlines; line++, fpos += linelen, cpos += (linelen + 1)) 159 { 160 cnt = len - fpos; 161 if (cnt > linelen) cnt = linelen; 162 p = storage + cpos; 163 memcpy(p, buf + fpos, cnt); 164 p[cnt] = '\r'; 165 /* fix weird chars */ 166 for (lpos = 0; lpos < linelen; lpos++, p++) { 167 switch (*p) { 168 case '\0': 169 case '\n': 170 case '\r': 171 *p = ' '; 172 break; 173 } 174 } 175 } 176 177 of.comment = storage; 178 MikMod_free(buf); 179 return 1; 180} 181 182int AllocPositions(int total) 183{ 184 if(!total) { 185 _mm_errno=MMERR_NOT_A_MODULE; 186 return 0; 187 } 188 if(!(of.positions=(UWORD*)MikMod_calloc(total,sizeof(UWORD)))) return 0; 189 return 1; 190} 191 192int AllocPatterns(void) 193{ 194 int s,t,tracks = 0; 195 196 if((!of.numpat)||(!of.numchn)) { 197 _mm_errno=MMERR_NOT_A_MODULE; 198 return 0; 199 } 200 /* Allocate track sequencing array */ 201 if(!(of.patterns=(UWORD*)MikMod_calloc((ULONG)(of.numpat+1)*of.numchn,sizeof(UWORD)))) return 0; 202 if(!(of.pattrows=(UWORD*)MikMod_calloc(of.numpat+1,sizeof(UWORD)))) return 0; 203 204 for(t=0;t<=of.numpat;t++) { 205 of.pattrows[t]=64; 206 for(s=0;s<of.numchn;s++) 207 of.patterns[(t*of.numchn)+s]=tracks++; 208 } 209 210 return 1; 211} 212 213int AllocTracks(void) 214{ 215 if(!of.numtrk) { 216 _mm_errno=MMERR_NOT_A_MODULE; 217 return 0; 218 } 219 if(!(of.tracks=(UBYTE **)MikMod_calloc(of.numtrk,sizeof(UBYTE *)))) return 0; 220 return 1; 221} 222 223int AllocInstruments(void) 224{ 225 int t,n; 226 227 if(!of.numins) { 228 _mm_errno=MMERR_NOT_A_MODULE; 229 return 0; 230 } 231 if(!(of.instruments=(INSTRUMENT*)MikMod_calloc(of.numins,sizeof(INSTRUMENT)))) 232 return 0; 233 234 for(t=0;t<of.numins;t++) { 235 for(n=0;n<INSTNOTES;n++) { 236 /* Init note / sample lookup table */ 237 of.instruments[t].samplenote[n] = n; 238 of.instruments[t].samplenumber[n] = t; 239 } 240 of.instruments[t].globvol = 64; 241 } 242 return 1; 243} 244 245int AllocSamples(void) 246{ 247 UWORD u; 248 249 if(!of.numsmp) { 250 _mm_errno=MMERR_NOT_A_MODULE; 251 return 0; 252 } 253 if(!(of.samples=(SAMPLE*)MikMod_calloc(of.numsmp,sizeof(SAMPLE)))) return 0; 254 255 for(u=0;u<of.numsmp;u++) { 256 of.samples[u].panning = 128; /* center */ 257 of.samples[u].handle = -1; 258 of.samples[u].globvol = 64; 259 of.samples[u].volume = 64; 260 } 261 return 1; 262} 263 264static int ML_LoadSamples(void) 265{ 266 SAMPLE *s; 267 int u; 268 269 for(u=of.numsmp,s=of.samples;u;u--,s++) 270 if(s->length) SL_RegisterSample(s,MD_MUSIC,modreader); 271 272 return 1; 273} 274 275/* Creates a CSTR out of a character buffer of 'len' bytes, but strips any 276 terminating non-printing characters like 0, spaces etc. */ 277CHAR *DupStr(const CHAR* s, UWORD len, int strict) 278{ 279 UWORD t; 280 CHAR *d=NULL; 281 282 /* Scan for last printing char in buffer [includes high ascii up to 254] */ 283 while(len) { 284 if(s[len-1]>0x20) break; 285 len--; 286 } 287 288 /* Scan forward for possible NULL character */ 289 if(strict) { 290 for(t=0;t<len;t++) if (!s[t]) break; 291 if (t<len) len=t; 292 } 293 294 /* When the buffer wasn't completely empty, allocate a cstring and copy the 295 buffer into that string, except for any control-chars */ 296 if((d=(CHAR*)MikMod_malloc(sizeof(CHAR)*(len+1))) != NULL) { 297 for(t=0;t<len;t++) d[t]=(s[t]<32)?'.':s[t]; 298 d[len]=0; 299 } 300 return d; 301} 302 303static void ML_XFreeSample(SAMPLE *s) 304{ 305 if(s->handle>=0) 306 MD_SampleUnload(s->handle); 307 308/* moved samplename freeing to our caller ML_FreeEx(), 309 * because we are called conditionally. */ 310} 311 312static void ML_XFreeInstrument(INSTRUMENT *i) 313{ 314 MikMod_free(i->insname); 315} 316 317static void ML_FreeEx(MODULE *mf) 318{ 319 UWORD t; 320 321 MikMod_free(mf->songname); 322 MikMod_free(mf->comment); 323 324 MikMod_free(mf->modtype); 325 MikMod_free(mf->positions); 326 MikMod_free(mf->patterns); 327 MikMod_free(mf->pattrows); 328 329 if(mf->tracks) { 330 for(t=0;t<mf->numtrk;t++) 331 MikMod_free(mf->tracks[t]); 332 MikMod_free(mf->tracks); 333 } 334 if(mf->instruments) { 335 for(t=0;t<mf->numins;t++) 336 ML_XFreeInstrument(&mf->instruments[t]); 337 MikMod_free(mf->instruments); 338 } 339 if(mf->samples) { 340 for(t=0;t<mf->numsmp;t++) { 341 MikMod_free(mf->samples[t].samplename); 342 if(mf->samples[t].length) ML_XFreeSample(&mf->samples[t]); 343 } 344 MikMod_free(mf->samples); 345 } 346 memset(mf,0,sizeof(MODULE)); 347 if(mf!=&of) MikMod_free(mf); 348} 349 350static MODULE *ML_AllocUniMod(void) 351{ 352 return (MODULE *) MikMod_malloc(sizeof(MODULE)); 353} 354 355#ifndef NO_DEPACKERS 356static int ML_TryUnpack(MREADER *reader,void **out,long *outlen) 357{ 358 int i; 359 360 *out = NULL; 361 *outlen = 0; 362 363 for(i=0;unpackers[i]!=NULL;++i) { 364 _mm_rewind(reader); 365 if(unpackers[i](reader,out,outlen)) return 1; 366 } 367 return 0; 368} 369#endif 370 371static void Player_Free_internal(MODULE *mf) 372{ 373 if(mf) { 374 Player_Exit_internal(mf); 375 ML_FreeEx(mf); 376 } 377} 378 379MIKMODAPI void Player_Free(MODULE *mf) 380{ 381 MUTEX_LOCK(vars); 382 Player_Free_internal(mf); 383 MUTEX_UNLOCK(vars); 384} 385 386static CHAR* Player_LoadTitle_internal(MREADER *reader) 387{ 388 MLOADER *l; 389 CHAR *title; 390 #ifndef NO_DEPACKERS 391 void *unpk; 392 long newlen; 393 #endif 394 395 modreader=reader; 396 _mm_errno = 0; 397 _mm_critical = 0; 398 _mm_iobase_setcur(modreader); 399 400 #ifndef NO_DEPACKERS 401 if(ML_TryUnpack(modreader,&unpk,&newlen)) { 402 if(!(modreader=_mm_new_mem_reader(unpk,newlen))) { 403 modreader=reader; 404 MikMod_free(unpk); 405 return NULL; 406 } 407 } 408 #endif 409 410 /* Try to find a loader that recognizes the module */ 411 for(l=firstloader;l;l=l->next) { 412 _mm_rewind(modreader); 413 if(l->Test()) break; 414 } 415 416 if(l) { 417 title = l->LoadTitle(); 418 } 419 else { 420 _mm_errno = MMERR_NOT_A_MODULE; 421 if(_mm_errorhandler) _mm_errorhandler(); 422 title = NULL; 423 } 424 425 #ifndef NO_DEPACKERS 426 if (modreader!=reader) { 427 _mm_delete_mem_reader(modreader); 428 modreader=reader; 429 MikMod_free(unpk); 430 } 431 #endif 432 return title; 433} 434 435MIKMODAPI CHAR* Player_LoadTitleFP(int fp) 436{ 437 CHAR* result=NULL; 438 MREADER* reader; 439 440 if(fp && (reader=_mm_new_file_reader(fp)) != NULL) { 441 MUTEX_LOCK(lists); 442 result=Player_LoadTitle_internal(reader); 443 MUTEX_UNLOCK(lists); 444 _mm_delete_file_reader(reader); 445 } 446 return result; 447} 448 449MIKMODAPI CHAR* Player_LoadTitleMem(const char *buffer,int len) 450{ 451 CHAR *result=NULL; 452 MREADER* reader; 453 454 if (!buffer || len <= 0) return NULL; 455 if ((reader=_mm_new_mem_reader(buffer,len)) != NULL) 456 { 457 MUTEX_LOCK(lists); 458 result=Player_LoadTitle_internal(reader); 459 MUTEX_UNLOCK(lists); 460 _mm_delete_mem_reader(reader); 461 } 462 463 return result; 464} 465 466MIKMODAPI CHAR* Player_LoadTitleGeneric(MREADER *reader) 467{ 468 CHAR *result=NULL; 469 470 if (reader) { 471 MUTEX_LOCK(lists); 472 result=Player_LoadTitle_internal(reader); 473 MUTEX_UNLOCK(lists); 474 } 475 return result; 476} 477 478MIKMODAPI CHAR* Player_LoadTitle(const CHAR* filename) 479{ 480 CHAR* result=NULL; 481 int fp; 482 MREADER* reader; 483 484 if((fp=_mm_fopen(filename,"rb")) >= 0) { 485 if((reader=_mm_new_file_reader(fp)) != NULL) { 486 MUTEX_LOCK(lists); 487 result=Player_LoadTitle_internal(reader); 488 MUTEX_UNLOCK(lists); 489 _mm_delete_file_reader(reader); 490 } 491 _mm_fclose(fp); 492 } 493 return result; 494} 495 496/* Loads a module given an reader */ 497static MODULE* Player_LoadGeneric_internal(MREADER *reader,int maxchan,int curious) 498{ 499 int t; 500 MLOADER *l; 501 int ok; 502 MODULE *mf; 503 #ifndef NO_DEPACKERS 504 void *unpk; 505 long newlen; 506 #endif 507 508 modreader = reader; 509 _mm_errno = 0; 510 _mm_critical = 0; 511 _mm_iobase_setcur(modreader); 512 513 #ifndef NO_DEPACKERS 514 if(ML_TryUnpack(modreader,&unpk,&newlen)) { 515 if(!(modreader=_mm_new_mem_reader(unpk,newlen))) { 516 modreader=reader; 517 MikMod_free(unpk); 518 return NULL; 519 } 520 } 521 #endif 522 523 /* Try to find a loader that recognizes the module */ 524 for(l=firstloader;l;l=l->next) { 525 _mm_rewind(modreader); 526 if(l->Test()) break; 527 } 528 529 if(!l) { 530 _mm_errno = MMERR_NOT_A_MODULE; 531 #ifndef NO_DEPACKERS 532 if(modreader!=reader) { 533 _mm_delete_mem_reader(modreader); 534 modreader=reader; 535 MikMod_free(unpk); 536 } 537 #endif 538 if(_mm_errorhandler) _mm_errorhandler(); 539 _mm_rewind(modreader); 540 _mm_iobase_revert(modreader); 541 return NULL; 542 } 543 544 /* init unitrk routines */ 545 if(!UniInit()) { 546 #ifndef NO_DEPACKERS 547 if(modreader!=reader) { 548 _mm_delete_mem_reader(modreader); 549 modreader=reader; 550 MikMod_free(unpk); 551 } 552 #endif 553 if(_mm_errorhandler) _mm_errorhandler(); 554 _mm_rewind(modreader); 555 _mm_iobase_revert(modreader); 556 return NULL; 557 } 558 559 /* init the module structure with vanilla settings */ 560 memset(&of,0,sizeof(MODULE)); 561 of.bpmlimit = 33; 562 of.initvolume = 128; 563 for (t = 0; t < UF_MAXCHAN; t++) of.chanvol[t] = 64; 564 for (t = 0; t < UF_MAXCHAN; t++) 565 of.panning[t] = ((t + 1) & 2) ? PAN_RIGHT : PAN_LEFT; 566 567 /* init module loader and load the header / patterns */ 568 if (!l->Init || l->Init()) { 569 _mm_rewind(modreader); 570 ok = l->Load(curious); 571 if (ok) { 572 /* propagate inflags=flags for in-module samples */ 573 for (t = 0; t < of.numsmp; t++) 574 if (of.samples[t].inflags == 0) 575 of.samples[t].inflags = of.samples[t].flags; 576 } 577 } else 578 ok = 0; 579 580 /* free loader and unitrk allocations */ 581 if (l->Cleanup) l->Cleanup(); 582 UniCleanup(); 583 584 if(ok) ok = ML_LoadSamples(); 585 if(ok) ok = ((mf=ML_AllocUniMod()) != NULL); 586 if(!ok) { 587 ML_FreeEx(&of); 588 #ifndef NO_DEPACKERS 589 if(modreader!=reader) { 590 _mm_delete_mem_reader(modreader); 591 modreader=reader; 592 MikMod_free(unpk); 593 } 594 #endif 595 if(_mm_errorhandler) _mm_errorhandler(); 596 _mm_rewind(modreader); 597 _mm_iobase_revert(modreader); 598 return NULL; 599 } 600 601 /* If the module doesn't have any specific panning, create a 602 MOD-like panning, with the channels half-separated. */ 603 if (!(of.flags & UF_PANNING)) 604 for (t = 0; t < of.numchn; t++) 605 of.panning[t] = ((t + 1) & 2) ? PAN_HALFRIGHT : PAN_HALFLEFT; 606 607 /* Copy the static MODULE contents into the dynamic MODULE struct. */ 608 memcpy(mf,&of,sizeof(MODULE)); 609 610 if(maxchan>0) { 611 if(!(mf->flags&UF_NNA)&&(mf->numchn<maxchan)) 612 maxchan = mf->numchn; 613 else 614 if((mf->numvoices)&&(mf->numvoices<maxchan)) 615 maxchan = mf->numvoices; 616 617 if(maxchan<mf->numchn) mf->flags |= UF_NNA; 618 619 ok = !MikMod_SetNumVoices_internal(maxchan,-1); 620 } 621 622 if(ok) ok = !SL_LoadSamples(); 623 if(ok) ok = !Player_Init(mf); 624 625 #ifndef NO_DEPACKERS 626 if(modreader!=reader) { 627 _mm_delete_mem_reader(modreader); 628 modreader=reader; 629 MikMod_free(unpk); 630 } 631 #endif 632 _mm_iobase_revert(modreader); 633 634 if(!ok) { 635 Player_Free_internal(mf); 636 return NULL; 637 } 638 return mf; 639} 640 641MIKMODAPI MODULE* Player_LoadGeneric(MREADER *reader,int maxchan,int curious) 642{ 643 MODULE* result; 644 645 MUTEX_LOCK(vars); 646 MUTEX_LOCK(lists); 647 result=Player_LoadGeneric_internal(reader,maxchan,curious); 648 MUTEX_UNLOCK(lists); 649 MUTEX_UNLOCK(vars); 650 651 return result; 652} 653 654MIKMODAPI MODULE* Player_LoadMem(const char *buffer,int len,int maxchan,int curious) 655{ 656 MODULE* result=NULL; 657 MREADER* reader; 658 659 if (!buffer || len <= 0) return NULL; 660 if ((reader=_mm_new_mem_reader(buffer, len)) != NULL) { 661 result=Player_LoadGeneric(reader,maxchan,curious); 662 _mm_delete_mem_reader(reader); 663 } 664 return result; 665} 666 667/* Loads a module given a file pointer. 668 File is loaded from the current file seek position. */ 669MIKMODAPI MODULE* Player_LoadFP(int fp,int maxchan,int curious) 670{ 671 MODULE* result=NULL; 672 struct MREADER* reader; 673 674 if (fp && (reader=_mm_new_file_reader(fp)) != NULL) { 675 result=Player_LoadGeneric(reader,maxchan,curious); 676 _mm_delete_file_reader(reader); 677 } 678 return result; 679} 680 681/* Open a module via its filename. The loader will initialize the specified 682 song-player 'player'. */ 683MIKMODAPI MODULE* Player_Load(const CHAR* filename,int maxchan,int curious) 684{ 685 int fp; 686 MODULE *mf=NULL; 687 688 if((fp=_mm_fopen(filename,"rb")) >= 0) { 689 mf=Player_LoadFP(fp,maxchan,curious); 690 _mm_fclose(fp); 691 } 692 return mf; 693} 694 695/* ex:set ts=4: */