fm.c
来自「DGen源码最后版本」· C语言 代码 · 共 2,003 行 · 第 1/5 页
C
2,003 行
}
}
/* load a */
if( v & 0x01 )
{
if( ST->TAC == 0 )
{
ST->TAC = (1024-ST->TA);
/* External timer handler */
// if (ST->Timer_Handler) (ST->Timer_Handler)(n,0,(double)ST->TAC,ST->TimerBase);
}
}else if (ST->timermodel == FM_TIMER_INTERVAL)
{ /* stop interbval timer */
if( ST->TAC != 0 )
{
ST->TAC = 0;
// if (ST->Timer_Handler) (ST->Timer_Handler)(n,0,0,ST->TimerBase);
}
}
}
/* Timer A Overflow */
INLINE void TimerAOver(FM_ST *ST)
{
/* status set if enabled */
if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01);
/* clear or reload the counter */
if (ST->timermodel == FM_TIMER_INTERVAL)
{
ST->TAC = (1024-ST->TA);
// if (ST->Timer_Handler) (ST->Timer_Handler)(ST->index,0,(double)ST->TAC,ST->TimerBase);
}
else ST->TAC = 0;
}
/* Timer B Overflow */
INLINE void TimerBOver(FM_ST *ST)
{
/* status set if enabled */
if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02);
/* clear or reload the counter */
if (ST->timermodel == FM_TIMER_INTERVAL)
{
ST->TBC = ( 256-ST->TB)<<4;
// if (ST->Timer_Handler) (ST->Timer_Handler)(ST->index,1,(double)ST->TBC,ST->TimerBase);
}
else ST->TBC = 0;
}
/* CSM Key Controll */
INLINE void CSMKeyControll(FM_CH *CH)
{
int ksl = KSL[CH->kcode];
/* all key off */
FM_KEYOFF(CH,SLOT1);
FM_KEYOFF(CH,SLOT2);
FM_KEYOFF(CH,SLOT3);
FM_KEYOFF(CH,SLOT4);
/* total level latch */
CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + ksl;
CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + ksl;
CH->SLOT[SLOT3].TLL = CH->SLOT[SLOT3].TL + ksl;
CH->SLOT[SLOT4].TLL = CH->SLOT[SLOT4].TL + ksl;
/* all key on */
FM_KEYON(CH,SLOT1);
FM_KEYON(CH,SLOT2);
FM_KEYON(CH,SLOT3);
FM_KEYON(CH,SLOT4);
}
#ifdef INTERNAL_TIMER
/* ---------- calcrate timer A ---------- */
INLINE void CALC_TIMER_A( FM_ST *ST , FM_CH *CSM_CH ){
if( ST->TAC && (ST->Timer_Handler==0) )
if( (ST->TAC -= ST->freqbase) <= 0 ){
TimerAOver( ST );
/* CSM mode key,TL controll */
if( ST->mode & 0x80 ){ /* CSM mode total level latch and auto key on */
CSMKeyControll( CSM_CH );
}
}
}
/* ---------- calcrate timer B ---------- */
INLINE void CALC_TIMER_B( FM_ST *ST,int step){
if( ST->TBC && (ST->Timer_Handler==0) )
if( (ST->TBC -= ST->freqbase*step) <= 0 ){
TimerBOver( ST );
}
}
#endif /* INTERNAL_TIMER */
#if BUILD_OPN
/* ---------- priscaler set(and make time tables) ---------- */
void OPNSetPris(FM_OPN *OPN , int pris , int TimerPris, int SSGpris)
{
int fn;
/* frequency base */
OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock * 4096.0 / OPN->ST.rate) / pris : 0;
/* Timer base time */
OPN->ST.TimerBase = (OPN->ST.rate) ? 1.0/((double)OPN->ST.clock / (double)TimerPris) : 0;
/* SSG part priscaler set */
//if( SSGpris ) SSGClk( OPN->ST.index, OPN->ST.clock * 2 / SSGpris );
/* make time tables */
init_timetables( &OPN->ST , OPN_DTTABLE , OPN_ARRATE , OPN_DRRATE );
/* make fnumber -> increment counter table */
for( fn=0 ; fn < 2048 ; fn++ )
{
/* it is freq table for octave 7 */
/* opn freq counter = 20bit */
OPN->FN_TABLE[fn] = (double)fn * OPN->ST.freqbase / 4096 * FREQ_RATE * (1<<7) / 2;
}
/* Log(LOG_INF,"OPN %d set priscaler %d\n",OPN->ST.index,pris);*/
}
/* ---------- write a OPN mode register 0x20-0x2f ---------- */
static void OPNWriteMode(FM_OPN *OPN, int r, int v)
{
unsigned char c;
FM_CH *CH;
switch(r){
case 0x21: /* Test */
break;
case 0x22: /* LFO FREQ (YM2608/YM2612) */
/* 3.98Hz,5.56Hz,6.02Hz,6.37Hz,6.88Hz,9.63Hz,48.1Hz,72.2Hz */
/* FM2608[n].LFOIncr = FM2608[n].LFO_TABLE[v&0x0f]; */
break;
case 0x24: /* timer A High 8*/
OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2);
break;
case 0x25: /* timer A Low 2*/
OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3);
break;
case 0x26: /* timer B */
OPN->ST.TB = v;
break;
case 0x27: /* mode , timer controll */
FMSetMode( &(OPN->ST),OPN->ST.index,v );
break;
case 0x28: /* key on / off */
c = v&0x03;
if( c == 3 ) break;
if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3;
CH = OPN->P_CH;
CH = &CH[c];
/* csm mode */
if( c == 2 && (OPN->ST.mode & 0x80) ) break;
if(v&0x10) FM_KEYON(CH,SLOT1); else FM_KEYOFF(CH,SLOT1);
if(v&0x20) FM_KEYON(CH,SLOT2); else FM_KEYOFF(CH,SLOT2);
if(v&0x40) FM_KEYON(CH,SLOT3); else FM_KEYOFF(CH,SLOT3);
if(v&0x80) FM_KEYON(CH,SLOT4); else FM_KEYOFF(CH,SLOT4);
/* Log(LOG_INF,"OPN %d:%d : KEY %02X\n",n,c,v&0xf0);*/
break;
}
}
/* ---------- write a OPN register (0x30-0xff) ---------- */
static void OPNWriteReg(FM_OPN *OPN, int r, int v)
{
unsigned char c;
FM_CH *CH;
FM_SLOT *SLOT;
/* 0x30 - 0xff */
if( (c = OPN_CHAN(r)) == 3 ) return; /* 0xX3,0xX7,0xXB,0xXF */
if( (r >= 0x100) /* && (OPN->type & TYPE_6CH) */ ) c+=3;
CH = OPN->P_CH;
CH = &CH[c];
SLOT = &(CH->SLOT[OPN_SLOT(r)]);
switch( r & 0xf0 ) {
case 0x30: /* DET , MUL */
set_det_mul(&OPN->ST,CH,SLOT,v);
break;
case 0x40: /* TL */
set_tl(CH,SLOT,v,(c == 2) && (OPN->ST.mode & 0x80) );
break;
case 0x50: /* KS, AR */
set_ar_ksr(CH,SLOT,v,OPN->ST.AR_TABLE);
break;
case 0x60: /* DR */
/* bit7 = AMS ENABLE(YM2612) */
set_dr(SLOT,v,OPN->ST.DR_TABLE);
break;
case 0x70: /* SR */
set_sr(SLOT,v,OPN->ST.DR_TABLE);
break;
case 0x80: /* SL, RR */
set_sl_rr(SLOT,v,OPN->ST.DR_TABLE);
break;
case 0x90: /* SSG-EG */
#ifndef SEG_SUPPORT
if(v&0x08) Log(LOG_ERR,"OPN %d,%d,%d :SSG-TYPE envelope selected (not supported )\n",OPN->ST.index,c,OPN_SLOT(r));
#endif
SLOT->SEG = v&0x0f;
break;
case 0xa0:
switch( OPN_SLOT(r) ){
case 0: /* 0xa0-0xa2 : FNUM1 */
{
unsigned int fn = (((unsigned int)( (CH->fn_h)&7))<<8) + v;
unsigned char blk = CH->fn_h>>3;
/* make keyscale code */
CH->kcode = (blk<<2)|OPN_FKTABLE[(fn>>7)];
/* make basic increment counter 32bit = 1 cycle */
CH->fc = OPN->FN_TABLE[fn]>>(7-blk);
CH->SLOT[SLOT1].Incr=-1;
}
break;
case 1: /* 0xa4-0xa6 : FNUM2,BLK */
CH->fn_h = v&0x3f;
break;
case 2: /* 0xa8-0xaa : 3CH FNUM1 */
if( r < 0x100)
{
unsigned int fn = (((unsigned int)(OPN->SL3.fn_h[c]&7))<<8) + v;
unsigned char blk = OPN->SL3.fn_h[c]>>3;
/* make keyscale code */
OPN->SL3.kcode[c]= (blk<<2)|OPN_FKTABLE[(fn>>7)];
/* make basic increment counter 32bit = 1 cycle */
OPN->SL3.fc[c] = OPN->FN_TABLE[fn]>>(7-blk);
(OPN->P_CH)[2].SLOT[SLOT1].Incr=-1;
}
break;
case 3: /* 0xac-0xae : 3CH FNUM2,BLK */
if( r < 0x100)
OPN->SL3.fn_h[c] = v&0x3f;
break;
}
break;
case 0xb0:
switch( OPN_SLOT(r) ){
case 0: /* 0xb0-0xb2 : FB,ALGO */
{
int feedback = (v>>3)&7;
CH->ALGO = v&7;
CH->FB = feedback ? 8 - feedback : 0;
set_algorythm( CH );
}
break;
case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2608) */
if( OPN->type & TYPE_LFOPAN)
{
/* b0-2 PMS */
/* 0,3.4,6.7,10,14,20,40,80(cent) */
SLOT->pms = (v>>4) & 0x07;
/* b4-5 AMS */
/* 0,1.4,5.9,11.8(dB) */
SLOT->ams = v & 0x03;
/* PAN */
CH->PAN = (v>>6)&0x03; /* PAN : b6 = R , b7 = L */
set_algorythm( CH );
/* Log(LOG_INF,"OPN %d,%d : PAN %d\n",n,c,CH->PAN);*/
}
break;
}
break;
}
}
#endif /* BUILD_OPN */
#if BUILD_YM2203
/*******************************************************************************/
/* YM2203 local section */
/*******************************************************************************/
static YM2203 *FM2203=NULL; /* array of YM2203's */
/* ---------- update one of chip ----------- */
void YM2203UpdateOne(int num, void *buffer, int length)
{
YM2203 *F2203 = &(FM2203[num]);
FM_OPN *OPN = &(FM2203[num].OPN);
int i,ch;
int data;
FMSAMPLE *buf = (FMSAMPLE *)buffer;
State = &F2203->OPN.ST;
cch[0] = &F2203->CH[0];
cch[1] = &F2203->CH[1];
cch[2] = &F2203->CH[2];
/* frequency counter channel A */
CALC_FCOUNT( cch[0] );
/* frequency counter channel B */
CALC_FCOUNT( cch[1] );
/* frequency counter channel C */
if( (State->mode & 0xc0) ){
/* 3SLOT MODE */
if( cch[2]->SLOT[SLOT1].Incr==-1){
/* 3 slot mode */
CALC_FCSLOT(&cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
CALC_FCSLOT(&cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
CALC_FCSLOT(&cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
CALC_FCSLOT(&cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
}
}else CALC_FCOUNT( cch[2] );
for( i=0; i < length ; i++ )
{
/* channel A channel B channel C */
outd[OPN_CENTER] = 0;
/* calcrate FM */
for( ch=0;ch<3;ch++) FM_CALC_CH( cch[ch] );
/* limit check */
data = Limit( outd[OPN_CENTER] , OPN_MAXOUT, OPN_MINOUT );
/* store to sound buffer */
buf[i] = data >> OPN_OUTSB;
#ifdef INTERNAL_TIMER
/* timer controll */
CALC_TIMER_A( State , cch[2] );
#endif
}
#ifdef INTERNAL_TIMER
CALC_TIMER_B( State , length );
#endif
}
/* ---------- reset one of chip ---------- */
void YM2203ResetChip(int num)
{
int i;
FM_OPN *OPN = &(FM2203[num].OPN);
/* Reset Priscaler */
OPNSetPris( OPN , 6*12 , 6*12 ,4); /* 1/6 , 1/4 */
/* reset SSG section */
//SSGReset(OPN->ST.index);
/* status clear */
FM_IRQMASK_SET(&OPN->ST,0x03);
OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
reset_channel( &OPN->ST , FM2203[num].CH , 3 );
/* reset OPerator paramater */
for(i = 0xb6 ; i >= 0xb4 ; i-- ) OPNWriteReg(OPN,i,0xc0); /* PAN RESET */
for(i = 0xb2 ; i >= 0x30 ; i-- ) OPNWriteReg(OPN,i,0);
for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
}
#if 0
/* ---------- return the buffer ---------- */
FMSAMPLE *YM2203Buffer(int n)
{
return FM2203[n].Buf;
}
/* ---------- set buffer ---------- */
int YM2203SetBuffer(int n, FMSAMPLE *buf)
{
if( buf == 0 ) return -1;
FM2203[n].Buf = buf;
return 0;
}
#endif
/* ---------- Initialize YM2203 emulator(s) ---------- */
/* 'num' is the number of virtual YM2203's to allocate */
/* 'rate' is sampling rate and 'bufsiz' is the size of the */
/* buffer that should be updated at each interval */
int YM2203Init(int num, int clock, int rate,
FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler)
{
int i;
if (FM2203) return (-1); /* duplicate init. */
cur_chip = NULL; /* hiro-shi!! */
FMNumChips = num;
/* allocate ym2203 state space */
if( (FM2203 = (YM2203 *)malloc(sizeof(YM2203) * FMNumChips))==NULL)
return (-1);
/* clear */
memset(FM2203,0,sizeof(YM2203) * FMNumChips);
/* allocate total level table (128kb space) */
if( !FMInitTable() )
{
free( FM2203 );
return (-1);
}
for ( i = 0 ; i < FMNumChips; i++ ) {
FM2203[i].OPN.ST.index = i;
FM2203[i].OPN.type = TYPE_YM2203;
FM2203[i].OPN.P_CH = FM2203[i].CH;
FM2203[i].OPN.ST.clock = clock;
FM2203[i].OPN.ST.rate = rate;
/* FM2203[i].OPN.ST.irq = 0; */
/* FM2203[i].OPN.ST.satus = 0; */
FM2203[i].OPN.ST.timermodel = FM_TIMER_SINGLE;
/* Extend handler */
FM2203[i].OPN.ST.Timer_Handler = TimerHandler;
FM2203[i].OPN.ST.IRQ_Handler = IRQHandler;
YM2203ResetChip(i);
}
return(0);
}
/* ---------- shut down emurator ----------- */
void YM2203Shutdown(void)
{
if (!FM2203) return;
FMCloseTable();
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?