#include "shared.h" #include #ifndef PI #define PI 3.14159265358979323846 #endif #define BUILD_OPN (BUILD_YM2612) #define BUILD_STEREO (BUILD_YM2612) #define BUILD_LFO (BUILD_YM2612) #define SIN_ENT 2048 #define ENV_BITS 16 #define EG_ENT 4096 #define EG_STEP (96.0/EG_ENT) /* OPL == 0.1875 dB */ #if FM_LFO_SUPPORT /* LFO table entries */ #define LFO_ENT 512 #define LFO_SHIFT (32-9) #define LFO_RATE 0x10000 #endif /* -------------------- preliminary define section --------------------- */ /* attack/decay rate time rate */ #define OPM_ARRATE 399128 #define OPM_DRRATE 5514396 /* It is not checked , because I haven't YM2203 rate */ #define OPN_ARRATE OPM_ARRATE #define OPN_DRRATE OPM_DRRATE /* PG output cut off level : 78dB(14bit)? */ #define PG_CUT_OFF ((int)(78.0/EG_STEP)) /* EG output cut off level : 68dB? */ #define EG_CUT_OFF ((int)(68.0/EG_STEP)) #define FREQ_BITS 24 /* frequency turn */ /* PG counter is 21bits @oct.7 */ #define FREQ_RATE (1<<(FREQ_BITS-21)) #define TL_BITS (FREQ_BITS+2) /* OPbit = 14(13+sign) : TL_BITS+1(sign) / output = 16bit */ #define TL_SHIFT (TL_BITS+1-(14-16)) /* output final shift */ #define FM_OUTSB (TL_SHIFT-FM_OUTPUT_BIT) #define FM_MAXOUT ((1<<(TL_SHIFT-1))-1) #define FM_MINOUT (-(1<<(TL_SHIFT-1))) /* -------------------- local defines , macros --------------------- */ /* envelope counter position */ #define EG_AST 0 /* start of Attack phase */ #define EG_AED (EG_ENT<>2)&3) #define OPM_CHAN(N) (N&7) #define OPM_SLOT(N) ((N>>3)&3) /* slot number */ #define SLOT1 0 #define SLOT2 2 #define SLOT3 1 #define SLOT4 3 /* bit0 = Right enable , bit1 = Left enable */ #define OUTD_RIGHT 1 #define OUTD_LEFT 2 #define OUTD_CENTER 3 /* FM timer model */ #define FM_TIMER_SINGLE (0) #define FM_TIMER_INTERVAL (1) /* ---------- OPN / OPM one channel ---------- */ typedef struct fm_slot { INT32 *DT; /* detune :DT_TABLE[DT] */ int DT2; /* multiple,Detune2:(DT2<<4)|ML for OPM*/ int TL; /* total level :TL << 8 */ UINT8 KSR; /* key scale rate :3-KSR */ const INT32 *AR; /* attack rate :&AR_TABLE[AR<<1] */ const INT32 *DR; /* decay rate :&DR_TABLE[DR<<1] */ const INT32 *SR; /* sustin rate :&DR_TABLE[SR<<1] */ int SL; /* sustin level :SL_TABLE[SL] */ const INT32 *RR; /* release rate :&DR_TABLE[RR<<2+2] */ UINT8 SEG; /* SSG EG type :SSGEG */ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ UINT32 mul; /* multiple :ML_TABLE[ML] */ /* Phase Generator */ UINT32 Cnt; /* frequency count : */ UINT32 Incr; /* frequency step : */ /* Envelope Generator */ void (*eg_next)(struct fm_slot *SLOT); /* pointer of phase handler */ INT32 evc; /* envelope counter */ INT32 eve; /* envelope counter end point */ INT32 evs; /* envelope counter step */ INT32 evsa; /* envelope step for Attack */ INT32 evsd; /* envelope step for Decay */ INT32 evss; /* envelope step for Sustain */ INT32 evsr; /* envelope step for Release */ INT32 TLL; /* adjusted TotalLevel */ /* LFO */ UINT8 amon; /* AMS enable flag */ UINT32 ams; /* AMS depth level of this SLOT */ }FM_SLOT; typedef struct fm_chan { FM_SLOT SLOT[4]; UINT8 PAN; /* PAN :NONE,LEFT,RIGHT or CENTER */ UINT8 ALGO; /* Algorythm */ UINT8 FB; /* shift count of self feed back */ INT32 op1_out[2]; /* op1 output for beedback */ /* Algorythm (connection) */ INT32 *connect1; /* pointer of SLOT1 output */ INT32 *connect2; /* pointer of SLOT2 output */ INT32 *connect3; /* pointer of SLOT3 output */ INT32 *connect4; /* pointer of SLOT4 output */ /* LFO */ INT32 pms; /* PMS depth level of channel */ UINT32 ams; /* AMS depth level of channel */ /* Phase Generator */ UINT32 fc; /* fnum,blk :adjusted to sampling rate */ UINT8 fn_h; /* freq latch : */ UINT8 kcode; /* key code : */ } FM_CH; /* OPN/OPM common state */ typedef struct fm_state { UINT8 index; /* chip index (number of chip) */ int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ double TimerBase; /* Timer base time */ UINT8 address; /* address register */ UINT8 irq; /* interrupt level */ UINT8 irqmask; /* irq mask */ UINT8 status; /* status flag */ UINT32 mode; /* mode CSM / 3SLOT */ int TA; /* timer a */ int TAC; /* timer a counter */ UINT8 TB; /* timer b */ int TBC; /* timer b counter */ /* speedup customize */ /* local time tables */ INT32 DT_TABLE[8][32]; /* DeTune tables */ INT32 AR_TABLE[94]; /* Atttack rate tables */ INT32 DR_TABLE[94]; /* Decay rate tables */ /* Extention Timer and IRQ handler */ FM_TIMERHANDLER Timer_Handler; FM_IRQHANDLER IRQ_Handler; /* timer model single / interval */ UINT8 timermodel; }FM_ST; /* -------------------- tables --------------------- */ /* sustain lebel table (3db per step) */ /* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ #define SC(db) (int)((db*((3/EG_STEP)*(1< max ) val = max; \ else if ( val < min ) val = min; \ } /* ----- buffering one of data(STEREO chip) ----- */ #if FM_STEREO_MIX /* stereo mixing */ #define FM_BUFFERING_STEREO \ { \ /* get left & right output with clipping */ \ out_ch[OUTD_LEFT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_LEFT] , FM_MAXOUT, FM_MINOUT ); \ out_ch[OUTD_RIGHT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_RIGHT], FM_MAXOUT, FM_MINOUT ); \ /* buffering */ \ *bufL++ = out_ch[OUTD_LEFT] >>FM_OUTSB; \ *bufL++ = out_ch[OUTD_RIGHT]>>FM_OUTSB; \ } #else /* stereo separate */ #define FM_BUFFERING_STEREO \ { \ /* get left & right output with clipping */ \ out_ch[OUTD_LEFT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_LEFT] , FM_MAXOUT, FM_MINOUT ); \ out_ch[OUTD_RIGHT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_RIGHT], FM_MAXOUT, FM_MINOUT ); \ /* buffering */ \ bufL[i] = out_ch[OUTD_LEFT] >>FM_OUTSB; \ bufR[i] = out_ch[OUTD_RIGHT]>>FM_OUTSB; \ } #endif #if FM_INTERNAL_TIMER /* ----- internal timer mode , update timer */ /* ---------- calcrate timer A ---------- */ #define INTERNAL_TIMER_A(ST,CSM_CH) \ { \ if( ST->TAC && (ST->Timer_Handler==0) ) \ if( (ST->TAC -= (int)(ST->freqbase*4096)) <= 0 ) \ { \ TimerAOver( ST ); \ /* CSM mode total level latch and auto key on */ \ if( ST->mode & 0x80 ) \ CSMKeyControll( CSM_CH ); \ } \ } /* ---------- calcrate timer B ---------- */ #define INTERNAL_TIMER_B(ST,step) \ { \ if( ST->TBC && (ST->Timer_Handler==0) ) \ if( (ST->TBC -= (int)(ST->freqbase*4096*step)) <= 0 ) \ TimerBOver( ST ); \ } #else /* FM_INTERNAL_TIMER */ /* external timer mode */ #define INTERNAL_TIMER_A(ST,CSM_CH) #define INTERNAL_TIMER_B(ST,step) #endif /* FM_INTERNAL_TIMER */ /* --------------------- subroutines --------------------- */ /* status set and IRQ handling */ static __inline__ void FM_STATUS_SET(FM_ST *ST,int flag) { /* set status flag */ ST->status |= flag; if ( !(ST->irq) && (ST->status & ST->irqmask) ) { ST->irq = 1; /* callback user interrupt handler (IRQ is OFF to ON) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->index,1); } } /* status reset and IRQ handling */ static __inline__ void FM_STATUS_RESET(FM_ST *ST,int flag) { /* reset status flag */ ST->status &=~flag; if ( (ST->irq) && !(ST->status & ST->irqmask) ) { ST->irq = 0; /* callback user interrupt handler (IRQ is ON to OFF) */ if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->index,0); } } /* IRQ mask set */ static __inline__ void FM_IRQMASK_SET(FM_ST *ST,int flag) { ST->irqmask = flag; /* IRQ handling check */ FM_STATUS_SET(ST,0); FM_STATUS_RESET(ST,0); } /* ---------- event hander of Phase Generator ---------- */ /* Release end -> stop counter */ static void FM_EG_Release( FM_SLOT *SLOT ) { SLOT->evc = EG_OFF; SLOT->eve = EG_OFF+1; SLOT->evs = 0; } /* SUSTAIN end -> stop counter */ static void FM_EG_SR( FM_SLOT *SLOT ) { SLOT->evs = 0; SLOT->evc = EG_OFF; SLOT->eve = EG_OFF+1; } /* Decay end -> Sustain */ static void FM_EG_DR( FM_SLOT *SLOT ) { SLOT->eg_next = FM_EG_SR; SLOT->evc = SLOT->SL; SLOT->eve = EG_DED; SLOT->evs = SLOT->evss; } /* Attack end -> Decay */ static void FM_EG_AR( FM_SLOT *SLOT ) { /* next DR */ SLOT->eg_next = FM_EG_DR; SLOT->evc = EG_DST; SLOT->eve = SLOT->SL; SLOT->evs = SLOT->evsd; } #if FM_SEG_SUPPORT static void FM_EG_SSG_SR( FM_SLOT *SLOT ); /* SEG down side end */ static void FM_EG_SSG_DR( FM_SLOT *SLOT ) { if( SLOT->SEG&2){ /* reverce */ SLOT->eg_next = FM_EG_SSG_SR; SLOT->evc = SLOT->SL + (EG_UST - EG_DST); SLOT->eve = EG_UED; SLOT->evs = SLOT->evss; }else{ /* again */ SLOT->evc = EG_DST; } /* hold */ if( SLOT->SEG&1) SLOT->evs = 0; } /* SEG upside side end */ static void FM_EG_SSG_SR( FM_SLOT *SLOT ) { if( SLOT->SEG&2){ /* reverce */ SLOT->eg_next = FM_EG_SSG_DR; SLOT->evc = EG_DST; SLOT->eve = EG_DED; SLOT->evs = SLOT->evsd; }else{ /* again */ SLOT->evc = SLOT->SL + (EG_UST - EG_DST); } /* hold check */ if( SLOT->SEG&1) SLOT->evs = 0; } /* SEG Attack end */ static void FM_EG_SSG_AR( FM_SLOT *SLOT ) { if( SLOT->SEG&4){ /* start direction */ /* next SSG-SR (upside start ) */ SLOT->eg_next = FM_EG_SSG_SR; SLOT->evc = SLOT->SL + (EG_UST - EG_DST); SLOT->eve = EG_UED; SLOT->evs = SLOT->evss; }else{ /* next SSG-DR (downside start ) */ SLOT->eg_next = FM_EG_SSG_DR; SLOT->evc = EG_DST; SLOT->eve = EG_DED; SLOT->evs = SLOT->evsd; } } #endif /* FM_SEG_SUPPORT */ /* ----- key on of SLOT ----- */ #define FM_KEY_IS(SLOT) ((SLOT)->eg_next!=FM_EG_Release) static __inline__ void FM_KEYON(FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if( !FM_KEY_IS(SLOT) ) { /* restart Phage Generator */ SLOT->Cnt = 0; /* phase -> Attack */ #if FM_SEG_SUPPORT if( SLOT->SEG&8 ) SLOT->eg_next = FM_EG_SSG_AR; else #endif SLOT->eg_next = FM_EG_AR; SLOT->evs = SLOT->evsa; #if 0 /* convert decay count to attack count */ /* --- This caused the problem by credit sound of paper boy. --- */ SLOT->evc = EG_AST + DRAR_TABLE[ENV_CURVE[SLOT->evc>>ENV_BITS]];/* + SLOT->evs;*/ #else /* reset attack counter */ SLOT->evc = EG_AST; #endif SLOT->eve = EG_AED; } } /* ----- key off of SLOT ----- */ static __inline__ void FM_KEYOFF(FM_CH *CH , int s ) { FM_SLOT *SLOT = &CH->SLOT[s]; if( FM_KEY_IS(SLOT) ) { /* if Attack phase then adjust envelope counter */ if( SLOT->evc < EG_DST ) SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]< Release */ SLOT->eg_next = FM_EG_Release; SLOT->eve = EG_DED; SLOT->evs = SLOT->evsr; } } /* setup Algorythm and PAN connection */ static void setup_connection( FM_CH *CH ) { INT32 *carrier = &out_ch[CH->PAN]; /* NONE,LEFT,RIGHT or CENTER */ switch( CH->ALGO ){ case 0: /* PG---S1---S2---S3---S4---OUT */ CH->connect1 = &pg_in2; CH->connect2 = &pg_in3; CH->connect3 = &pg_in4; break; case 1: /* PG---S1-+-S3---S4---OUT */ /* PG---S2-+ */ CH->connect1 = &pg_in3; CH->connect2 = &pg_in3; CH->connect3 = &pg_in4; break; case 2: /* PG---S1------+-S4---OUT */ /* PG---S2---S3-+ */ CH->connect1 = &pg_in4; CH->connect2 = &pg_in3; CH->connect3 = &pg_in4; break; case 3: /* PG---S1---S2-+-S4---OUT */ /* PG---S3------+ */ CH->connect1 = &pg_in2; CH->connect2 = &pg_in4; CH->connect3 = &pg_in4; break; case 4: /* PG---S1---S2-+--OUT */ /* PG---S3---S4-+ */ CH->connect1 = &pg_in2; CH->connect2 = carrier; CH->connect3 = &pg_in4; break; case 5: /* +-S2-+ */ /* PG---S1-+-S3-+-OUT */ /* +-S4-+ */ CH->connect1 = 0; /* special case */ CH->connect2 = carrier; CH->connect3 = carrier; break; case 6: /* PG---S1---S2-+ */ /* PG--------S3-+-OUT */ /* PG--------S4-+ */ CH->connect1 = &pg_in2; CH->connect2 = carrier; CH->connect3 = carrier; break; case 7: /* PG---S1-+ */ /* PG---S2-+-OUT */ /* PG---S3-+ */ /* PG---S4-+ */ CH->connect1 = carrier; CH->connect2 = carrier; CH->connect3 = carrier; } CH->connect4 = carrier; } /* set detune & multiple */ static __inline__ void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) { SLOT->mul = MUL_TABLE[v&0x0f]; SLOT->DT = ST->DT_TABLE[(v>>4)&7]; CH->SLOT[SLOT1].Incr=-1; } /* set total level */ static __inline__ void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v,int csmflag) { v &= 0x7f; v = (v<<7)|v; /* 7bit -> 14bit */ SLOT->TL = (v*EG_ENT)>>14; /* if it is not a CSM channel , latch the total level */ if( !csmflag ) SLOT->TLL = SLOT->TL; } /* set attack rate & key scale */ static __inline__ void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v,INT32 *ar_table) { SLOT->KSR = 3-(v>>6); SLOT->AR = (v&=0x1f) ? &ar_table[v<<1] : RATE_0; SLOT->evsa = SLOT->AR[SLOT->ksr]; if( SLOT->eg_next == FM_EG_AR ) SLOT->evs = SLOT->evsa; CH->SLOT[SLOT1].Incr=-1; } /* set decay rate */ static __inline__ void set_dr(FM_SLOT *SLOT,int v,INT32 *dr_table) { SLOT->DR = (v&=0x1f) ? &dr_table[v<<1] : RATE_0; SLOT->evsd = SLOT->DR[SLOT->ksr]; if( SLOT->eg_next == FM_EG_DR ) SLOT->evs = SLOT->evsd; } /* set sustain rate */ static __inline__ void set_sr(FM_SLOT *SLOT,int v,INT32 *dr_table) { SLOT->SR = (v&=0x1f) ? &dr_table[v<<1] : RATE_0; SLOT->evss = SLOT->SR[SLOT->ksr]; if( SLOT->eg_next == FM_EG_SR ) SLOT->evs = SLOT->evss; } /* set release rate */ static __inline__ void set_sl_rr(FM_SLOT *SLOT,int v,INT32 *dr_table) { SLOT->SL = SL_TABLE[(v>>4)]; SLOT->RR = &dr_table[((v&0x0f)<<2)|2]; SLOT->evsr = SLOT->RR[SLOT->ksr]; if( SLOT->eg_next == FM_EG_Release ) SLOT->evs = SLOT->evsr; } /* operator output calcrator */ #define OP_OUT(PG,EG) SIN_TABLE[(PG/(0x1000000/SIN_ENT))&(SIN_ENT-1)][EG] #define OP_OUTN(PG,EG) NOISE_TABLE[(PG/(0x1000000/SIN_ENT))&(SIN_ENT-1)][EG] /* eg calcration */ #if FM_LFO_SUPPORT #define FM_CALC_EG(OUT,SLOT) \ { \ if( (SLOT.evc += SLOT.evs) >= SLOT.eve) \ SLOT.eg_next(&(SLOT)); \ OUT = SLOT.TLL+ENV_CURVE[SLOT.evc>>ENV_BITS]; \ if(SLOT.ams) \ OUT += (SLOT.ams*lfo_amd/LFO_RATE); \ } #else #define FM_CALC_EG(OUT,SLOT) \ { \ if( (SLOT.evc += SLOT.evs) >= SLOT.eve) \ SLOT.eg_next(&(SLOT)); \ OUT = SLOT.TLL+ENV_CURVE[SLOT.evc>>ENV_BITS]; \ } #endif /* ---------- calcrate one of channel ---------- */ static __inline__ void FM_CALC_CH( FM_CH *CH ) { UINT32 eg_out1,eg_out2,eg_out3,eg_out4; //envelope output /* Phase Generator */ #if FM_LFO_SUPPORT INT32 pms = lfo_pmd * CH->pms / LFO_RATE; if(pms) { pg_in1 = (CH->SLOT[SLOT1].Cnt += CH->SLOT[SLOT1].Incr + (INT32)(pms * CH->SLOT[SLOT1].Incr) / PMS_RATE); pg_in2 = (CH->SLOT[SLOT2].Cnt += CH->SLOT[SLOT2].Incr + (INT32)(pms * CH->SLOT[SLOT2].Incr) / PMS_RATE); pg_in3 = (CH->SLOT[SLOT3].Cnt += CH->SLOT[SLOT3].Incr + (INT32)(pms * CH->SLOT[SLOT3].Incr) / PMS_RATE); pg_in4 = (CH->SLOT[SLOT4].Cnt += CH->SLOT[SLOT4].Incr + (INT32)(pms * CH->SLOT[SLOT4].Incr) / PMS_RATE); } else #endif { pg_in1 = (CH->SLOT[SLOT1].Cnt += CH->SLOT[SLOT1].Incr); pg_in2 = (CH->SLOT[SLOT2].Cnt += CH->SLOT[SLOT2].Incr); pg_in3 = (CH->SLOT[SLOT3].Cnt += CH->SLOT[SLOT3].Incr); pg_in4 = (CH->SLOT[SLOT4].Cnt += CH->SLOT[SLOT4].Incr); } /* Envelope Generator */ FM_CALC_EG(eg_out1,CH->SLOT[SLOT1]); FM_CALC_EG(eg_out2,CH->SLOT[SLOT2]); FM_CALC_EG(eg_out3,CH->SLOT[SLOT3]); FM_CALC_EG(eg_out4,CH->SLOT[SLOT4]); /* Connection */ if( eg_out1 < EG_CUT_OFF ) /* SLOT 1 */ { if( CH->FB ){ /* with self feed back */ pg_in1 += (CH->op1_out[0]+CH->op1_out[1])>>CH->FB; CH->op1_out[1] = CH->op1_out[0]; } CH->op1_out[0] = OP_OUT(pg_in1,eg_out1); /* output slot1 */ if( !CH->connect1 ) { /* algorythm 5 */ pg_in2 += CH->op1_out[0]; pg_in3 += CH->op1_out[0]; pg_in4 += CH->op1_out[0]; }else{ /* other algorythm */ *CH->connect1 += CH->op1_out[0]; } } if( eg_out2 < EG_CUT_OFF ) /* SLOT 2 */ *CH->connect2 += OP_OUT(pg_in2,eg_out2); if( eg_out3 < EG_CUT_OFF ) /* SLOT 3 */ *CH->connect3 += OP_OUT(pg_in3,eg_out3); if( eg_out4 < EG_CUT_OFF ) /* SLOT 4 */ *CH->connect4 += OP_OUT(pg_in4,eg_out4); } /* ---------- frequency counter for operater update ---------- */ static __inline__ void CALC_FCSLOT(FM_SLOT *SLOT , int fc , int kc ) { int ksr; /* frequency step counter */ SLOT->Incr= (fc+SLOT->DT[kc])*SLOT->mul; /* verified on real chip */ /* SLOT->Incr= fc*SLOT->mul + SLOT->DT[kc]; */ ksr = kc >> SLOT->KSR; if( SLOT->ksr != ksr ) { SLOT->ksr = ksr; /* attack , decay rate recalcration */ SLOT->evsa = SLOT->AR[ksr]; SLOT->evsd = SLOT->DR[ksr]; SLOT->evss = SLOT->SR[ksr]; SLOT->evsr = SLOT->RR[ksr]; } } /* ---------- frequency counter ---------- */ static __inline__ void OPN_CALC_FCOUNT(FM_CH *CH ) { if( CH->SLOT[SLOT1].Incr==-1){ int fc = CH->fc; int kc = CH->kcode; CALC_FCSLOT(&CH->SLOT[SLOT1] , fc , kc ); CALC_FCSLOT(&CH->SLOT[SLOT2] , fc , kc ); CALC_FCSLOT(&CH->SLOT[SLOT3] , fc , kc ); CALC_FCSLOT(&CH->SLOT[SLOT4] , fc , kc ); } } /* ----------- initialize time tabls ----------- */ static void init_timetables( FM_ST *ST , UINT8 *DTTABLE , int ARRATE , int DRRATE ) { int i,d; double rate; /* DeTune table */ for (d = 0;d <= 3;d++){ for (i = 0;i <= 31;i++){ rate = (double)DTTABLE[d*32 + i] * ST->freqbase * FREQ_RATE; ST->DT_TABLE[d][i] = (INT32) rate; ST->DT_TABLE[d+4][i] = (INT32)-rate; } } /* make Attack & Decay tables */ for (i = 0;i < 4;i++) ST->AR_TABLE[i] = ST->DR_TABLE[i] = 0; for (i = 4;i < 64;i++){ rate = ST->freqbase; /* frequency rate */ if( i < 60 ) rate *= 1.0+(i&3)*0.25; /* b0-1 : x1 , x1.25 , x1.5 , x1.75 */ rate *= 1<<((i>>2)-1); /* b2-5 : shift bit */ rate *= (double)(EG_ENT<AR_TABLE[i] = (INT32)(rate / ARRATE); ST->DR_TABLE[i] = (INT32)(rate / DRRATE); } ST->AR_TABLE[62] = EG_AED; ST->AR_TABLE[63] = EG_AED; for (i = 64;i < 94 ;i++){ /* make for overflow area */ ST->AR_TABLE[i] = ST->AR_TABLE[63]; ST->DR_TABLE[i] = ST->DR_TABLE[63]; } } /* ---------- reset one of channel ---------- */ static void reset_channel( FM_ST *ST , FM_CH *CH , int chan ) { int c,s; ST->mode = 0; /* normal mode */ FM_STATUS_RESET(ST,0xff); ST->TA = 0; ST->TAC = 0; ST->TB = 0; ST->TBC = 0; for( c = 0 ; c < chan ; c++ ) { CH[c].fc = 0; CH[c].PAN = OUTD_CENTER; for(s = 0 ; s < 4 ; s++ ) { CH[c].SLOT[s].SEG = 0; CH[c].SLOT[s].eg_next= FM_EG_Release; CH[c].SLOT[s].evc = EG_OFF; CH[c].SLOT[s].eve = EG_OFF+1; CH[c].SLOT[s].evs = 0; } } } /* ---------- generic table initialize ---------- */ static int FMInitTable( void ) { int s,t; double rate; int i,j; double pom; /* allocate total level table plus+minus section */ TL_TABLE = (INT32 *)emu_Malloc(2*TL_MAX*sizeof(int)); if( TL_TABLE == 0 ) return 0; /* make total level table */ for (t = 0;t < TL_MAX ;t++){ if(t >= PG_CUT_OFF) rate = 0; /* under cut off area */ else rate = ((1< voltage */ TL_TABLE[ t] = (int)rate; TL_TABLE[TL_MAX+t] = -TL_TABLE[t]; } /* make sinwave table (pointer of total level) */ for (s = 1;s <= SIN_ENT/4;s++){ pom = sin(2.0*PI*s/SIN_ENT); /* sin */ pom = 20*log10(1/pom); /* -> decibel */ j = (int)(pom / EG_STEP); /* TL_TABLE steps */ /* cut off check */ if(j > PG_CUT_OFF) j = PG_CUT_OFF; /* degree 0 - 90 , degree 180 - 90 : plus section */ SIN_TABLE[ s] = SIN_TABLE[SIN_ENT/2-s] = &TL_TABLE[j]; /* degree 180 - 270 , degree 360 - 270 : minus section */ SIN_TABLE[SIN_ENT/2+s] = SIN_TABLE[SIN_ENT -s] = &TL_TABLE[TL_MAX+j]; } /* degree 0 = degree 180 = off */ SIN_TABLE[0] = SIN_TABLE[SIN_ENT/2] = &TL_TABLE[PG_CUT_OFF]; /* envelope counter -> envelope output table */ for (i=0; i= EG_ENT ) pom = EG_ENT-1; */ ENV_CURVE[i] = (int)pom; /* DECAY ,RELEASE curve */ ENV_CURVE[(EG_DST>>ENV_BITS)+i]= i; #if FM_SEG_SUPPORT /* DECAY UPSIDE (SSG ENV) */ ENV_CURVE[(EG_UST>>ENV_BITS)+i]= EG_ENT-1-i; #endif } /* off */ ENV_CURVE[EG_OFF>>ENV_BITS]= EG_ENT-1; /* decay to reattack envelope converttable */ j = EG_ENT-1; for (i=0; imode = v; /* reset Timer b flag */ if( v & 0x20 ) FM_STATUS_RESET(ST,0x02); /* reset Timer a flag */ if( v & 0x10 ) FM_STATUS_RESET(ST,0x01); /* load b */ if( v & 0x02 ) { if( ST->TBC == 0 ) { ST->TBC = ( 256-ST->TB)<<4; /* External timer handler */ if (ST->Timer_Handler) (ST->Timer_Handler)(n,1,ST->TBC,ST->TimerBase); } }else if (ST->timermodel == FM_TIMER_INTERVAL) { /* stop interbval timer */ if( ST->TBC != 0 ) { ST->TBC = 0; if (ST->Timer_Handler) (ST->Timer_Handler)(n,1,0,ST->TimerBase); } } /* 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,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 */ static __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,ST->TAC,ST->TimerBase); } else ST->TAC = 0; } /* Timer B Overflow */ static __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,ST->TBC,ST->TimerBase); } else ST->TBC = 0; } /* CSM Key Controll */ static __inline__ void CSMKeyControll(FM_CH *CH) { /* 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; CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL; CH->SLOT[SLOT3].TLL = CH->SLOT[SLOT3].TL; CH->SLOT[SLOT4].TLL = CH->SLOT[SLOT4].TL; /* all key on */ FM_KEYON(CH,SLOT1); FM_KEYON(CH,SLOT2); FM_KEYON(CH,SLOT3); FM_KEYON(CH,SLOT4); } #if BUILD_OPN /***********************************************************/ /* OPN unit */ /***********************************************************/ /* OPN 3slot struct */ typedef struct opn_3slot { UINT32 fc[3]; /* fnum3,blk3 :calcrated */ UINT8 fn_h[3]; /* freq3 latch */ UINT8 kcode[3]; /* key code : */ }FM_3SLOT; /* OPN/A/B common state */ typedef struct opn_f { UINT8 type; /* chip type */ FM_ST ST; /* general state */ FM_3SLOT SL3; /* 3 slot mode state */ FM_CH *P_CH; /* pointer of CH */ UINT32 FN_TABLE[2048]; /* fnumber -> increment counter */ #if FM_LFO_SUPPORT /* LFO */ UINT32 LFOCnt; UINT32 LFOIncr; UINT32 LFO_FREQ[8];/* LFO FREQ table */ #endif } FM_OPN; /* OPN key frequency number -> key code follow table */ /* fnum higher 4bit -> keycode lower 2bit */ static const UINT8 OPN_FKTABLE[16]={0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; #if FM_LFO_SUPPORT /* OPN LFO waveform table */ static INT32 OPN_LFO_wave[LFO_ENT]; #endif static int OPNInitTable(void) { int i; #if FM_LFO_SUPPORT /* LFO wave table */ for(i=0;iST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pris : 0; /* Timer base time */ OPN->ST.TimerBase = 1.0/((double)OPN->ST.clock / (double)TimerPris); // /* 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( i=0 ; i < 2048 ; i++ ) { /* it is freq table for octave 7 */ /* opn freq counter = 20bit */ OPN->FN_TABLE[i] = (UINT32)( (double)i * OPN->ST.freqbase * FREQ_RATE * (1<<7) / 2 ); } #if FM_LFO_SUPPORT /* LFO freq. table */ { /* 3.98Hz,5.56Hz,6.02Hz,6.37Hz,6.88Hz,9.63Hz,48.1Hz,72.2Hz @ 8MHz */ #define FM_LF(Hz) ((double)LFO_ENT*(1<LFO_FREQ[i] = (UINT32)(freq_table[i] * OPN->ST.freqbase); } } #endif } /* ---------- write a OPN mode register 0x20-0x2f ---------- */ static void OPNWriteMode(FM_OPN *OPN, int r, int v) { UINT8 c; FM_CH *CH; switch(r){ case 0x21: /* Test */ break; #if FM_LFO_SUPPORT case 0x22: /* LFO FREQ (YM2608/YM2612) */ if( OPN->type & TYPE_LFOPAN ) { OPN->LFOIncr = (v&0x08) ? OPN->LFO_FREQ[v&7] : 0; cur_chip = NULL; } break; #endif 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); break; } } /* ---------- write a OPN register (0x30-0xff) ---------- */ static void OPNWriteReg(FM_OPN *OPN, int r, int v) { UINT8 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_ON ENABLE(YM2612) */ set_dr(SLOT,v,OPN->ST.DR_TABLE); #if FM_LFO_SUPPORT if( OPN->type & TYPE_LFOPAN) { SLOT->amon = v>>7; SLOT->ams = CH->ams * SLOT->amon; } #endif 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 */ SLOT->SEG = v&0x0f; break; case 0xa0: switch( OPN_SLOT(r) ){ case 0: /* 0xa0-0xa2 : FNUM1 */ { UINT32 fn = (((UINT32)( (CH->fn_h)&7))<<8) + v; UINT8 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) { UINT32 fn = (((UINT32)(OPN->SL3.fn_h[c]&7))<<8) + v; UINT8 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+1 - feedback : 0; setup_connection( CH ); } break; case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2608) */ if( OPN->type & TYPE_LFOPAN) { #if FM_LFO_SUPPORT /* b0-2 PMS */ /* 0,3.4,6.7,10,14,20,40,80(cent) */ static const double pmd_table[8]={0,3.4,6.7,10,14,20,40,80}; static const int amd_table[4]={(int)(0/EG_STEP),(int)(1.4/EG_STEP),(int)(5.9/EG_STEP),(int)(11.8/EG_STEP) }; CH->pms = (INT32)( (1.5/1200.0)*pmd_table[v & 7] * PMS_RATE); /* b4-5 AMS */ /* 0 , 1.4 , 5.9 , 11.8(dB) */ CH->ams = amd_table[(v>>4) & 0x03]; CH->SLOT[SLOT1].ams = CH->ams * CH->SLOT[SLOT1].amon; CH->SLOT[SLOT2].ams = CH->ams * CH->SLOT[SLOT2].amon; CH->SLOT[SLOT3].ams = CH->ams * CH->SLOT[SLOT3].amon; CH->SLOT[SLOT4].ams = CH->ams * CH->SLOT[SLOT4].amon; #endif /* PAN */ CH->PAN = (v>>6)&0x03; /* PAN : b6 = R , b7 = L */ setup_connection( CH ); } break; } break; } } #endif /* BUILD_OPN */ #if BUILD_YM2612 /*******************************************************************************/ /* YM2612 local section */ /*******************************************************************************/ /* here's the virtual YM2612 */ typedef struct ym2612_f { FM_OPN OPN; /* OPN state */ FM_CH CH[6]; /* channel state */ int address1; /* address register1 */ /* dac output (YM2612) */ int dacen; int dacout; } YM2612; static int YM2612NumChips; /* total chip */ static YM2612 *FM2612=NULL; /* array of YM2612's */ static int dacen; /* ---------- update one of chip ----------- */ void YM2612UpdateOne(int num, INT16 **buffer, int length) { YM2612 *F2612 = &(FM2612[num]); FM_OPN *OPN = &(FM2612[num].OPN); int i; FM_CH *ch,*ech; FMSAMPLE *bufL,*bufR; int dacout = F2612->dacout; /* set bufer */ bufL = buffer[0]; bufR = buffer[1]; if( (void *)F2612 != cur_chip ){ cur_chip = (void *)F2612; State = &OPN->ST; cch[0] = &F2612->CH[0]; cch[1] = &F2612->CH[1]; cch[2] = &F2612->CH[2]; cch[3] = &F2612->CH[3]; cch[4] = &F2612->CH[4]; cch[5] = &F2612->CH[5]; /* DAC mode */ dacen = F2612->dacen; #if FM_LFO_SUPPORT LFOCnt = OPN->LFOCnt; LFOIncr = OPN->LFOIncr; if( !LFOIncr ) lfo_amd = lfo_pmd = 0; #endif } /* update frequency counter */ OPN_CALC_FCOUNT( cch[0] ); OPN_CALC_FCOUNT( cch[1] ); 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 OPN_CALC_FCOUNT( cch[2] ); OPN_CALC_FCOUNT( cch[3] ); OPN_CALC_FCOUNT( cch[4] ); OPN_CALC_FCOUNT( cch[5] ); ech = dacen ? cch[4] : cch[5]; /* buffering */ for( i=0; i < length ; i++ ) { #if FM_LFO_SUPPORT /* LFO */ if( LFOIncr ) { lfo_amd = OPN_LFO_wave[(LFOCnt+=LFOIncr)>>LFO_SHIFT]; lfo_pmd = lfo_amd-(LFO_RATE/2); } #endif /* clear output acc. */ out_ch[OUTD_LEFT] = out_ch[OUTD_RIGHT]= out_ch[OUTD_CENTER] = 0; /* calcrate channel output */ for(ch = cch[0] ; ch <= ech ; ch++) FM_CALC_CH( ch ); if( dacen ) *cch[5]->connect4 += dacout; /* buffering */ FM_BUFFERING_STEREO; /* timer A controll */ INTERNAL_TIMER_A( State , cch[2] ) } INTERNAL_TIMER_B(State,length) #if FM_LFO_SUPPORT OPN->LFOCnt = LFOCnt; #endif } /* -------------------------- YM2612 ---------------------------------- */ int YM2612Init(int num, int clock, int rate, FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler) { int i; if (FM2612) return (-1); /* duplicate init. */ cur_chip = NULL; /* hiro-shi!! */ YM2612NumChips = num; /* allocate extend state space */ if( (FM2612 = (YM2612 *)emu_Malloc(sizeof(YM2612) * YM2612NumChips))==NULL) return (-1); /* clear */ memset(FM2612,0,sizeof(YM2612) * YM2612NumChips); /* allocate total level table (128kb space) */ if( !OPNInitTable() ) { emu_Free( FM2612 ); return (-1); } for ( i = 0 ; i < YM2612NumChips; i++ ) { FM2612[i].OPN.ST.index = i; FM2612[i].OPN.type = TYPE_YM2612; FM2612[i].OPN.P_CH = FM2612[i].CH; FM2612[i].OPN.ST.clock = clock; FM2612[i].OPN.ST.rate = rate; /* FM2612[i].OPN.ST.irq = 0; */ /* FM2612[i].OPN.ST.status = 0; */ FM2612[i].OPN.ST.timermodel = FM_TIMER_INTERVAL; /* Extend handler */ FM2612[i].OPN.ST.Timer_Handler = TimerHandler; FM2612[i].OPN.ST.IRQ_Handler = IRQHandler; YM2612ResetChip(i); } return 0; } /* ---------- shut down emurator ----------- */ void YM2612Shutdown() { if (!FM2612) return; FMCloseTable(); emu_Free(FM2612); FM2612 = NULL; } /* ---------- reset one of chip ---------- */ void YM2612ResetChip(int num) { int i; YM2612 *F2612 = &(FM2612[num]); FM_OPN *OPN = &(FM2612[num].OPN); OPNSetPris( OPN , 12*12, 12*12, 0); /* status clear */ FM_IRQMASK_SET(&OPN->ST,0x03); OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */ reset_channel( &OPN->ST , &F2612->CH[0] , 6 ); for(i = 0xb6 ; i >= 0xb4 ; i-- ) { OPNWriteReg(OPN,i ,0xc0); OPNWriteReg(OPN,i|0x100,0xc0); } for(i = 0xb2 ; i >= 0x30 ; i-- ) { OPNWriteReg(OPN,i ,0); OPNWriteReg(OPN,i|0x100,0); } for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0); /* DAC mode clear */ F2612->dacen = 0; } /* YM2612 write */ /* n = number */ /* a = address */ /* v = value */ int YM2612Write(int n, int a,UINT8 v) { YM2612 *F2612 = &(FM2612[n]); int addr; switch( a&3){ case 0: /* address port 0 */ F2612->OPN.ST.address = v & 0xff; break; case 1: /* data port 0 */ addr = F2612->OPN.ST.address; switch( addr & 0xf0 ) { case 0x20: /* 0x20-0x2f Mode */ switch( addr ) { case 0x2a: /* DAC data (YM2612) */ F2612->dacout = ((int)v - 0x80)<<(TL_BITS-7); F2612->dacout = ((int)(v/2)-0x80)<<(TL_BITS-7); break; case 0x2b: /* DAC Sel (YM2612) */ /* b7 = dac enable */ F2612->dacen = v & 0x80; cur_chip = NULL; break; default: /* OPN section */ /* write register */ OPNWriteMode(&(F2612->OPN),addr,v); } break; default: /* 0x30-0xff OPN section */ /* write register */ OPNWriteReg(&(F2612->OPN),addr,v); } break; case 2: /* address port 1 */ F2612->address1 = v & 0xff; break; case 3: /* data port 1 */ addr = F2612->address1; OPNWriteReg(&(F2612->OPN),addr|0x100,v); break; } return F2612->OPN.ST.irq; } UINT8 YM2612Read(int n,int a) { YM2612 *F2612 = &(FM2612[n]); switch( a&3){ case 0: /* status 0 */ return F2612->OPN.ST.status; case 1: case 2: case 3: return F2612->OPN.ST.status; } return 0; } int YM2612TimerOver(int n,int c) { YM2612 *F2612 = &(FM2612[n]); if( c ) { /* Timer B */ TimerBOver( &(F2612->OPN.ST) ); } else { /* Timer A */ /* timer update */ TimerAOver( &(F2612->OPN.ST) ); /* CSM mode key,TL controll */ if( F2612->OPN.ST.mode & 0x80 ) { /* CSM mode total level latch and auto key on */ CSMKeyControll( &(F2612->CH[2]) ); } } return F2612->OPN.ST.irq; } #endif /* BUILD_YM2612 */