1 /*----------------------------------------------------------------------------- 2 3 ST-Sound ( YM files player library ) 4 5 Copyright (C) 1995-1999 Arnaud Carre ( http://leonard.oxg.free.fr ) 6 7 YM Music Driver 8 9 -----------------------------------------------------------------------------*/ 10 11 /*----------------------------------------------------------------------------- 12 13 This file is part of ST-Sound 14 15 ST-Sound is free software; you can redistribute it and/or modify 16 it under the terms of the GNU General Public License as published by 17 the Free Software Foundation; either version 2 of the License, or 18 (at your option) any later version. 19 20 ST-Sound is distributed in the hope that it will be useful, 21 but WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 GNU General Public License for more details. 24 25 You should have received a copy of the GNU General Public License 26 along with ST-Sound; if not, write to the Free Software 27 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 28 29 -----------------------------------------------------------------------------*/ 30 31 #include <string.h> 32 #include <stdlib.h> 33 #include "YmMusic.h" 34 35 #define _LINEAR_OVRS // Activate linear oversampling (best quality) Only used for DigiMix and UniversalTracker YM file type 36 37 38 // ATARI-ST MFP chip predivisor 39 static const ymint mfpPrediv[8] = {0,4,10,16,50,64,100,200}; 40 41 42 43 CYmMusic::CYmMusic(ymint _replayRate) 44 { 45 46 pBigMalloc = NULL; 47 pSongName = NULL; 48 pSongAuthor = NULL; 49 pSongComment = NULL; 50 pSongType = NULL; 51 pSongPlayer = NULL; 52 53 pBigSampleBuffer = NULL; 54 pMixBlock = NULL; 55 56 replayRate = _replayRate; 57 innerSamplePos = 0; 58 nbDrum = 0; 59 pDrumTab = NULL; 60 setLoopMode(YMFALSE); 61 } 62 63 void CYmMusic::setTimeControl(ymbool bTime) 64 { 65 if (bTime) 66 attrib |= A_TIMECONTROL; 67 else 68 attrib &= (~A_TIMECONTROL); 69 } 70 71 CYmMusic::~CYmMusic() 72 { 73 stop(); 74 unLoad(); 75 } 76 77 void CYmMusic::setLoopMode(ymbool bLoopMode) 78 { 79 bLoop = bLoopMode; 80 } 81 82 void CYmMusic::setPlayerRate(ymint rate) 83 { 84 playerRate = rate; 85 } 86 87 ymu32 CYmMusic::getPos() 88 { 89 if (!isSeekable()) return 0; 90 if ((nbFrame>0) && (playerRate>0)) 91 { 92 return ((ymu32)currentFrame*1000)/(ymu32)playerRate; 93 } 94 else 95 return 0; 96 97 } 98 99 ymu32 CYmMusic::getMusicTime(void) 100 { 101 if ((nbFrame>0) && (playerRate>0)) 102 { 103 return ((ymu32)nbFrame*1000)/(ymu32)playerRate; 104 } 105 else 106 return 0; 107 108 } 109 110 ymu32 CYmMusic::setMusicTime(ymu32 time) 111 { 112 if (!isSeekable()) return 0; 113 ymu32 newTime = 0; 114 115 if ((songType>=YM_V2) && (songType<YM_VMAX)) 116 { 117 newTime = time; 118 if (newTime>=getMusicTime()) newTime = 0; 119 currentFrame = (newTime*(ymu32)playerRate)/1000; 120 } 121 else if ((songType>=YM_TRACKER1) && (songType<YM_TRACKERMAX)) 122 { 123 newTime = time; 124 if (newTime>=getMusicTime()) newTime = 0; 125 currentFrame = (newTime*(ymu32)playerRate)/1000; 126 } 127 128 return newTime; 129 } 130 131 void CYmMusic::getMusicInfo(ymMusicInfo_t *pInfo) 132 { 133 if (pInfo) 134 { 135 pInfo->pSongName = pSongName; 136 pInfo->pSongAuthor = pSongAuthor; 137 pInfo->pSongComment = pSongComment; 138 pInfo->pSongType = pSongType; 139 pInfo->pSongPlayer = pSongPlayer; 140 141 if (playerRate>0) 142 { 143 pInfo->musicTimeInMs = (ymu32(nbFrame) * 1000) / (ymu32)playerRate; 144 pInfo->musicTimeInSec = pInfo->musicTimeInMs / 1000; 145 } 146 else 147 { 148 pInfo->musicTimeInSec = 0; 149 pInfo->musicTimeInMs = 0; 150 } 151 } 152 } 153 154 155 void CYmMusic::setAttrib(ymint _attrib) 156 { 157 attrib = _attrib; 158 } 159 160 ymint CYmMusic::getAttrib(void) 161 { 162 return attrib; 163 } 164 165 ymbool CYmMusic::isSeekable(void) 166 { 167 return getAttrib()&A_TIMECONTROL; 168 } 169 170 void CYmMusic::setLastError(const char *pError) 171 { 172 pLastError = pError; 173 } 174 175 const char *CYmMusic::getLastError(void) 176 { 177 return pLastError; 178 } 179 180 void bufferClear(ymsample *pBuffer,ymint nbSample) 181 { 182 memset((void*)pBuffer,0,nbSample*sizeof(ymsample)); 183 } 184 185 ymbool CYmMusic::update(ymsample *sampleBuffer,ymint nbSample) 186 { 187 ymint sampleToCompute; 188 ymint vblNbSample; 189 190 191 if ((!bMusicOk) || 192 (bPause) || 193 (bMusicOver)) 194 { 195 bufferClear(sampleBuffer,nbSample); 196 if (bMusicOver) 197 return YMFALSE; 198 else 199 return YMTRUE; 200 } 201 202 if ((songType >= YM_MIX1) && (songType < YM_MIXMAX)) 203 { 204 stDigitMix(sampleBuffer,nbSample); 205 } 206 else if ((songType >= YM_TRACKER1) && (songType<YM_TRACKERMAX)) 207 { 208 ymTrackerUpdate(sampleBuffer,nbSample); 209 } 210 else 211 { 212 ymsample *pOut = sampleBuffer; 213 ymint nbs = nbSample; 214 vblNbSample = replayRate/playerRate; 215 do 216 { 217 // Nb de sample � calculer avant l'appel de Player 218 sampleToCompute = vblNbSample-innerSamplePos; 219 // Test si la fin du buffer arrive avant la fin de sampleToCompute 220 if (sampleToCompute>nbs) sampleToCompute = nbs; 221 innerSamplePos += sampleToCompute; 222 if (innerSamplePos>=vblNbSample) 223 { 224 player(); // Lecture de la partition (playerRate Hz) 225 innerSamplePos -= vblNbSample; 226 } 227 if (sampleToCompute>0) 228 { 229 ymChip.update(pOut,sampleToCompute); // YM Emulation. 230 pOut += sampleToCompute; 231 } 232 nbs -= sampleToCompute; 233 } 234 while (nbs>0); 235 } 236 237 238 return YMTRUE; 239 } 240 241 242 243 void CYmMusic::readYm6Effect(unsigned char *pReg,ymint code,ymint prediv,ymint count) 244 { 245 ymint voice; 246 ymint ndrum; 247 248 code = pReg[code]&0xf0; 249 prediv = (pReg[prediv]>>5)&7; 250 count = pReg[count]; 251 252 if (code&0x30) 253 { 254 ymu32 tmpFreq; 255 // Ici il y a un effet sur la voie: 256 257 voice = ((code&0x30)>>4)-1; 258 switch (code&0xc0) 259 { 260 case 0x00: // SID 261 case 0x80: // Sinus-SID 262 263 prediv = mfpPrediv[prediv]; 264 prediv *= count; 265 tmpFreq = 0; 266 if (prediv) 267 { 268 tmpFreq = 2457600L / prediv; 269 if ((code&0xc0)==0x00) 270 ymChip.sidStart(voice,tmpFreq,pReg[voice+8]&15); 271 else 272 ymChip.sidSinStart(voice,tmpFreq,pReg[voice+8]&15); 273 } 274 break; 275 276 case 0x40: // DigiDrum 277 ndrum = pReg[voice+8]&31; 278 if ((ndrum>=0) && (ndrum<nbDrum)) 279 { 280 prediv = mfpPrediv[prediv]; 281 prediv *= count; 282 if (prediv>0) 283 { 284 tmpFreq = 2457600L / prediv; 285 ymChip.drumStart(voice,pDrumTab[ndrum].pData,pDrumTab[ndrum].size,tmpFreq); 286 } 287 } 288 break; 289 290 case 0xc0: // Sync-Buzzer. 291 292 prediv = mfpPrediv[prediv]; 293 prediv *= count; 294 tmpFreq = 0; 295 if (prediv) 296 { 297 tmpFreq = 2457600L / prediv; 298 ymChip.syncBuzzerStart(tmpFreq,pReg[voice+8]&15); 299 } 300 break; 301 302 303 } 304 305 } 306 } 307 308 void CYmMusic::setVolume(ymint volume) 309 { 310 // ymChip.setGlobalVolume(volume); 311 } 312 313 void CYmMusic::player(void) 314 { 315 ymu8 *ptr; 316 ymu32 prediv; 317 ymint voice; 318 ymint ndrum; 319 320 321 if (currentFrame<0) currentFrame = 0; 322 323 if (currentFrame>=nbFrame) 324 { 325 if (bLoop) 326 { 327 currentFrame = loopFrame; 328 } 329 else 330 { 331 bMusicOver = YMTRUE; 332 ymChip.reset(); 333 return; 334 } 335 } 336 337 ptr = pDataStream+currentFrame*streamInc; 338 339 for (ymint i=0;i<=10;i++) 340 ymChip.writeRegister(i,ptr[i]); 341 342 ymChip.sidStop(0); 343 ymChip.sidStop(1); 344 ymChip.sidStop(2); 345 ymChip.syncBuzzerStop(); 346 347 //--------------------------------------------- 348 // Check digi-drum 349 //--------------------------------------------- 350 if (songType == YM_V2) // MADMAX specific ! 351 { 352 if (ptr[13]!=0xff) 353 { 354 ymChip.writeRegister(11,ptr[11]); 355 ymChip.writeRegister(12,0); 356 ymChip.writeRegister(13,10); // MADMAX specific !! 357 } 358 if (ptr[10]&0x80) // bit 7 volume canal C pour annoncer une digi-drum madmax. 359 { 360 ymint sampleNum; 361 ymu32 sampleFrq; 362 ymChip.writeRegister(7,ymChip.readRegister(7)|0x24) ; // Coupe TONE + NOISE canal C. 363 sampleNum = ptr[10]&0x7f; // Numero du sample 364 365 if (ptr[12]) 366 { 367 if (sampleNum < MAX_DIGIDRUM) 368 { 369 sampleFrq = (MFP_CLOCK / ptr[12]); 370 ymChip.drumStart( 2, // Voice C 371 sampleAdress[sampleNum], 372 sampleLen[sampleNum], 373 sampleFrq); 374 } 375 } 376 } 377 } 378 else if (songType >= YM_V3) 379 { 380 ymChip.writeRegister(11,ptr[11]); 381 ymChip.writeRegister(12,ptr[12]); 382 if (ptr[13]!=0xff) 383 { 384 ymChip.writeRegister(13,ptr[13]); 385 } 386 387 if (songType >= YM_V5) 388 { 389 ymint code; 390 391 if (songType == YM_V6) 392 { 393 readYm6Effect(ptr,1,6,14); 394 readYm6Effect(ptr,3,8,15); 395 } 396 else 397 { // YM5 effect decoding 398 399 //------------------------------------------------------ 400 // Sid Voice !! 401 //------------------------------------------------------ 402 code = (ptr[1]>>4)&3; 403 if (code!=0) 404 { 405 ymu32 tmpFreq; 406 voice = code-1; 407 prediv = mfpPrediv[(ptr[6]>>5)&7]; 408 prediv *= ptr[14]; 409 tmpFreq = 0; 410 if (prediv) 411 { 412 tmpFreq = 2457600L / prediv; 413 ymChip.sidStart(voice,tmpFreq,ptr[voice+8]&15); 414 } 415 } 416 417 //------------------------------------------------------ 418 // YM5 Digi Drum. 419 //------------------------------------------------------ 420 code = (ptr[3]>>4)&3; 421 if (code!=0) 422 { // Ici un digidrum demarre sur la voie voice. 423 voice = code-1; 424 ndrum = ptr[8+voice]&31; 425 if ((ndrum>=0) && (ndrum<nbDrum)) 426 { 427 ymu32 sampleFrq; 428 prediv = mfpPrediv[(ptr[8]>>5)&7]; 429 prediv *= ptr[15]; 430 if (prediv) 431 { 432 sampleFrq = MFP_CLOCK / prediv; 433 ymChip.drumStart(voice,pDrumTab[ndrum].pData,pDrumTab[ndrum].size,sampleFrq); 434 } 435 } 436 } 437 } 438 } 439 } 440 currentFrame++; 441 } 442 443 /* 444 445 x x x x x x x x r0 446 0 0 0 0 x x x x r1 // Special FX 1a 447 x x x x x x x x r2 448 0 0 0 0 x x x x r3 // Special FX 2a 449 x x x x x x x x r4 450 0 0 0 0 x x x x r5 451 0 0 0 x x x x x r6 // Special FX 1b 452 0 0 x x x x x x r7 453 0 0 0 x x x x x r8 // Special FX 2b 454 0 0 0 x x x x x r9 455 0 0 0 x x x x x r10 456 x x x x x x x x r11 457 x x x x x x x x r12 458 0 0 0 0 x x x x r13 459 0 0 0 0 0 0 0 0 r14 // Special FX 1c 460 0 0 0 0 0 0 0 0 r15 // Special FX 2c 461 462 463 Special Fx ?a 464 0 0 0 0 : No special FX running 465 0 0 0 1 : Sid Voice A 466 0 0 1 0 : Sid Voice B 467 0 0 1 1 : Sid Voice C 468 0 1 0 0 : Extended Fx voice A 469 0 1 0 1 : Digidrum voice A 470 0 1 1 0 : Digidrum voice B 471 0 1 1 1 : Digidrum voice C 472 1 0 0 0 : Extended Fx voice B 473 1 0 0 1 : Sinus SID voice A 474 1 0 1 0 : Sinus SID voice B 475 1 0 1 1 : Sinus SID voice C 476 1 1 0 0 : Extended Fx voice C 477 1 1 0 1 : Sync Buzzer voice A 478 1 1 1 0 : Sync Buzzer voice B 479 1 1 1 1 : Sync Buzzer voice C 480 481 482 483 */ 484 485 void CYmMusic::readNextBlockInfo(void) 486 { 487 nbRepeat--; 488 if (nbRepeat<=0) 489 { 490 mixPos++; 491 if (mixPos >= nbMixBlock) 492 { 493 mixPos = 0; 494 if (!bLoop) bMusicOver = YMTRUE; 495 } 496 nbRepeat = pMixBlock[mixPos].nbRepeat; 497 } 498 pCurrentMixSample = pBigSampleBuffer + pMixBlock[mixPos].sampleStart; 499 currentSampleLength = (pMixBlock[mixPos].sampleLength)<<12; 500 currentPente = (((ymu32)pMixBlock[mixPos].replayFreq)<<12) / PC_DAC_FREQ; 501 currentPos &= ((1<<12)-1); 502 } 503 504 void CYmMusic::stDigitMix(ymsample *pWrite16,ymint nbs) 505 { 506 507 508 if (bMusicOver) return; 509 510 if (mixPos == -1) 511 { 512 nbRepeat = -1; 513 readNextBlockInfo(); 514 } 515 516 if (nbs) do 517 { 518 519 ymint sa = (ymint)(ymsample)(pCurrentMixSample[currentPos>>12]<<8); 520 #ifdef _LINEAR_OVRS 521 ymint sb = sa; 522 if ((currentPos>>12)<((currentSampleLength>>12)-1)) 523 sb = (ymint)(ymsample)(pCurrentMixSample[(currentPos>>12)+1]<<8); 524 ymint frac = currentPos&((1<<12)-1); 525 sa += (((sb-sa)*frac)>>12); 526 #endif 527 *pWrite16++ = sa; 528 529 currentPos += currentPente; 530 if (currentPos>=currentSampleLength) 531 { 532 readNextBlockInfo(); 533 if (bMusicOver) return; 534 } 535 } 536 while (--nbs); 537 } 538 539 void CYmMusic::ymTrackerDesInterleave(void) 540 { 541 unsigned char *a0,*a1,*a2; 542 unsigned char *pNewBuffer; 543 ymint step; 544 ymu32 n1,n2; 545 546 547 if (attrib&A_STREAMINTERLEAVED) 548 { 549 a0 = pDataStream; 550 ymint size = sizeof(ymTrackerLine_t)*nbVoice*nbFrame; 551 pNewBuffer = (unsigned char*)malloc(size); 552 step = sizeof(ymTrackerLine_t)*nbVoice; 553 n1 = step; 554 a2 = pNewBuffer; 555 do 556 { 557 n2 = nbFrame; 558 a1 = a2; 559 do 560 { 561 *a1 = *a0++; 562 a1 += step; 563 } 564 while (--n2); 565 a2++; 566 } 567 while (--n1); 568 memcpy(pDataStream,pNewBuffer,size); 569 free(pNewBuffer); 570 attrib &= (~A_STREAMINTERLEAVED); 571 } 572 } 573 574 575 void CYmMusic::ymTrackerInit(ymint volMaxPercent) 576 { 577 ymint i,s; 578 ymint vol; 579 ymint scale; 580 ymsample *pTab; 581 582 583 584 for (i=0;i<MAX_VOICE;i++) 585 ymTrackerVoice[i].bRunning = 0; 586 587 ymTrackerNbSampleBefore = 0; 588 589 scale = (256*volMaxPercent) / (nbVoice*100); 590 pTab = ymTrackerVolumeTable; 591 592 // Construit la table de volume. 593 for (vol=0;vol<64;vol++) 594 { 595 for (s=-128;s<128;s++) 596 { 597 *pTab++ = (s*(ymint)scale*vol)/64; 598 } 599 } 600 601 // Des-interleave si necessaire. 602 ymTrackerDesInterleave(); 603 604 } 605 606 607 void CYmMusic::ymTrackerPlayer(ymTrackerVoice_t *pVoice) 608 { 609 ymint i; 610 ymTrackerLine_t *pLine; 611 612 613 pLine = (ymTrackerLine_t*)pDataStream; 614 pLine += (currentFrame*nbVoice); 615 for (i=0;i<nbVoice;i++) 616 { 617 ymint n; 618 pVoice[i].sampleFreq = ((ymint)pLine->freqHigh<<8) | pLine->freqLow; 619 if (pVoice[i].sampleFreq) 620 { 621 pVoice[i].sampleVolume = pLine->volume&63; 622 pVoice[i].bLoop = (pLine->volume&0x40); 623 n = pLine->noteOn; 624 if (n != 0xff) // Note ON. 625 { 626 pVoice[i].bRunning = 1; 627 pVoice[i].pSample = pDrumTab[n].pData; 628 pVoice[i].sampleSize = pDrumTab[n].size; 629 pVoice[i].repLen = pDrumTab[n].repLen; 630 pVoice[i].samplePos = 0; 631 } 632 } 633 else 634 { 635 pVoice[i].bRunning = 0; 636 } 637 pLine++; 638 } 639 640 currentFrame++; 641 if (currentFrame >= nbFrame) 642 { 643 if (!bLoop) 644 { 645 bMusicOver = YMTRUE; 646 } 647 currentFrame = 0; 648 } 649 } 650 651 652 void CYmMusic::ymTrackerVoiceAdd(ymTrackerVoice_t *pVoice,ymsample *pBuffer,ymint nbs) 653 { 654 ymsample *pVolumeTab; 655 ymu8 *pSample; 656 ymu32 samplePos; 657 ymu32 sampleEnd; 658 ymu32 sampleInc; 659 ymu32 repLen; 660 double step; 661 662 663 if (!(pVoice->bRunning)) return; 664 665 pVolumeTab = &ymTrackerVolumeTable[256*(pVoice->sampleVolume&63)]; 666 pSample = pVoice->pSample; 667 samplePos = pVoice->samplePos; 668 669 step = (double)(pVoice->sampleFreq<<YMTPREC); 670 step *= (double)(1<<ymTrackerFreqShift); 671 step /= (double)replayRate; 672 sampleInc = (ymu32)step; 673 674 sampleEnd = (pVoice->sampleSize<<YMTPREC); 675 repLen = (pVoice->repLen<<YMTPREC); 676 if (nbs>0) do 677 { 678 ymint va = pVolumeTab[pSample[samplePos>>YMTPREC]]; 679 #ifdef _LINEAR_OVRS 680 ymint vb = va; 681 if (samplePos < (sampleEnd-(1<<YMTPREC))) 682 vb = pVolumeTab[pSample[(samplePos>>YMTPREC)+1]]; 683 ymint frac = samplePos & ((1<<YMTPREC)-1); 684 va += (((vb-va)*frac)>>YMTPREC); 685 #endif 686 (*pBuffer++) += va; 687 688 samplePos += sampleInc; 689 if (samplePos>=sampleEnd) 690 { 691 if (pVoice->bLoop) 692 { 693 samplePos -= repLen; 694 } 695 else 696 { 697 pVoice->bRunning = 0; 698 return; 699 } 700 } 701 } 702 while (--nbs); 703 pVoice->samplePos = samplePos; 704 } 705 706 void CYmMusic::ymTrackerUpdate(ymsample *pBuffer,ymint nbSample) 707 { 708 ymint i; 709 ymint _nbs; 710 711 // Clear les buffers. 712 memset(pBuffer,0,sizeof(ymsample)*nbSample); 713 if (bMusicOver) return; 714 715 do 716 { 717 if (ymTrackerNbSampleBefore == 0) 718 { 719 // Lit la partition ymTracker 720 ymTrackerPlayer(ymTrackerVoice); 721 if (bMusicOver) return; 722 ymTrackerNbSampleBefore = YMTNBSRATE; 723 } 724 _nbs = ymTrackerNbSampleBefore; // nb avant playerUpdate. 725 if (_nbs>nbSample) _nbs = nbSample; 726 ymTrackerNbSampleBefore -= _nbs; 727 if (_nbs>0) 728 { 729 // Genere les samples. 730 for (i=0;i<nbVoice;i++) 731 { 732 ymTrackerVoiceAdd(&ymTrackerVoice[i],pBuffer,_nbs); 733 } 734 pBuffer += _nbs; 735 nbSample -= _nbs; 736 } 737 } 738 while (nbSample>0); 739 } 740