REDRIVER2/src_rebuild/GAME/C/XMPLAY.C

3094 lines
62 KiB
C++
Raw Normal View History

/*****************************************************************************
XMPLAY.C
XM (Extended MOD) Player code.
Backported version from PS2 and Driver 2 disassembly
Written by Jason Page
(c)1999 SCEE
****************************************************************************/
#include "TYPES.H"
#include "STRINGS.H"
#include "XMPLAY.H"
#include "XMCALLS.H"
#include "LIBETC.H"
#include "LIBSPU.H"
#include "LIBMATH.H"
/**** PSX SPECIFIC ****/
SpuVoiceAttr xm_g_s_attr; /* Structure for individual voice attributes*/
SpuVoiceAttr uxm_g_s_attr; /* Structure for individual voice attributes*/
SpuReverbAttr xm_r_attr; /* Structure for reverb */
unsigned long xm_l_vag_spu_addr[8][128]; /* Address in memory for first sound file */
/**** XM SPECIFIC ****/
int JP_Do_Nothing;
#define NTSCBPMLIMIT 150 //150 //750/5 //750/5
int BPMLimit;
int PALType;
int XM_SCAN = 0;
int JPError = 0;
char MonoMode = XM_STEREO;
XMCHANNEL *XMCU;
XMCHANNEL *XMC;
//XMHEADER XM_Header[8]; /* MAX 8 XM's files in memory at once */
XMHEADER *mh; /* Pointer used by internal routines */
XMHEADER *mhu; /* Pointer used by User routines */
//XMSONG XM_Song[24]; /* MAX 24 XM's playing at once */
XMSONG *ms; /* Pointer used by internal routines */
XMSONG *mu; /* Pointer used by User routines */
int XM_NSA = 0; // NEXT SONG ADDRESS
int XM_HA = 0; // NEXT HEADER Address;
u_char *XM_SngAddress[32];
u_char *XM_HeaderAddress[8];
//XMSONG *XM_SngAddress[24];
//#define getSWord(mpp) ((short)((u_char)(*(mpp))+(u_char)((*((mpp)+1))<<8)))
//#define getWord(mpp) ((u_short)((u_char)(*(mpp))+(u_char)((*((mpp)+1))<<8)))
#define getWord(mpp) ((u_short)((*(u_char *)(mpp)) + ((*((u_char *)(mpp) + 1)) << 8)))
#define getSWord(mpp) ((short)((*(u_char *)(mpp)) + ((*((u_char *)(mpp) + 1)) << 8)))
int CurrentCh;
short iVABID[8] = { -1,-1,-1,-1,-1,-1,-1,-1 };
2020-05-13 15:55:56 +02:00
short XMSongIDs[24] =
{ -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,
2020-05-13 15:55:56 +02:00
-1,-1,-1,-1,-1,-1,-1,-1};
2020-05-13 15:55:56 +02:00
short XMSPU_SFX[24] =
{ -1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,
2020-05-13 15:55:56 +02:00
-1,-1,-1,-1,-1,-1,-1,-1};
/*****************************************************************************
LINEAR FREQUENCY TABLE used to calculate final SPU pitch
*****************************************************************************/
u_short lintab[768] = {
16726,16741,16756,16771,16786,16801,16816,16832,16847,16862,16877,16892,16908,16923,16938
,16953,
16969,16984,16999,17015,17030,17046,17061,17076,17092,17107,17123,17138,17154,17169,17185
,17200,
17216,17231,17247,17262,17278,17293,17309,17325,17340,17356,17372,17387,17403,17419,17435
,17450,
17466,17482,17498,17513,17529,17545,17561,17577,17593,17608,17624,17640,17656,17672,17688
,17704,
17720,17736,17752,17768,17784,17800,17816,17832,17848,17865,17881,17897,17913,17929,17945
,17962,
17978,17994,18010,18027,18043,18059,18075,18092,18108,18124,18141,18157,18174,18190,18206
,18223,
18239,18256,18272,18289,18305,18322,18338,18355,18372,18388,18405,18421,18438,18455,18471
,18488,
18505,18521,18538,18555,18572,18588,18605,18622,18639,18656,18672,18689,18706,18723,18740
,18757,
18774,18791,18808,18825,18842,18859,18876,18893,18910,18927,18944,18961,18978,18995,19013
,19030,
19047,19064,19081,19099,19116,19133,19150,19168,19185,19202,19220,19237,19254,19272,19289
,19306,
19324,19341,19359,19376,19394,19411,19429,19446,19464,19482,19499,19517,19534,19552,19570
,19587,
19605,19623,19640,19658,19676,19694,19711,19729,19747,19765,19783,19801,19819,19836,19854
,19872,
19890,19908,19926,19944,19962,19980,19998,20016,20034,20052,20071,20089,20107,20125,20143
,20161,
20179,20198,20216,20234,20252,20271,20289,20307,20326,20344,20362,20381,20399,20418,20436
,20455,
20473,20492,20510,20529,20547,20566,20584,20603,20621,20640,20659,20677,20696,20715,20733
,20752,
20771,20790,20808,20827,20846,20865,20884,20902,20921,20940,20959,20978,20997,21016,21035
,21054,
21073,21092,21111,21130,21149,21168,21187,21206,21226,21245,21264,21283,21302,21322,21341
,21360,
21379,21399,21418,21437,21457,21476,21496,21515,21534,21554,21573,21593,21612,21632,21651
,21671,
21690,21710,21730,21749,21769,21789,21808,21828,21848,21867,21887,21907,21927,21946,21966
,21986,
22006,22026,22046,22066,22086,22105,22125,22145,22165,22185,22205,22226,22246,22266,22286
,22306,
22326,22346,22366,22387,22407,22427,22447,22468,22488,22508,22528,22549,22569,22590,22610
,22630,
22651,22671,22692,22712,22733,22753,22774,22794,22815,22836,22856,22877,22897,22918,22939
,22960,
22980,23001,23022,23043,23063,23084,23105,23126,23147,23168,23189,23210,23230,23251,23272
,23293,
23315,23336,23357,23378,23399,23420,23441,23462,23483,23505,23526,23547,23568,23590,23611
,23632,
23654,23675,23696,23718,23739,23761,23782,23804,23825,23847,23868,23890,23911,23933,23954
,23976,
23998,24019,24041,24063,24084,24106,24128,24150,24172,24193,24215,24237,24259,24281,24303
,24325,
24347,24369,24391,24413,24435,24457,24479,24501,24523,24545,24567,24590,24612,24634,24656
,24679,
24701,24723,24746,24768,24790,24813,24835,24857,24880,24902,24925,24947,24970,24992,25015
,25038,
25060,25083,25105,25128,25151,25174,25196,25219,25242,25265,25287,25310,25333,25356,25379
,25402,
25425,25448,25471,25494,25517,25540,25563,25586,25609,25632,25655,25678,25702,25725,25748
,25771,
25795,25818,25841,25864,25888,25911,25935,25958,25981,26005,26028,26052,26075,26099,26123
,26146,
26170,26193,26217,26241,26264,26288,26312,26336,26359,26383,26407,26431,26455,26479,26502
,26526,
26550,26574,26598,26622,26646,26670,26695,26719,26743,26767,26791,26815,26839,26864,26888
,26912,
26937,26961,26985,27010,27034,27058,27083,27107,27132,27156,27181,27205,27230,27254,27279
,27304,
27328,27353,27378,27402,27427,27452,27477,27502,27526,27551,27576,27601,27626,27651,27676
,27701,
27726,27751,27776,27801,27826,27851,27876,27902,27927,27952,27977,28003,28028,28053,28078
,28104,
28129,28155,28180,28205,28231,28256,28282,28307,28333,28359,28384,28410,28435,28461,28487
,28513,
28538,28564,28590,28616,28642,28667,28693,28719,28745,28771,28797,28823,28849,28875,28901
,28927,
28953,28980,29006,29032,29058,29084,29111,29137,29163,29190,29216,29242,29269,29295,29322
,29348,
29375,29401,29428,29454,29481,29507,29534,29561,29587,29614,29641,29668,29694,29721,29748
,29775,
29802,29829,29856,29883,29910,29937,29964,29991,30018,30045,30072,30099,30126,30154,30181
,30208,
30235,30263,30290,30317,30345,30372,30400,30427,30454,30482,30509,30537,30565,30592,30620
,30647,
30675,30703,30731,30758,30786,30814,30842,30870,30897,30925,30953,30981,31009,31037,31065
,31093,
31121,31149,31178,31206,31234,31262,31290,31319,31347,31375,31403,31432,31460,31489,31517
,31546,
31574,31602,31631,31660,31688,31717,31745,31774,31803,31832,31860,31889,31918,31947,31975
,32004,
32033,32062,32091,32120,32149,32178,32207,32236,32265,32295,32324,32353,32382,32411,32441
,32470,
32499,32529,32558,32587,32617,32646,32676,32705,32735,32764,32794,32823,32853,32883,32912
,32942,
32972,33002,33031,33061,33091,33121,33151,33181,33211,33241,33271,33301,33331,33361,33391
,33421
};
/*****************************************************************************
AMIGA FREQUENCY TABLE
*****************************************************************************/
#define LOGFAC 2*16
u_short logtab[] = {
LOGFAC * 907,LOGFAC * 900,LOGFAC * 894,LOGFAC * 887,LOGFAC * 881,LOGFAC * 875,LOGFAC * 868,LOGFAC * 862,
LOGFAC * 856,LOGFAC * 850,LOGFAC * 844,LOGFAC * 838,LOGFAC * 832,LOGFAC * 826,LOGFAC * 820,LOGFAC * 814,
LOGFAC * 808,LOGFAC * 802,LOGFAC * 796,LOGFAC * 791,LOGFAC * 785,LOGFAC * 779,LOGFAC * 774,LOGFAC * 768,
LOGFAC * 762,LOGFAC * 757,LOGFAC * 752,LOGFAC * 746,LOGFAC * 741,LOGFAC * 736,LOGFAC * 730,LOGFAC * 725,
LOGFAC * 720,LOGFAC * 715,LOGFAC * 709,LOGFAC * 704,LOGFAC * 699,LOGFAC * 694,LOGFAC * 689,LOGFAC * 684,
LOGFAC * 678,LOGFAC * 675,LOGFAC * 670,LOGFAC * 665,LOGFAC * 660,LOGFAC * 655,LOGFAC * 651,LOGFAC * 646,
LOGFAC * 640,LOGFAC * 636,LOGFAC * 632,LOGFAC * 628,LOGFAC * 623,LOGFAC * 619,LOGFAC * 614,LOGFAC * 610,
LOGFAC * 604,LOGFAC * 601,LOGFAC * 597,LOGFAC * 592,LOGFAC * 588,LOGFAC * 584,LOGFAC * 580,LOGFAC * 575,
LOGFAC * 570,LOGFAC * 567,LOGFAC * 563,LOGFAC * 559,LOGFAC * 555,LOGFAC * 551,LOGFAC * 547,LOGFAC * 543,
LOGFAC * 538,LOGFAC * 535,LOGFAC * 532,LOGFAC * 528,LOGFAC * 524,LOGFAC * 520,LOGFAC * 516,LOGFAC * 513,
LOGFAC * 508,LOGFAC * 505,LOGFAC * 502,LOGFAC * 498,LOGFAC * 494,LOGFAC * 491,LOGFAC * 487,LOGFAC * 484,
LOGFAC * 480,LOGFAC * 477,LOGFAC * 474,LOGFAC * 470,LOGFAC * 467,LOGFAC * 463,LOGFAC * 460,LOGFAC * 457,
LOGFAC * 453,LOGFAC * 450,LOGFAC * 447,LOGFAC * 443,LOGFAC * 440,LOGFAC * 437,LOGFAC * 434,LOGFAC * 431
};
/*****************************************************************************
VIBRATO SINE TABLE
*****************************************************************************/
static u_char VibratoTable[32] = {
0,24,49,74,97,120,141,161,
180,197,212,224,235,244,250,253,
255,253,250,244,235,224,212,197,
180,161,141,120,97,74,49,24
};
/*****************************************************************************
Interpolate
Requires:
p CurrentPos
p1 Start Pos
p2 End Pos
v1 Original Value
v2 Destination value
*****************************************************************************/
short Interpolate(short p, short p1, short p2, short v1, short v2)
{
short dp, dv, di;
if (p1 == p2) return v1;
dv = v2 - v1;
dp = p2 - p1;
di = p - p1;
return v1 + ((long)(di*dv) / dp);
}
/*****************************************************************************
GetLogPeriod
Returns AMIGA period, calculated from note and fine.
*****************************************************************************/
u_short GetLogPeriod(u_char note, u_short fine)
{
u_char n, o;
u_short p1, p2, i;
short j;
int f;
n = note % 12;
o = (note / 12);
i = fine >> 4;
i += (n << 3); /* n*8 + fine/16 */
p1 = logtab[i];
p2 = logtab[i + 1];
j = (Interpolate(fine / 16, 0, 15, p1, p2) >> o);
if (j == 0)
{
f = 1;
}
return(j);
}
/*****************************************************************************
JPGetPeriod
Returns linear period calculated from note and fine.
*****************************************************************************/
u_short JPGetPeriod(u_char note, short fine)
{
return((10L * 12 * 16 * 4) - ((u_short)note * 16 * 4) - (fine / 2) + 64);
}
/*****************************************************************************
GetPeriod
Returns period using either linear or Amiga(log) frequency
*****************************************************************************/
u_short GetPeriod(u_char note, u_short c2spd)
{
int a;
a = mh->flags;
if (ms->NotAmiga == 1)
return (JPGetPeriod(note, c2spd)); // Linear Freq table
else
return (GetLogPeriod(note, c2spd)); // Amiga Freq table
}
/*****************************************************************************
XM_Exit
Stop all tunes.
Clear SPU of all samples used.
*****************************************************************************/
void XM_Exit(void)
{
int i;
for (i = 0; i < XM_NSA; i++)
{
XM_Quit(i);
}
for (i = 0; i < 8; i++)
{
XM_CloseVAB(i);
}
}
void XM_CloseVAB(int VabID)
{
if (iVABID[VabID] != -1)
{
ClearSPU(VabID);
iVABID[VabID] = -1;
}
}
void XM_CloseVAB2(int VabID)
{
int Amount, i;
if (iVABID[VabID] != -1)
{
Amount = iVABID[VabID];
for (i = 0; i < (Amount); i++)
{
xm_l_vag_spu_addr[VabID][i] = 0;
}
iVABID[VabID] = -1;
}
}
/*****************************************************************************
XM_Update
Updates all songs and SFX.
*****************************************************************************/
void XM_Update(void)
{
2020-05-14 19:40:24 +02:00
//XM_SCAN=VSync(1);
if (JP_Do_Nothing) /* Failsafe switch - if called on interrupt */
return;
UpdateXMData();
2020-05-14 19:40:24 +02:00
//XM_SCAN=VSync(1)-XM_SCAN;
}
/*****************************************************************************
XM_PlayStart
Starts or continues a song.
Requires: Song_ID
*****************************************************************************/
void XM_PlayStart(int Song_ID, int PlayMask)
{
if (XMSongIDs[Song_ID] == -1)
return;
if (PlayMask != 0)
{
mu = (XMSONG*)(XM_SngAddress[Song_ID]);
// mu=&XM_Song[Song_ID];
mu->PlayMask = PlayMask;
mu->XMPlay = XM_PLAYING;
}
}
/*****************************************************************************
XM_PlayStop
Stops a currently playing song.
Silences SPU.
*****************************************************************************/
void XM_PlayStop(int Song_ID)
{
if (XMSongIDs[Song_ID] == -1)
return;
mu = (XMSONG*)(XM_SngAddress[Song_ID]);
// mu=&XM_Song[Song_ID];
if (!mu->XMPlay)
return;
mu->XMPlay = XM_STOPPED;
SilenceXM(Song_ID);
}
/*****************************************************************************
InitXMData
Initialise an XM file.
Requires:
Address of XM File
XM ID (up to user 0-23)
S3MPan switch (XM_UseS3MPanning or XM_UseXMPanning)
*****************************************************************************/
int InitXMData(u_char *mpp, int XM_ID, int S3MPan)
{
int t;
int a;
int c;
u_long b;
u_short b2;
// mhu=&XM_Header[XM_ID];
mhu = (XMHEADER*)(XM_HeaderAddress[XM_ID]);
mhu->S3MPanning = S3MPan;
mhu->version = getWord(mpp + MD_version);
mhu->songlength = getWord(mpp + MD_snglen);
mhu->restart = getWord(mpp + MD_restart);
mhu->XMChannels = getWord(mpp + MD_numchan);
mhu->numpat = getWord(mpp + MD_numpat);
mhu->numins = getWord(mpp + MD_numins);
mhu->flags = getWord(mpp + MD_flags);
mhu->tempo = getWord(mpp + MD_tempo);
mhu->bpm = getWord(mpp + MD_bpm);
mhu->XMPSXChannels = mhu->XMChannels;
2020-05-13 15:55:56 +02:00
if (mhu->XMPSXChannels > 24)
mhu->XMPSXChannels = 24;
for (t = 0; t < mhu->songlength; t++)
{
mhu->jorders[t] = ((u_char)*(mpp + MD_orders + t));
}
a = MD_patstrt;
c = mhu->numpat;
for (t = 0; t < c; t++)
{
mhu->JAP_PAT_ADDR[t] = (u_long*)(mpp + a); /* Store Pattern Addr's */
b = GetLong(mpp + a);
mhu->JAP_PAT_ADDR2[t] = (u_long*)(mpp + a + b); /* Store Pattern Addr's */
b2 = getWord(mpp + a + 7);
b += b2;
a += b;
}
c = mhu->numins;
for (t = 0; t < c; t++)
{
b = GetLong(mpp + a);
b2 = getWord(mpp + a + 27);
if (b2 != 0)
{
mhu->JAP_SampAddr[t] = (u_long*)(mpp + 29 + a);
mhu->JAP_SampHdrAddr[t] = (u_long*)(mpp + b + a);
b += (40 * b2);
}
else
{
mhu->JAP_SampAddr[t] = (u_long*)0xcdcdcdcd; //(u_long*)(mpp+29+a);
mhu->JAP_SampHdrAddr[t] = (u_long*)0x01234567; //(u_long*)(mpp+b+a);
}
mhu->JAP_InstrumentOffset[t] = (u_long*)(mpp + a); /* Store Instrument Addr's*/
a += b;
}
return(mhu->XMPSXChannels);
}
/*****************************************************************************
GetLong
Returns a long from given address
*****************************************************************************/
u_long GetLong(u_char *mpp)
{
u_char a;
u_char b;
u_char c;
u_char d;
u_long e;
a = *(mpp);
b = *(mpp + 1);
c = *(mpp + 2);
d = *(mpp + 3);
e = a + (b << 8) + (c << 16) + (d << 24);
return(e);
}
/*****************************************************************************
XM_OnceOffInit
Initialises system. Sets up for 50 or 60 fps playback
*****************************************************************************/
void XM_OnceOffInit(int PAL)
{
XM_NSA = 0;
JP_Do_Nothing = 0; /* Allow XM_Update to process */
// for (i=0;i<24;i++)
// {
// mu=&XM_Song[i];
// mu->Status=0; /* Turn off every song */
// }
BPMLimit = NTSCBPMLIMIT; /* Set Max limit for BPM */
if (PAL == XM_NTSC)
PALType = 0;
else if (PAL == XM_PAL)
{
PALType = 1; /* Set if PAL or NTSC */
// PALType=0; /* Set if PAL or NTSC */
// BPMLimit=125;
}
}
/*****************************************************************************
XM_Init
Initialise a song or SFX.
Requires:
VAB_ID (0-7) ID of VAB data (returned from PSX_Init)
XM_ID (0-23) Value corresponding to InitXMData call.
FirstCh First SPU Channel to play back on.
Loop XM_Loop or XM_NoLoop
PlayMask Bitpattern for which channels to process
PlayType XM_Music or XM_SFX (|0x80 will not start sound)
SFXNum Pattern number to play, if XM_SFX
*****************************************************************************/
int XM_Init(int VabID,int XM_ID,int SongID, int FirstCh,
int Loop,int PlayMask,int PlayType,int SFXNum)
{
2020-05-13 18:00:28 +02:00
int i;
int pmsk;
int Chn;
int t;
int SngID;
int fr;
if (SongID==-1)
{
SngID=GetFreeSongID();
if (SngID==-1)
return(-1);
}
else
{
if (SongID>=XM_NSA)
return(-1); // Not enough malloc'd song structures..
XM_Quit(SongID);
XMSongIDs[SongID]=0;
SngID=SongID;
}
JP_Do_Nothing=1;
mu=(XMSONG*)(XM_SngAddress[SngID]);
// mhu=&XM_Header[XM_ID];
mhu=(XMHEADER*)(XM_HeaderAddress[XM_ID]);
// mu=&XM_Song[SngID];
mu->XMPlay=XM_STOPPED;
mu->JUp=0;
mu->VabID=VabID;
mu->SongLoop=Loop;
mu->PlayMask=PlayMask;
mu->SFXNum=SFXNum;
mu->Status=PlayType&0x7f;
mu->HeaderNum=XM_ID;
mu->JBPM=0;
mu->PCounter=3;
mu->reppos=mhu->restart;
mu->repcnt=0;
mu->CurPos=0;
mu->SongPos=0;
if ((PlayType&0x7f)==XM_Music)
{
mu->SongPos=SFXNum;
mu->CurrentStart=SFXNum;
}
mu->SongSpeed=mhu->tempo;
mu->vbtick=mu->SongSpeed;
mu->SongBPM=mhu->bpm;
mu->NotAmiga=mhu->flags;
mu->patdly=0;
mu->patdly2=0;
mu->SongVolume=128;
mu->MasterVolume=128;
mu->FirstCh=FirstCh;
mu->PatternPos=0;
mu->posjmp=1; /* <- make sure the player fetches the first note */
mu->patbrk=0;
mu->PlayNext=-1;
mu->BPlayNext=-1;
mu->BPlayFlag=0;
mu->UserPan=0; /* Default Middle */
mu->MaxChans=0;
if (FirstCh!=-1)
{
Chn=FirstCh;
for(i=0;i<mhu->XMPSXChannels;i++)
{
pmsk=mu->PlayMask;
pmsk&=(1<<i);
if (pmsk!=0)
{
XMCU=&mu->XM_Chnl[i]; /* Build list of SPU Channels to use */
XMCU->SPUChannel=Chn; /* Depending on bitmask */
mu->MaxChans++;
2020-05-13 15:55:56 +02:00
if (Chn<24)
InitSPUChannel(Chn);
Chn++;
}
}
}
else
{
for(i=0;i<mhu->XMPSXChannels;i++)
{
pmsk=mu->PlayMask;
pmsk&=(1<<i);
if (pmsk!=0)
{
XMCU=&mu->XM_Chnl[i]; /* Build list of SPU Channels to use */
fr=0;
2020-05-13 15:55:56 +02:00
for (t=0;t<24;t++)
{
if (XMSPU_SFX[t]==0)
{
XMSPU_SFX[t]=SngID+1;
XMCU->SPUChannel=t; /* Depending on bitmask */
mu->MaxChans++;
InitSPUChannel(t);
fr=1;
break;
}
}
if (fr==0)
{
JPClearSPUFlags(SngID+1);
XMSongIDs[SngID]=-1;
JP_Do_Nothing=0;
return (-2); /* NOT ENOUGH FREE CHANNELS */
}
}
}
}
for(i=0;i<mhu->XMChannels;i++)
{
mu->XM_Chnl[i].UserVol=0;
mu->XM_Chnl[i].kick=0;
mu->XM_Chnl[i].tmpvolume=0+64;
mu->XM_Chnl[i].retrig=0;
mu->XM_Chnl[i].wavecontrol=0;
mu->XM_Chnl[i].glissando=0;
mu->XM_Chnl[i].panning=128;
mu->XM_Chnl[i].note=0;
mu->XM_Chnl[i].vol=0;
mu->XM_Chnl[i].eff=0;
mu->XM_Chnl[i].dat=0;
mu->XM_Chnl[i].ins=0;
mu->XM_Chnl[i].ChDead=1;
mu->XM_Chnl[i].nothing=1;
mu->XM_Chnl[i].OldLVol=-1;
mu->XM_Chnl[i].OldRVol=-1;
mu->XM_Chnl[i].OldPeriod=0;
mu->XM_Chnl[i].sample=254;
mu->XM_Chnl[i].OldSample=-1;
mu->XM_Chnl[i].Dolby=XM_DOLBY_OFF;
}
if (PlayType<0x80)
mu->XMPlay=XM_PLAYING;
JP_Do_Nothing=0;
return(SngID);
}
/*****************************************************************************
JPlayNote
Decode Pattern data.
Setup note/instrument/volume/effect
Return amount of bytes read.
*****************************************************************************/
int JPlayNote(u_char *j, int pmsk)
{
u_char b;
int ret = 1;
u_char note = 0;
b = *j;
XMC->eff = 0;
XMC->vol = 0;
XMC->dat = 0;
if (b == 0x80)
{
XMC->nothing = 1;
return(ret);
}
j++;
if ((b & 0x80) != 0)
{
if ((b & 1) != 0)
{
if (pmsk != 0)
{
XMC->nothing = 0;
note = *(j);
SetNote(note - 1);
}
j++;
ret++;
}
if ((b & 2) != 0)
{
if (pmsk != 0)
{
XMC->ins = *(j);
SetInstr(XMC->ins - 1);
}
j++;
ret++;
}
if (((b & 3) != 0) && (note != 97) && (pmsk != 0))
SetPer(); /* Set Pitch if new instrument/note */
if ((b & 4) != 0)
{
XMC->nothing = 0;
XMC->vol = *(j);
j++;
ret++;
}
if ((b & 8) != 0)
{
XMC->nothing = 0;
XMC->eff = *(j);
j++;
ret++;
}
if ((b & 16) != 0)
{
XMC->dat = *(j);
ret++;
}
}
else
{
XMC->nothing = 0;
note = b;
if (pmsk != 0)
{
SetNote(note - 1);
XMC->ins = *(j);
SetInstr(XMC->ins - 1);
if (note != 97)
SetPer();
XMC->vol = *(j + 1);
XMC->eff = *(j + 2);
XMC->dat = *(j + 3);
}
ret = 5;
}
return(ret);
}
/*****************************************************************************
JPlayEffects
Checks if any effects to be played.
Calls relevent routines if thay are.
*****************************************************************************/
void JPlayEffects(void)
{
u_char note=0;
u_char ins=0;
u_char vol=0;
u_char eff=0;
u_char dat=0;
note=XMC->note;
vol=XMC->vol;
eff=XMC->eff;
dat=XMC->dat;
ins=XMC->ins;
XMC->ownper=0;
XMC->ownvol=0;
switch(vol>>4)
{
case 0x6:
if(vol&0xf)
SPE(0xa,vol&0xf); /* Volume slide down */
break;
case 0x7:
if(vol&0xf)
SPE(0xa,vol<<4); /* Volume slide up */
break;
case 0x8:
SPE (0xe,0xb0 | (vol&0xf)); /* Fine volume slide down */
break;
case 0x9:
SPE (0xe,0xa0 | (vol&0xf)); /* Fine volume slide up */
break;
case 0xa:
SPE (0x4,vol<<4); /* Set Vibrato speed */
break;
case 0xb:
SPE (0x4,vol&0xf); /* Vibrato */
break;
case 0xc:
XMC->panning=(vol&0xf)<<4; /* Set Panning */
break;
case 0xd:
if(vol&0xf)
DoXMPanSlide(vol&0xf); /* Pan slide left */
break;
case 0xe:
if(vol&0xf)
DoXMPanSlide(vol<<4); /* Pan slide right */
break;
case 0xf:
SPE (0x3,vol<<4); /* Portamento */
break;
default:
if(vol>=0x10 && vol<=0x50)
{
SPE (0xc,vol-0x10); /* Set volume */
}
}
// if (eff!=0)
// {
switch(eff)
{
case 'G'-55: /* G - set global volume */
ms->SongVolume=dat*2;
break;
case 'H'-55: /* H - global volume slide */
break;
case 'K'-55: /* K - keyoff */
SetNote(96);
break;
case 'L'-55: /* L - set envelope position */
break;
case 'P'-55: /* P - panning slide */
XMC->panning=dat;
// DoXMPanSlide(dat);
break;
case 'R'-55: /* R - multi retrig note */
DoS3MRetrig(dat);
break;
case 'T'-55:
SPE (0x6,dat);
break;
case 'X'-55:
break;
default:
if(eff<=0xf)
SPE (eff,dat); /* Do most of the effects */
break;
}
// }
if(!XMC->ownper)
{
XMC->period=XMC->tmpperiod;
}
if(!XMC->ownvol)
{
XMC->volume=XMC->tmpvolume;
}
}
/*****************************************************************************
SPE
This is the bit that does most of the updating of effects
*****************************************************************************/
void SPE (u_char eff,u_char dat)
{
int hi;
int lo;
switch(eff)
{
case XMEF_APPREGIO: /* 0 */
Arpeggio(dat);
break;
case XMEF_PORTUP: /* 1 */
if(dat!=0)
XMC->slidespeed=((u_short)(dat))<<2;
if(ms->vbtick)
{
XMC->tmpperiod-=XMC->slidespeed;
}
break;
case XMEF_PORTDOWN: /* 2 */
if(dat!=0)
XMC->slidespeed=(u_short)(dat)<<2;
if(ms->vbtick)
{
XMC->tmpperiod+=XMC->slidespeed;
}
break;
case XMEF_TONEPORT: /* 3 */
XMC->kick=0;
if(dat!=0)
{
XMC->portspeed=dat;
XMC->portspeed<<=2;
}
DoToneSlide();
XMC->ownper=1;
break;
case XMEF_VIBRATO: /* 4 */
if(dat&0x0f) XMC->vibdepth=dat&0x0f;
if(dat&0xf0) XMC->vibspd=(dat&0xf0)>>2;
DoVibrato();
XMC->ownper=1;
break;
case XMEF_PORT_VOLSLD: /* 5 */
XMC->kick=0;
DoToneSlide();
if (dat==0)
dat=XMC->oldvslide;
XMC->oldvslide=dat;
DoVolSlide(dat);
XMC->ownper=1;
break;
case XMEF_VIB_VOLSLD: /* 6 */
DoVibrato();
if (dat==0)
dat=XMC->oldvslide;
XMC->oldvslide=dat;
DoVolSlide(dat);
XMC->ownper=1;
break;
case XMEF_TREMELO: /* 7 */
if(dat&0x0f) XMC->trmdepth=dat&0xf;
if(dat&0xf0) XMC->trmspd=(dat&0xf0)>>2;
DoTremolo();
XMC->ownvol=1;
break;
case XMEF_PANPOS: /* 8 */
XMC->panning=dat;
break;
case XMEF_SAMPOFFSET: /* 9 */
if(ms->vbtick)
break;
if(dat==0)
break;
XMC->SOffset=dat;
XMC->SOffset*=256;
XMC->SOffset*=2;
XMC->SOffset/=7;
XMC->SOffset*=2;
XMC->SOffset&=(~31);
break;
case XMEF_VOLSLD: /* A */
if (dat==0)
dat=XMC->oldvslide;
XMC->oldvslide=dat;
DoVolSlide(dat);
break;
case XMEF_POSJMP: /* B */
if(ms->patdly2)
break;
ms->patbrk=0;
ms->SongPos=dat-1;
ms->posjmp=2;
break;
case XMEF_VOLUME: /* C */
if(ms->vbtick)
break;
if (dat==0xff) // 0xff = set user jump flag
ms->BPlayFlag=1;
else if (dat==0xfe) // 0xfe = clear user jump flag
ms->BPlayFlag=0;
else if (dat==0xfd) // 0xfd = force tune to end.
ms->BPlayFlag=2;
else if (dat==0xfc)
XMC->Dolby=XM_DOLBY_OFF;
else if (dat==0xfb)
XMC->Dolby=XM_DOLBY_LEFT;
else if (dat==0xfa)
XMC->Dolby=XM_DOLBY_RIGHT;
else
{
if(dat>64) dat=64;
XMC->tmpvolume=dat+64;
}
break;
case XMEF_PATBREAK: /* D */
if(ms->patdly2)
break;
hi=(dat&0xf0)>>4;
lo=(dat&0xf);
ms->patbrk=((hi*10)+lo)+1;
if(ms->patbrk>(ms->numrow+1))
ms->patbrk=ms->numrow+1;
ms->posjmp=2;
break;
case XMEF_E: /* E */
DoEEffects(dat);
break;
case XMEF_SETSPEED: /* F */
if(ms->vbtick || ms->patdly2)
break;
if(dat<0x20)
{
ms->SongSpeed=dat; // Speed
ms->vbtick=0;
}
else
{
ms->SongBPM=dat; // BPM
ms->vbtick=0;
}
break;
default:
break;
}
}
/*****************************************************************************
DoEEffects
Process effects if 0xe command used.
*****************************************************************************/
void DoEEffects(u_char dat)
{
u_char nib;
nib = dat & 0xf;
switch (dat >> 4)
{
case 0x0: /* 0 filter toggle */
break;
case XMEF_E_FINESLD_UP: /* 1 Fineslide up */
if (!ms->vbtick)
{
if (nib == 0)
nib = XMC->oldfslide;
XMC->oldfslide = nib;
XMC->tmpperiod += (nib << 2);
}
break;
case XMEF_E_FINESLD_DOWN: /* 2 Fineslide down */
if (!ms->vbtick)
{
if (nib == 0)
nib = XMC->oldfslide;
XMC->oldfslide = nib;
XMC->tmpperiod -= (nib << 2);
}
break;
case XMEF_E_GLISSANDO: /* 3 Glissando ctrl */
XMC->glissando = nib;
break;
case XMEF_E_VIB_WAVE: /* 4 Set vibrato waveform */
XMC->wavecontrol &= 0xf0;
XMC->wavecontrol |= nib;
break;
case XMEF_E_FINETUNE: /* 5 Set finetune */
break;
case XMEF_E_PATLOOP: /* 6 Set patternloop */
if (ms->vbtick)
break;
if (nib)
{
if (ms->repcnt > 0)
ms->repcnt--; /* already looping, decrease counter */
else
ms->repcnt = nib; /* not yet looping, so set repcnt */
if (ms->repcnt) /* jump to reppos if repcnt>0 */
ms->PatternPos = ms->reppos;
}
else
{
ms->reppos = ms->PatternPos - 1; /* set reppos */
}
break;
case XMEF_E_TREM_WAVE: /* 7 Set tremolo waveform */
XMC->wavecontrol &= 0x0f;
XMC->wavecontrol |= nib << 4;
break;
case XMEF_E_NOTUSED: /* 8 Not used */
XMC->panning = nib << 4;
break;
case XMEF_E_RETRIG: /* 9 Retrig note */
/* only retrigger if
data nibble > 0 */
if (nib > 0)
{
if (XMC->retrig == 0)
{
XMC->kick = 1;
XMC->retrig = nib;
}
XMC->retrig--; /* countdown */
}
break;
case XMEF_E_FINEVOL_UP: /* A - Fine volume slide up */
if (ms->vbtick)
break;
if (nib == 0)
nib = XMC->oldfvslide;
XMC->oldfvslide = nib;
XMC->tmpvolume += nib;
if (XMC->tmpvolume > 128) XMC->tmpvolume = 128;
break;
case XMEF_E_FINEVOL_DOWN: /* B - Fine volume slide down */
if (ms->vbtick)
break;
if (nib == 0)
nib = XMC->oldfvslide;
XMC->oldfvslide = nib;
XMC->tmpvolume -= nib;
if (XMC->tmpvolume < 64) XMC->tmpvolume = 64;
break;
case XMEF_E_CUTNOTE: /* C - Cut note */
if (ms->vbtick >= nib)
{
XMC->tmpvolume = 64;
}
break;
case XMEF_E_NOTEDELAY: /* D - Note delay */
if (ms->vbtick == nib)
{
XMC->kick = 1;
}
else XMC->kick = 0;
break;
case XMEF_E_PATDELAY: /* E - Pattern delay */
if (ms->vbtick)
break;
if (!ms->patdly2)
ms->patdly = nib + 1; /* only once (when vbtick=0) */
break;
case 0xf: /* invert loop, not supported */
break;
}
}
/*****************************************************************************
SetNote
Set note if a new note is to processed
*****************************************************************************/
void SetNote(u_char note)
{
u_char* j;
u_char vf;
if (CurrentCh > 23)
return;
if (note == 96)
{
XMC->keyon = 0;
if (XMC->sample == 254)
{
XMC->tmpvolume = 64; /* Force 0 vol if no envelope used */
}
else
{
j = (u_char*)mh->JAP_InstrumentOffset[XMC->sample];
vf = *((u_char*)j + 233);
if (!(vf & EF_ON))
{
XMC->tmpvolume = 64; /* Force 0 vol if no envelope used */
}
}
}
else
{
XMC->note = note;
XMC->kick = 1;
if (!(XMC->wavecontrol & 0x80)) /* retrig tremolo and vibrato waves ? */
XMC->trmpos = 0;
if (!(XMC->wavecontrol & 0x08))
XMC->vibpos = 0;
}
}
/*****************************************************************************
SetInstr
Set instrument if new one to be processed
Copy values from XM data (transpose, fine,volume...)
*****************************************************************************/
void SetInstr(u_char inst)
{
u_long *j;
u_char j2;
short ddd;
// if (ms->SongPos==4)
// {
// inst+=0;
// }
if (CurrentCh > 23)
return;
if (!XMC->kick)
return; // Instrument but no note...
if (inst == 255)
{
inst = XMC->sample; // If instrument=0, use last instrument.
}
if (inst >= mh->numins) return; // No Instrument available
XMC->sample = inst;
/*
j=mh->JAP_InstrumentOffset[inst];
j2=*((u_char*)j+(XMC->note+33));
j3=*((u_char*)j);
if (j2!=0)
{
ddd=243*j2; // Not using instrument 0
}
// if (j2>=j3)
// return;
*/
j = mh->JAP_SampHdrAddr[inst];
if (j == (u_long*)0x01234567)
return;
j2 = *((u_char*)j + 12);
XMC->tmpvolume = j2 + 64;
XMC->volume = j2 + 64;
XMC->vol = j2 + 0x10;
XMC->retrig = 0;
if (mh->S3MPanning == XM_UseXMPanning)
XMC->panning = *((u_char*)j + 15);
XMC->transpose = *((u_char*)j + 16);
ddd = (*((u_char*)j + 13));
ddd += 128;
XMC->c2spd = ddd;
XMC->c2spd &= 255;
}
/*****************************************************************************
SetPer
Init insturment envelopes, period...
Called if either SetInst or SetNote are called
*****************************************************************************/
void SetPer(void)
{
u_long *j;
u_short period;
u_char a;
if (CurrentCh > 23)
return;
a = XMC->note;
a += XMC->transpose;
// a+=jtt;
period = GetPeriod(a, XMC->c2spd);
//if (XMC->sample==5)
//FntPrint("%d,%d,%d=%d\n",XMC->note,XMC->transpose,a,period);
XMC->wantedperiod = period;
XMC->tmpperiod = period;
XMC->OldPeriod = 0;
XMC->SOffset = 0;
// XMC->OldLVol=-1;
// XMC->OldRVol=-1;
if (XMC->OldSample != XMC->sample)
{
XMC->OldSample = XMC->sample;
j = mh->JAP_InstrumentOffset[XMC->sample];
XMC->keyoffspd = *((u_char*)j + 239);
XMC->keyoffspd; //*=1; //4;
XMC->envflg = *((u_char*)j + 233);
if (XMC->envflg & EF_ON)
{
XMC->envpts = *((u_char*)j + 225);
XMC->envsus = *((u_char*)j + 227);
XMC->envbeg = *((u_char*)j + 228);
XMC->envend = *((u_char*)j + 229);
}
XMC->panenvflg = *((u_char*)j + 234);
if (XMC->panenvflg & EF_ON)
{
XMC->panenvpts = *((u_char*)j + 226);
XMC->panenvsus = *((u_char*)j + 230);
XMC->panenvbeg = *((u_char*)j + 231);
XMC->panenvend = *((u_char*)j + 232);
}
}
if (XMC->panenvflg & EF_ON)
{
XMC->panenvp = 0;
XMC->panenva = 0;
XMC->panenvb = 1;
}
if (XMC->envflg & EF_ON)
{
XMC->envp = 0;
XMC->enva = 0;
XMC->envb = 1;
}
}
/*****************************************************************************
Arpeggio
EFFECT - Modify pitch
*****************************************************************************/
void Arpeggio(u_char dat)
{
u_char note;
note = XMC->note;
if (dat != 0)
{
switch (ms->vbtick % 3)
{
case 1:
note += (dat >> 4);
break;
case 2:
note += (dat & 0xf);
break;
}
XMC->period = GetPeriod(note + XMC->transpose, XMC->c2spd);
XMC->ownper = 1;
}
}
/*****************************************************************************
DoVolSlide
EFFECT - Either +/- 0-0xf to volume
*****************************************************************************/
void DoVolSlide(u_char dat)
{
if (!ms->vbtick) return; /* do not update when vbtick==0 */
XMC->tmpvolume += dat >> 4; /* volume slide */
if (XMC->tmpvolume > 128) XMC->tmpvolume = 128;
XMC->tmpvolume -= dat & 0xf;
if (XMC->tmpvolume < 64) XMC->tmpvolume = 64;
}
/*****************************************************************************
DoXMPanSlide
EFFECT - Pan slide
*****************************************************************************/
void DoXMPanSlide(u_char inf)
{
u_char lo, hi;
short pan;
if (inf != 0) XMC->pansspd = inf;
else inf = XMC->pansspd;
if (!ms->vbtick) return;
lo = inf & 0xf;
hi = inf >> 4;
/* slide right has absolute priority: */
if (hi)
lo = 0;
pan = XMC->panning;
pan -= lo;
pan += hi;
if (pan < 0) pan = 0;
if (pan > 255) pan = 255;
XMC->panning = pan;
}
/*****************************************************************************
DoS3MRetrig
EFFECT - Pan slide
*****************************************************************************/
void DoS3MRetrig(u_char inf)
{
u_char hi,lo;
hi=inf>>4;
lo=inf&0xf;
if(lo)
{
XMC->s3mrtgslide=hi;
XMC->s3mrtgspeed=lo;
}
if(hi)
{
XMC->s3mrtgslide=hi;
}
/* only retrigger if lo nibble > 0 */
if(XMC->s3mrtgspeed>0)
{
if(XMC->retrig==0)
{
/* when retrig counter reaches 0,
reset counter and restart the sample */
XMC->kick=1;
XMC->retrig=XMC->s3mrtgspeed;
if(ms->vbtick)
{ /* don't slide on first retrig */
switch(XMC->s3mrtgslide)
{
case 1:
case 2:
case 3:
case 4:
case 5:
XMC->tmpvolume-=(1<<(XMC->s3mrtgslide-1));
break;
case 6:
XMC->tmpvolume=(2*XMC->tmpvolume)/3;
break;
case 7:
XMC->tmpvolume=XMC->tmpvolume>>1;
if (XMC->tmpvolume<64)
XMC->tmpvolume=64;
break;
case 9:
case 0xa:
case 0xb:
case 0xc:
case 0xd:
XMC->tmpvolume+=(1<<(XMC->s3mrtgslide-9));
break;
case 0xe:
XMC->tmpvolume=(3*XMC->tmpvolume)/2;
break;
case 0xf:
XMC->tmpvolume=XMC->tmpvolume<<1;
break;
}
if(XMC->tmpvolume<64) XMC->tmpvolume=64;
if(XMC->tmpvolume>128) XMC->tmpvolume=128;
}
}
XMC->retrig--; /* countdown */
}
}
/*****************************************************************************
DoToneSlide
EFFECT
*****************************************************************************/
void DoToneSlide(void)
{
int dist;
/* We have to slide XMC->period towards XMC->wantedperiod, so
compute the difference between those two values */
dist=XMC->period-XMC->wantedperiod;
if(dist==0 || XMC->portspeed>abs(dist))/* if they are equal */
{ /* or if portamentospeed is too big */
XMC->period=XMC->wantedperiod; /* make tmpperiod equal tperiod */
}
else if(dist>0)
{ /* dist>0 ? */
XMC->period-=XMC->portspeed; /* then slide up */
}
else
XMC->period+=XMC->portspeed; /* dist<0 -> slide down */
XMC->tmpperiod=XMC->period; /* Store new period */
}
/*****************************************************************************
DoVibrato
EFFECT
*****************************************************************************/
void DoVibrato(void)
{
u_char q;
u_short temp=0;
q=(XMC->vibpos>>2)&0x1f;
switch(XMC->wavecontrol&3)
{
case 0: /* sine */
temp=VibratoTable[q];
break;
case 1: /* ramp down */
q<<=3;
if(XMC->vibpos>127)
q=255-q;
temp=q;
break;
case 2: /* square wave */
temp=255;
break;
}
temp*=XMC->vibdepth;
temp>>=7;
temp<<=2;
if(XMC->vibpos<128)
XMC->period=XMC->tmpperiod+temp;
else
XMC->period=XMC->tmpperiod-temp;
if(ms->vbtick) XMC->vibpos+=XMC->vibspd; /* do not update when vbtick==0 */
}
/*****************************************************************************
DoTremelo
EFFECT
*****************************************************************************/
void DoTremolo(void)
{
u_char q;
u_short temp = 0;
q = (XMC->trmpos >> 2) & 0x1f;
switch ((XMC->wavecontrol >> 4) & 3)
{
case 0: /* sine */
temp = VibratoTable[q];
break;
case 1: /* ramp down */
q <<= 3;
if (XMC->trmpos > 127) q = 255 - q;
temp = q;
break;
case 2: /* square wave */
temp = 255;
break;
}
temp *= XMC->trmdepth;
temp >>= 6;
if (XMC->trmpos < 128)
{
XMC->volume = XMC->tmpvolume + temp;
if (XMC->volume > 128) XMC->volume = 128;
}
else
{
XMC->volume = XMC->tmpvolume - temp;
if (XMC->volume < 64) XMC->volume = 64;
}
if (ms->vbtick) XMC->trmpos += XMC->trmspd; /* do not update when vbtick==0 */
}
/*****************************************************************************
DoPan
Calculate panning using current envelope panning and instrument pan
*****************************************************************************/
short DoPan(short envpan,short pan)
{
return(pan + (((envpan-128)*(128-abs(pan-128)))/128));
}
/*****************************************************************************
DoVol
Calculate volume using
a Fade volume
b Envelope volume
c Instrument volume
*****************************************************************************/
short DoVol(u_long a,short b,short c)
{
a*=b;
a*=c;
a>>=23;
return(a);
}
/*****************************************************************************
UpdateXMData
This does everything
*****************************************************************************/
void UpdateXMData(void)
{
int SC;
if(JP_Do_Nothing) /* Failsafe switch - if called on interrupt */
return;
for (SC=0;SC<XM_NSA;SC++)
{
ms=(XMSONG*)(XM_SngAddress[SC]);
// ms=&XM_Song[SC];
UpdateWithTimer(SC);
if (ms->PCounter==5)
{
UpdateWithTimer(SC);
ms->PCounter=0;
}
}
}
int tt=0;
int aa=0;
void UpdateWithTimer(int SC)
{
aa++;
ms=(XMSONG*)(XM_SngAddress[SC]);
if (ms->XMPlay!=XM_PLAYING)
return;
if (PALType==1)
ms->PCounter++; /* PAL Mode stuff...bodge timer */
mh=(XMHEADER*)(XM_HeaderAddress[ms->HeaderNum]);
ms->JBPM+=ms->SongBPM;
if (ms->JBPM<BPMLimit) /* Time to process data ? */
{
if ((ms->JUp==0)&&(PALType!=0))
{
ms->JUp=1;
UpdateEffs(); /* If nothing else to do for a frame */
ApplyEffs(); /* (spread out processing time */
UpdateHardware(); /* Update SPU */
}
return;
}
ms->JBPM-=BPMLimit; /* YES! Update song/hardware */
XM_DoFullUpdate(SC);
}
void XM_DoFullUpdate(int SC)
{
ms = (XMSONG*)(XM_SngAddress[SC]);
// ms=&XM_Song[SC];
if (ms->XMPlay == XM_PLAYING)
{
// mh=&XM_Header[ms->HeaderNum];
mh = (XMHEADER*)(XM_HeaderAddress[ms->HeaderNum]);
if (ms->JUp == 0)
{
// UpdatePatternData(SC);
UpdateEffs(); /* Do this if not done before in spare frame*/
ApplyEffs();
}
ms->JUp = 0; /* Clear update flag */
UpdateHardware(); /* Update SPU */
if (ms->vbtick == 1) /* Check for zero volume,keyed off channels*/
CurrentKeyStat(); /* BUT not on first tick - wait for keyons */
/****
Update song at the end - so it doesn't have to process so much in
a frame. This does mean that what you are hearing the notes processed
a frame ago. But saves on processing time due to not keying on/initialising
channels AND processing song data AND processing effects at once.
****/
UpdatePatternData(SC);
}
// UpdateHardware(); /* Update SPU */
}
void UpdatePatternData(int SC)
{
int SP, t, pmsk;
int FindOffset;
u_char *pataddr;
u_char patdat;
if (++ms->vbtick >= ms->SongSpeed)
{
ms->PatternPos++; /* Time to read more pattern data */
ms->vbtick = 0;
if (ms->patdly)
{
ms->patdly2 = ms->patdly;
ms->patdly = 0;
}
if (ms->patdly2)
{
/* patterndelay active */
if (--ms->patdly2)
{
ms->PatternPos--; /* so turn back PatternPos by 1 */
}
}
/*
Do we have to get a new patternpointer ?
(when PatternPos reaches, say 64 or when a patternbreak is active)
*/
FindOffset = 0;
if (ms->PatternPos == ms->numrow)
ms->posjmp = 2;
if (ms->BPlayFlag == 2)
{
XM_Quit(SC);
return;
}
if (ms->posjmp)
{
if ((ms->posjmp == 2) && (ms->Status == XM_SFX)) /* SFX ? */
{
// t=ms->posjmp;
// t=ms->patbrk;
// ms->posjmp--;
if (ms->patbrk == 0)
{
XM_Quit(SC);
return;
}
}
if (ms->patbrk != 0)
ms->PatternPos = ms->patbrk - 1;
else
ms->PatternPos = 0;
ms->CurPos = ms->PatternPos;
if (ms->CurPos != 0)
FindOffset = 1;
// if (ms->BPlayFlag==2)
// {
// XM_Quit(SC);
// return;
// }
if ((ms->BPlayNext != -1) && (ms->BPlayFlag != 0))
{
ms->BPlayFlag = 0;
ms->SongPos = ms->BPlayNext;
ms->CurrentStart = ms->SongPos;
ms->BPlayNext = -1;
}
else if (ms->PlayNext != -1)
{
ms->SongPos = ms->PlayNext;
ms->CurrentStart = ms->SongPos;
ms->PlayNext = -1;
}
else // if (ms->Status==XM_Music)
{
ms->SongPos += (ms->posjmp - 1);
t = ms->SongPos;
}
ms->patbrk = ms->posjmp = 0;
if (ms->SongPos >= mh->songlength) /* End of song data ? */
{
if (!ms->SongLoop)
{
ms->XMPlay = XM_STOPPED; /* Once off tune */
return;
}
ms->SongPos = ms->reppos; /* Loop to loop point */
}
if (ms->SongPos < 0)
ms->SongPos = mh->songlength - 1;
if (ms->Status == XM_Music)
SP = mh->jorders[ms->SongPos]; /* Pattern from song data*/
else
SP = ms->SFXNum; /* Pattern from user SFX */
// SP+=0;
ms->CurrentPattern = SP;
ms->PatAdr = mh->JAP_PAT_ADDR[SP]; /* Addr of pat header */
ms->PatAdr2 = mh->JAP_PAT_ADDR2[SP]; /* Addr of pat data */
ms->numrow = getWord((u_char*)ms->PatAdr + 5); /* Number of rows */
ms->PatSize = getWord((u_char*)ms->PatAdr + 7); /* pattern size */
}
if (!ms->patdly2)
{
if (ms->PatSize > 0)
{
if (mh->version == 0xddba) /*** SUPER COMPRESSED ***/
{
if (FindOffset != 0)
ms->CurPos = PACKEDCalcPlayPos(ms->CurPos);
SP = ms->CurPos;
for (t = 0; t < mh->XMPSXChannels; t++)
{
XMC = &ms->XM_Chnl[t]; /* Decode pattern data */
XMC->eff = 0;
XMC->vol = 0;
XMC->dat = 0;
// XMC->nothing=1;
}
while (1)
{
pataddr = ((u_char*)ms->PatAdr2 + SP);
patdat = *pataddr++; /* Channel or 0xff for end */
SP++;
if (patdat == 0xff)
break;
CurrentCh = patdat;
XMC = &ms->XM_Chnl[patdat]; /* Decode pattern data */
pmsk = ms->PlayMask;
pmsk &= (1 << patdat);
SP += JPlayNote((u_char*)ms->PatAdr2 + SP, pmsk);
}
ms->CurPos = SP;
}
else
{
if (FindOffset != 0) /**** ORIGNAL XM FILE ****/
ms->CurPos = CalcPlayPos(ms->CurPos);
SP = ms->CurPos;
for (t = 0; t < mh->XMChannels; t++)
{
CurrentCh = t;
XMC = &ms->XM_Chnl[t]; /* Decode pattern data */
pmsk = ms->PlayMask;
pmsk &= (1 << t);
SP += JPlayNote((u_char*)ms->PatAdr2 + SP, pmsk);
}
ms->CurPos = SP;
}
}
}
}
}
int CalcPlayPos(int StartPos)
{
int SP = 0;
int t, i;
for (i = 0; i < StartPos; i++)
{
for (t = 0; t < mh->XMChannels; t++)
{
CurrentCh = t;
XMC = &ms->XM_Chnl[t]; /* Decode pattern data */
SP += JCalcPat((u_char*)ms->PatAdr2 + SP);
}
}
return(SP);
}
int PACKEDCalcPlayPos(int StartPos)
{
int SP = 0;
int i;
u_char *pataddr;
u_char patdat;
for (i = 0; i < StartPos; i++)
{
while (1)
{
pataddr = ((u_char*)ms->PatAdr2 + SP);
patdat = *pataddr++; /* Channel or 0xff for end */
SP++;
if (patdat == 0xff)
break;
CurrentCh = patdat;
XMC = &ms->XM_Chnl[patdat]; /* Decode pattern data */
SP += JCalcPat((u_char*)ms->PatAdr2 + SP);
}
}
return(SP);
}
int JCalcPat(u_char *j)
{
u_char b;
int ret = 1;
b = *j;
if (b == 0x80)
return(ret);
j++;
if ((b & 0x80) != 0)
{
if ((b & 1) != 0)
{
j++;
ret++;
}
if ((b & 2) != 0)
{
j++;
ret++;
}
if ((b & 4) != 0)
{
j++;
ret++;
}
if ((b & 8) != 0)
{
j++;
ret++;
}
if ((b & 16) != 0)
{
ret++;
}
}
else
ret = 5;
return(ret);
}
/*****************************************************************************
ApplyEffs
Process envelope/panning.
Kill silent, keyed off channels.
*****************************************************************************/
void ApplyEffs(void)
{
short envpan,envvol;
int t;
int pmsk;
short vlm;
short Pn;
u_char MaxChans;
ms->XMActiveVoices=0;
MaxChans=ms->MaxChans;
for(t=0;t<mh->XMPSXChannels;t++)
{
if (MaxChans==0)
return;
pmsk=ms->PlayMask;
pmsk&=(1<<t);
if (pmsk!=0)
{
MaxChans--;
XMC=&ms->XM_Chnl[t];
if(XMC->kick) /* New note keyed on ? */
{
XMC->keyon=1;
XMC->fadevol=32767;
XMC->ChDead=0;
}
if (!XMC->ChDead) /* Update envelope/pitch/vol */
{ /* If channel not dead */
ms->XMActiveVoices++;
if(XMC->period<1) /* Cap period */
XMC->period=1;
else if(XMC->period>32768)
XMC->period=32768;
if(XMC->envflg & EF_ON)
envvol=ProcessEnvelope(256,XMC->keyon,XMC->sample);
else
envvol=256;
if(XMC->panenvflg & EF_ON)
envpan=ProcessPanEnvelope(128,XMC->keyon,XMC->sample);
else
envpan=128;
// vlm=XMC->volume-XMC->UserVol;
vlm=((XMC->volume-64)*(64-XMC->UserVol))/64;
vlm+=64;
if (vlm<=64)
{
XMC->LVol=XMC->RVol=0;
}
else
{
vlm=((DoVol(XMC->fadevol,envvol,vlm-64)*ms->SongVolume)/128);
vlm*=ms->MasterVolume;
vlm/=128;
Pn=XMC->panning;
Pn+=ms->UserPan;
if (Pn<0)
Pn=0;
else if (Pn>255)
Pn=255;
Pn=DoPan(envpan,Pn);
XMC->LVol=(vlm*(255-Pn)); // /128;
XMC->RVol=(vlm*Pn); // /128;
}
if ((!XMC->LVol)&&(!XMC->RVol)&&(!XMC->keyon))
{
StpCh(XMC->SPUChannel);
XMC->ChDead=1; /* Released & 0vol - so kill channel */
}
else if(!XMC->keyon) /* Key off? - do release on envelope */
{
XMC->fadevol-=XMC->keyoffspd;
if(XMC->fadevol<0)
XMC->fadevol=0;
}
}
}
}
}
/*****************************************************************************
UpdateEffs
Play any effects if required
*****************************************************************************/
void UpdateEffs(void)
{
int t;
int pmsk;
u_char MaxChans;
MaxChans = ms->MaxChans;
for (t = 0; t < mh->XMPSXChannels; t++)
{
if (MaxChans == 0)
return;
XMC = &ms->XM_Chnl[t];
pmsk = ms->PlayMask;
pmsk &= (1 << t);
if (pmsk != 0)
{
MaxChans--;
if (!XMC->nothing) /* Any note/vol/eff's to process? */
{
JPlayEffects();
}
}
}
}
#define JPPIT 2682522
int DVL;
int DVR;
void DoDolbySS(void)
{
if (MonoMode==XM_MONO)
{
DVL=(XMC->OldLVol+XMC->OldRVol)/2;
DVR=DVL;
return;
}
if (XMC->Dolby==XM_DOLBY_LEFT)
{
DVL=XMC->OldLVol;
if (XMC->OldRVol>XMC->OldLVol)
{
DVR=-XMC->OldRVol;
DVL=XMC->OldRVol;
}
else
DVR=-XMC->OldRVol;
}
else if (XMC->Dolby==XM_DOLBY_RIGHT)
{
DVR=XMC->OldLVol;
if (XMC->OldRVol>XMC->OldLVol)
{
DVL=-XMC->OldRVol;
DVR=XMC->OldRVol;
}
else
DVL=-XMC->OldRVol;
}
else
{
DVL=XMC->OldLVol;
DVR=XMC->OldRVol;
}
}
int dd = 0;
int de = 0;
// [D]
int GetEmpty(int old)
{
unsigned char bVar1;
XMCHANNEL *j;
int iVar3;
int i;
de++;
if (mh->XMPSXChannels <= de)
de = 0;
dd = de;
for (i = 0; i < mh->XMPSXChannels; i++)
{
j = ms->XM_Chnl + dd;
dd++;
if (mh->XMPSXChannels <= dd)
dd = 0;
if (j->kick == 0 && j->ChDead == 1)
{
bVar1 = j->SPUChannel;
j->SPUChannel = old;
return bVar1;
}
}
return -1;
}
/*****************************************************************************
UpdateHardware
Update SPU if needed
*****************************************************************************/
// [D] [A] Half-source, half-decomp
void UpdateHardware(void)
{
int t;
int prd;
int SPUKeyOn = 0;
int pmsk;
int Chnl;
int Ch2;
u_char MaxChans;
MaxChans=ms->MaxChans;
for(t=0;t<mh->XMPSXChannels;t++)
{
if (MaxChans==0)
break;
pmsk=ms->PlayMask;
pmsk&=(1<<t);
if (pmsk!=0)
{
MaxChans--;
XMC=&ms->XM_Chnl[t];
if(XMC->kick)
{
Chnl = XMC->SPUChannel;
SpuSetKey(0, 1 << XMC->SPUChannel);
Ch2 = GetEmpty(XMC->SPUChannel);
if (Ch2 != -1)
2020-05-13 15:55:56 +02:00
Chnl = Ch2;
XMC->SPUChannel = Chnl;
prd=XMC->period;
if (ms->NotAmiga==0)
prd=((JPPIT<<2)/prd)>>3; /* Using AMIGA freq table */
else
prd=GetFreq2(prd); /* Using LINEAR freq table */
XMC->OldLVol=XMC->LVol;
XMC->OldRVol=XMC->RVol;
DoDolbySS();
2020-05-13 15:55:56 +02:00
SPUKeyOn|=(1<< Chnl);
XMC->SPUPitch=prd;
XMC->OldPeriod=XMC->period;
2020-05-13 15:55:56 +02:00
PlaySFX(ms->VabID,Chnl,XMC->sample,prd,DVL,DVR);
}
}
}
if (SPUKeyOn!=0)
{
2020-05-13 15:55:56 +02:00
//XMTime1 = 1;
SpuSetKey(1, SPUKeyOn);
SpuFlush(SPU_EVENT_ALL);
2020-05-13 15:55:56 +02:00
aa = 0;
}
MaxChans=ms->MaxChans;
for(t=0;t<mh->XMPSXChannels;t++)
{
if (MaxChans==0)
return;
pmsk=ms->PlayMask;
pmsk&=(1<<t);
if (pmsk!=0)
{
MaxChans--;
XMC=&ms->XM_Chnl[t];
if (!XMC->ChDead)
{
Ch2=XMC->SPUChannel;
if (XMC->kick==0)
{
if ((XMC->LVol!=XMC->OldLVol)||(XMC->RVol!=XMC->OldRVol))
{
if (XMC->OldLVol!=XMC->LVol)
XMC->OldLVol=IntVols(XMC->LVol,XMC->OldLVol);
// if (XMC->LVol==XMC->RVol)
// XMC->OldRVol=XMC->OldLVol; /* Mono sound */
// else
// {
if (XMC->OldRVol!=XMC->RVol)
XMC->OldRVol=IntVols(XMC->RVol,XMC->OldRVol);
// }
DoDolbySS();
// SetVol(Ch2,XMC->OldLVol,XMC->OldRVol); /* Update volumes */
SetVol(Ch2,DVL,DVR); /* Update volumes */
}
if (XMC->period!=XMC->OldPeriod)
{
XMC->OldPeriod=XMC->period;
prd=XMC->period;
if(ms->NotAmiga==0)
prd=((JPPIT<<2)/prd)>>3; /* Using AMIGA freq table */
else
prd=GetFreq2(prd);
XMC->SPUPitch=prd;
SetFrq(Ch2,prd); /* Update periods */
}
}
else
XMC->kick=0;
}
}
}
}
/*****************************************************************************
IntVols
Calculates point between Current volume and Required volume.
Requires:
Vol1 LVol
Vol2 OldLVol
Returns:
value half way between two points.
*****************************************************************************/
int IntVols(int Vol1, int Vol2)
{
int a;
a = abs(Vol2 - Vol1);
a >>= 2;
a += 1;
if (Vol1 < Vol2)
return(Vol1 + a);
else
return(Vol2 + a);
}
/*****************************************************************************
GetFreq2
Returns SPU pitch.
Calculated from linear value
*****************************************************************************/
#define joc 8
//int JPPer = 6578; //6578 D1 has this value and game were shipped with broken soundtrack
int JPPer = 7350; //6578
long GetFreq2(long period)
{
int okt;
long frequency;
//FntPrint("period %d\n",period);
period = JPPer - period;
okt = period / 768;
//FntPrint("octave %d\n",okt);
frequency = lintab[period % 768];
//FntPrint("freq %d\n",frequency);
//if (XMC->sample==5)
//printf("aa %d aa\n",frequency>>(joc-okt));
return(frequency >> (joc - okt)); /* Final SPU pitch */
}
/*****************************************************************************
ProcessEnvelope
Updates amplitude envelope
*****************************************************************************/
short ProcessEnvelope(short v,u_char keyon,int JSmp)
{
u_char a2,b;
u_short p;
u_long* j;
short apos;
short aval;
short bpos;
short bval;
a2=XMC->enva;
b=XMC->envb;
p=XMC->envp;
/* compute the envelope value between points a and b */
j=mh->JAP_InstrumentOffset[JSmp];
aval=(getSWord((u_char*)j+129+2+(a2*4)))<<2;
bval=(getSWord((u_char*)j+129+2+(b*4)))<<2;
apos=getSWord((u_char*)j+129+(a2*4));
bpos=getSWord((u_char*)j+129+(b*4));
v=Interpolate(p,apos,bpos,aval,bval);
/* Should we sustain? (sustain flag on, key-on, point a is the sustain
point, and the pointer is exactly on point a) */
if((XMC->envflg & EF_SUSTAIN) && keyon && a2==XMC->envsus && p==apos)
{
/* do nothing */
}
else
{
p++; /* don't sustain, so increase pointer. */
if(p >= bpos) /* pointer reached point b? */
{
a2=b; b++; /* shift points a and b */
if(XMC->envflg & EF_LOOP)
{
if(b > XMC->envend)
{
a2=XMC->envbeg;
b=a2+1;
p=getSWord((u_char*)j+129+(a2*4));
}
}
else
{
if(b >= XMC->envpts)
{
b--;
p--;
}
}
}
}
XMC->enva=a2;
XMC->envb=b;
XMC->envp=p;
return v;
}
/*****************************************************************************
ProcessPanEnvelope
Updates panning envelope
*****************************************************************************/
short ProcessPanEnvelope(short v, u_char keyon, int JSmp)
{
u_char a2, b;
u_short p;
u_long* j;
short apos;
short aval;
short bpos;
short bval;
a2 = XMC->panenva;
b = XMC->panenvb;
p = XMC->panenvp;
/* compute the panenvelope value between points a and b */
j = mh->JAP_InstrumentOffset[JSmp];
aval = (getSWord((u_char*)j + 177 + 2 + (a2 * 4))) << 2;
bval = (getSWord((u_char*)j + 177 + 2 + (b * 4))) << 2;
apos = getSWord((u_char*)j + 177 + (a2 * 4));
bpos = getSWord((u_char*)j + 177 + (b * 4));
v = Interpolate(p, apos, bpos, aval, bval);
/* Should we sustain? (sustain flag on, key-on, point a is the sustain
point, and the pointer is exactly on point a) */
if ((XMC->panenvflg & EF_SUSTAIN) && keyon && a2 == XMC->panenvsus && p == apos)
{
/* do nothing */
}
else
{
p++; /* don't sustain, so increase pointer. */
if (p >= bpos) /* pointer reached point b? */
{
a2 = b; b++; /* shift points a and b */
if (XMC->panenvflg & EF_LOOP)
{
if (b > XMC->panenvend)
{
a2 = XMC->panenvbeg;
b = a2 + 1;
p = getSWord((u_char*)j + 177 + (a2 * 4));
}
}
else
{
if (b >= XMC->panenvpts)
{
b--;
p--;
}
}
}
}
XMC->panenva = a2;
XMC->panenvb = b;
XMC->panenvp = p;
return v;
}
/*****************************************************************************
XM_SetSongPos
User Call. Set song position to requested value.
*****************************************************************************/
void XM_SetSongPos(int Song_ID,u_short pos)
{
int t;
if (XMSongIDs[Song_ID]==-1)
return;
mu=(XMSONG*)(XM_SngAddress[Song_ID]);
// mu=&XM_Song[Song_ID];
// mhu=&XM_Header[mu->HeaderNum];
mhu=(XMHEADER*)(XM_HeaderAddress[mu->HeaderNum]);
if (pos>=mhu->songlength)
return;
JP_Do_Nothing=1; /* Incase user using interrupt to call player */
for(t=0;t<mhu->XMChannels;t++)
{
XMCU=&mu->XM_Chnl[t];
XMCU->keyon=0;
XMCU->tmpvolume=64;
2020-05-15 15:24:32 +02:00
if(XMCU->sample!=254)
XMCU->kick=1; // [A] temporary fix
}
mu->posjmp=1;
mu->patbrk=0;
mu->SongPos=pos;
mu->vbtick=mu->SongSpeed;
// mu->JBPM=0;
JP_Do_Nothing=0;
}
/*****************************************************************************
PlaySFX
Requires:
Channel 0-23 Channel to play SFX on
Voice 0-? SFX number to play
Note 0-127 Note number
Fine 0-127 Fine tune number
VolL 0-127 Left vol
VolR 0-127 Right vol
(To be added)
Reverb 0-1 1=Use reverb
****************************************************************************/
void PlaySFX(int VBID,int Channel,int Inst,int Pitch,int LV,int RV)
{
int a;
2020-05-13 15:55:56 +02:00
/* Mask which specific voice attributes are to be set */
xm_g_s_attr.mask = (SPU_VOICE_VOLL |
SPU_VOICE_VOLR |
SPU_VOICE_PITCH |
SPU_VOICE_WDSA |
SPU_VOICE_LSAX);
xm_g_s_attr.voice = SPU_VOICECH(Channel); //(1<<Channel);
xm_g_s_attr.volume.left = LV;
xm_g_s_attr.volume.right = RV;
xm_g_s_attr.pitch = Pitch;
a=xm_l_vag_spu_addr[VBID][Inst];
a=xm_l_vag_spu_addr[VBID][Inst]+XMC->SOffset;
xm_g_s_attr.addr = xm_l_vag_spu_addr[VBID][Inst]+XMC->SOffset;
xm_g_s_attr.loop_addr = xm_l_vag_spu_addr[VBID][Inst]+XMC->SOffset;
SpuSetVoiceAttr(&xm_g_s_attr);
}
/*****************************************************************************
InitSPUChannel
Sets up ADSR for selected channel
Called only when Init_XM
*****************************************************************************/
void InitSPUChannel(int Channel)
{
xm_g_s_attr.mask =
(SPU_VOICE_WDSA |
SPU_VOICE_ADSR_AMODE |
SPU_VOICE_ADSR_SMODE |
SPU_VOICE_ADSR_RMODE |
SPU_VOICE_ADSR_AR |
SPU_VOICE_ADSR_DR |
SPU_VOICE_ADSR_SR |
SPU_VOICE_ADSR_RR |
SPU_VOICE_ADSR_SL
);
xm_g_s_attr.voice = SPU_VOICECH(Channel); //(1<<Channel);
xm_g_s_attr.a_mode = SPU_VOICE_LINEARIncN;
xm_g_s_attr.s_mode = SPU_VOICE_LINEARIncN;
xm_g_s_attr.r_mode = SPU_VOICE_LINEARDecN;
xm_g_s_attr.ar = 0x4;
xm_g_s_attr.dr = 0x0;
xm_g_s_attr.sr = 0x0;
xm_g_s_attr.rr = 0x1;
xm_g_s_attr.sl = 0xf; //0xf;
SpuSetVoiceAttr(&xm_g_s_attr);
}
/*****************************************************************************
CurrentKeyStat
Stops processing of any dead channels
*****************************************************************************/
// [D]
void CurrentKeyStat(void)
{
int t;
char KeyStat[24];
SpuGetAllKeysStatus(KeyStat);
for (t = 0; t < mh->XMPSXChannels; t++)
{
XMC = &ms->XM_Chnl[t];
if (KeyStat[XMC->SPUChannel] != 1 && XMC->ChDead == 0)
{
XMC->ChDead = 1;
XMC->nothing = 1;
}
}
}
/*****************************************************************************
StpCh
Turns off channel
*****************************************************************************/
void StpCh(int Channel)
{
SpuSetVoiceVolume(Channel, 0, 0);
}
/*****************************************************************************
SetVol
Sets volume for channel
*****************************************************************************/
void SetVol(int Channel, int LVol, int RVol)
{
SpuSetVoiceVolume(Channel, LVol, RVol);
}
/*****************************************************************************
SetFrq
Sets frequency for channel
*****************************************************************************/
void SetFrq(int Channel, int Pitch)
{
SpuSetVoicePitch(Channel, Pitch);
}
/*****************************************************************************
SilenceXM
Silence any channel used for Song_ID
*****************************************************************************/
void SilenceXM(int Song_ID)
{
int t, i, c;
int pmsk;
mu = (XMSONG*)(XM_SngAddress[Song_ID]);
// mu=&XM_Song[Song_ID];
// mhu=&XM_Header[mu->HeaderNum];
mhu = (XMHEADER*)(XM_HeaderAddress[mu->HeaderNum]);
i = 0;
for (t = 0; t < mhu->XMPSXChannels; t++)
{
pmsk = mu->PlayMask;
pmsk &= (1 << t);
if (pmsk != 0)
{
XMC = &mu->XM_Chnl[t];
c = XMC->SPUChannel;
i |= (1 << c);
SetVol(c, 0, 0);
}
}
SpuSetKey(0, i);
}
/*****************************************************************************
XM_Pause
User Call. Pauses Song_ID
(by setting pitch to 0 - not by keying off..)
*****************************************************************************/
void XM_Pause(int Song_ID)
{
int t;
if (XMSongIDs[Song_ID] == -1)
return;
mu = (XMSONG*)(XM_SngAddress[Song_ID]);
// mu=&XM_Song[Song_ID];
// mhu=&XM_Header[mu->HeaderNum];
mhu = (XMHEADER*)(XM_HeaderAddress[mu->HeaderNum]);
if (mu->XMPlay == XM_PLAYING)
{
mu->XMPlay = XM_PAUSED;
for (t = 0; t < mhu->XMPSXChannels; t++)
{
XMCU = &mu->XM_Chnl[t];
SetFrq(XMCU->SPUChannel, 0);
}
}
}
/*****************************************************************************
XM_Restart
User Call. Restarts Song_ID
(returns the channels pitch to the original value)
*****************************************************************************/
void XM_Restart(int Song_ID)
{
int t;
if (XMSongIDs[Song_ID] == -1)
return;
mu = (XMSONG*)(XM_SngAddress[Song_ID]);
// mu=&XM_Song[Song_ID];
// mhu=&XM_Header[mu->HeaderNum];
mhu = (XMHEADER*)(XM_HeaderAddress[mu->HeaderNum]);
if (mu->XMPlay == XM_PAUSED)
{
mu->XMPlay = XM_PLAYING;
for (t = 0; t < mhu->XMPSXChannels; t++)
{
XMCU = &mu->XM_Chnl[t];
if (!XMCU->ChDead)
{
SetFrq(XMCU->SPUChannel, XMCU->SPUPitch);
}
}
}
}
/*****************************************************************************
XM_SetMasterVol
User Call. Set Master Volume from 0-128;
Requires:
Song ID
Volume;
*****************************************************************************/
void XM_SetMasterVol(int Song_ID, u_char Vol)
{
if (XMSongIDs[Song_ID] == -1)
return;
if (Vol <= 128)
{
mu = (XMSONG*)(XM_SngAddress[Song_ID]);
// mu=&XM_Song[Song_ID];
// mu->SongVolume=Vol;
mu->MasterVolume = Vol;
}
}
/*****************************************************************************
ClearSPU
Free up malloc'ed blocks. Call between exiting game/loading menus fx etc.
(same sort of thing as closevab).
****************************************************************************/
void ClearSPU(int VBID)
{
int i;
int Amount;
Amount=iVABID[VBID];
for (i=0;i<(Amount);i++)
{
if(xm_l_vag_spu_addr[VBID][i]!=0)
{
XM_FreeVAG(xm_l_vag_spu_addr[VBID][i]);
xm_l_vag_spu_addr[VBID][i]=0;
}
}
}
/*****************************************************************************
XM_FreeVAG
UserCall. Free up SPU RAM (deallocate VAG).
Use if the XM_SendVAGToSRAM has been used.
****************************************************************************/
void XM_FreeVAG(int addr)
{
SpuFree(addr);
}
int GetFreeSongID(void)
{
int i;
for (i = 0; i < XM_NSA; i++)
{
if (XMSongIDs[i] == -1)
{
XMSongIDs[i] = 0;
return i;
}
}
return(-1);
}
void XM_Quit(int SongID)
{
XM_PlayStop(SongID);
XMSongIDs[SongID]=-1;
JPClearSPUFlags(SongID+1);
}
void JPClearSPUFlags(int SongID)
{
int i;
2020-05-13 15:55:56 +02:00
for (i = 0; i < 24; i++)
{
if (XMSPU_SFX[i] == SongID)
XMSPU_SFX[i] = 0;
}
}
int XM_GetFreeVAB(void)
{
int i;
for (i=0;i<8;i++)
{
if (iVABID[i]==-1)
return(i); /* Free VAB slot */
}
return(-1); /* No Free VAB slots */
}
void XM_SetVAGAddress(int VabID, int slot, int addr)
{
xm_l_vag_spu_addr[VabID][slot] = addr;
iVABID[VabID] = slot + 1;
}
int XM_GetSongSize(void)
{
return(sizeof(XMSONG));
}
void XM_SetSongAddress(u_char *Address)
{
XM_SngAddress[XM_NSA] = Address;
mu = ((XMSONG*)(Address));
mu->Status = 0; /* Turn off song */
mu->XMPlay = XM_STOPPED;
XM_NSA++;
}
void XM_FreeAllSongIDs(void)
{
XM_NSA = 0;
}
int XM_GetFileHeaderSize(void)
{
return(sizeof(XMHEADER));
}
void XM_SetFileHeaderAddress(u_char *Address)
{
if (XM_HA<8)
{
XM_HeaderAddress[XM_HA]=Address;
XM_HA++;
}
}