📄 fm.c
字号:
/* limit check */ Limit( out_ch[OUTD_CENTER] , FM_MAXOUT, FM_MINOUT ); /* store to sound buffer */ buf[i] = out_ch[OUTD_CENTER] >> FM_OUTSB; /* timer controll */ INTERNAL_TIMER_A( State , cch[2] ) } INTERNAL_TIMER_B(State,length)}/* ---------- 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);}/* ---------- 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!! */ YM2203NumChips = num; /* allocate ym2203 state space */ if( (FM2203 = (YM2203 *)malloc(sizeof(YM2203) * YM2203NumChips))==NULL) return (-1); /* clear */ memset(FM2203,0,sizeof(YM2203) * YM2203NumChips); /* allocate total level table (128kb space) */ if( !OPNInitTable() ) { free( FM2203 ); return (-1); } for ( i = 0 ; i < YM2203NumChips; 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_INTERVAL; /* 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(); free(FM2203); FM2203 = NULL;}/* ---------- YM2203 I/O interface ---------- */int YM2203Write(int n,int a,UINT8 v){ FM_OPN *OPN = &(FM2203[n].OPN); if( !(a&1) ) { /* address port */ OPN->ST.address = v & 0xff; /* Write register to SSG emurator */ if( v < 16 ) SSGWrite(n,0,v); switch(OPN->ST.address) { case 0x2d: /* divider sel */ OPNSetPris( OPN, 6*12, 6*12 ,4); /* OPN 1/6 , SSG 1/4 */ break; case 0x2e: /* divider sel */ OPNSetPris( OPN, 3*12, 3*12,2); /* OPN 1/3 , SSG 1/2 */ break; case 0x2f: /* divider sel */ OPNSetPris( OPN, 2*12, 2*12,1); /* OPN 1/2 , SSG 1/1 */ break; } } else { /* data port */ int addr = OPN->ST.address; switch( addr & 0xf0 ) { case 0x00: /* 0x00-0x0f : SSG section */ /* Write data to SSG emurator */ SSGWrite(n,a,v); break; case 0x20: /* 0x20-0x2f : Mode section */ YM2203UpdateReq(n); /* write register */ OPNWriteMode(OPN,addr,v); break; default: /* 0x30-0xff : OPN section */ YM2203UpdateReq(n); /* write register */ OPNWriteReg(OPN,addr,v); } } return OPN->ST.irq;}UINT8 YM2203Read(int n,int a){ YM2203 *F2203 = &(FM2203[n]); int addr = F2203->OPN.ST.address; int ret = 0; if( !(a&1) ) { /* status port */ ret = F2203->OPN.ST.status; } else { /* data port (ONLY SSG) */ if( addr < 16 ) ret = SSGRead(n); } return ret;}int YM2203TimerOver(int n,int c){ YM2203 *F2203 = &(FM2203[n]); if( c ) { /* Timer B */ TimerBOver( &(F2203->OPN.ST) ); } else { /* Timer A */ YM2203UpdateReq(n); /* timer update */ TimerAOver( &(F2203->OPN.ST) ); /* CSM mode key,TL controll */ if( F2203->OPN.ST.mode & 0x80 ) { /* CSM mode total level latch and auto key on */ CSMKeyControll( &(F2203->CH[2]) ); } } return F2203->OPN.ST.irq;}#endif /* BUILD_YM2203 */#if (BUILD_YM2608||BUILD_OPNB)/* adpcm type A struct */typedef struct adpcm_state { UINT8 flag; /* port state */ UINT8 flagMask; /* arrived flag mask */ UINT8 now_data; UINT32 now_addr; UINT32 now_step; UINT32 step; UINT32 start; UINT32 end; int IL; int volume; /* calcrated mixing level */ INT32 *pan; /* &out_ch[OPN_xxxx] */ int /*adpcmm,*/ adpcmx, adpcmd; int adpcml; /* hiro-shi!! */}ADPCM_CH;/* here's the virtual YM2610 */typedef struct ym2610_f { FM_OPN OPN; /* OPN state */ FM_CH CH[6]; /* channel state */ int address1; /* address register1 */ /* ADPCM-A unit */ UINT8 *pcmbuf; /* pcm rom buffer */ UINT32 pcm_size; /* size of pcm rom */ INT32 *adpcmTL; /* adpcmA total level */ ADPCM_CH adpcm[6]; /* adpcm channels */ UINT32 adpcmreg[0x30]; /* registers */ UINT8 adpcm_arrivedEndAddress; /* Delta-T ADPCM unit */ YM_DELTAT deltaT;} YM2610;/* here's the virtual YM2608 */typedef YM2610 YM2608;#endif /* (BUILD_YM2608||BUILD_OPNB) */#if BUILD_FM_ADPCMA/***************************************************************//* ADPCMA units are made by Hiromitsu Shioya (MMSND) *//***************************************************************//**** YM2610 ADPCM defines ****/#define ADPCMA_MIXING_LEVEL (3) /* ADPCMA mixing level */#define ADPCM_SHIFT (16) /* frequency step rate */#define ADPCMA_ADDRESS_SHIFT 8 /* adpcm A address shift *//*#define ADPCMA_DECODE_RANGE 1024 */#define ADPCMA_DECODE_RANGE 2048#define ADPCMA_DECODE_MIN (-(ADPCMA_DECODE_RANGE*ADPCMA_MIXING_LEVEL))#define ADPCMA_DECODE_MAX ((ADPCMA_DECODE_RANGE*ADPCMA_MIXING_LEVEL)-1)#define ADPCMA_VOLUME_DIV 1static UINT8 *pcmbufA;static UINT32 pcmsizeA;/************************************************************//************************************************************//* --------------------- subroutines --------------------- *//************************************************************//************************************************************//************************//* ADPCM A tables *//************************/static int jedi_table[(48+1)*16];static int decode_tableA1[16] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16, -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16};/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 *//* 8 = -1 , 2 5 8 11 *//* 9 = -1 , 2 5 9 13 *//* 10= -1 , 2 6 10 14 *//* 12= -1 , 2 7 12 17 *//* 20= -2 , 4 12 20 32 */#if 1static void InitOPNB_ADPCMATable(void){ int step, nib; for (step = 0; step <= 48; step++) { double stepval = floor(16.0 * pow (11.0 / 10.0, (double)step) * ADPCMA_MIXING_LEVEL); /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { int value = (int)stepval*((nib&0x07)*2+1)/8; jedi_table[step*16+nib] = (nib&0x08) ? -value : value; } }}#elsestatic int decode_tableA2[49] = { 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001c, 0x001f, 0x0022, 0x0025, 0x0029, 0x002d, 0x0032, 0x0037, 0x003c, 0x0042, 0x0049, 0x0050, 0x0058, 0x0061, 0x006b, 0x0076, 0x0082, 0x008f, 0x009d, 0x00ad, 0x00be, 0x00d1, 0x00e6, 0x00fd, 0x0117, 0x0133, 0x0151, 0x0173, 0x0198, 0x01c1, 0x01ee, 0x0220, 0x0256, 0x0292, 0x02d4, 0x031c, 0x036c, 0x03c3, 0x0424, 0x048e, 0x0502, 0x0583, 0x0610};static void InitOPNB_ADPCMATable(void){ int ta,tb,tc; for(ta=0;ta<49;ta++){ for(tb=0;tb<16;tb++){ tc=0; if(tb&0x04){tc+=((decode_tableA2[ta]*ADPCMA_MIXING_LEVEL));} if(tb&0x02){tc+=((decode_tableA2[ta]*ADPCMA_MIXING_LEVEL)>>1);} if(tb&0x01){tc+=((decode_tableA2[ta]*ADPCMA_MIXING_LEVEL)>>2);} tc+=((decode_tableA2[ta]*ADPCMA_MIXING_LEVEL)>>3); if(tb&0x08){tc=(0-tc);} jedi_table[ta*16+tb]=tc; } }}#endif/**** ADPCM A (Non control type) ****/INLINE void OPNB_ADPCM_CALC_CHA( YM2610 *F2610, ADPCM_CH *ch ){ UINT32 step; int data; ch->now_step += ch->step; if ( ch->now_step >= (1<<ADPCM_SHIFT) ) { step = ch->now_step >> ADPCM_SHIFT; ch->now_step &= (1<<ADPCM_SHIFT)-1; /* end check */ if ( (ch->now_addr+step) > (ch->end<<1) ) { ch->flag = 0; F2610->adpcm_arrivedEndAddress |= ch->flagMask; return; } do{#if 0 if ( ch->now_addr > (pcmsizeA<<1) ) { LOG(LOG_WAR,("YM2610: Attempting to play past adpcm rom size!\n" )); return; }#endif if( ch->now_addr&1 ) data = ch->now_data & 0x0f; else { ch->now_data = *(pcmbufA+(ch->now_addr>>1)); data = (ch->now_data >> 4)&0x0f; } ch->now_addr++; ch->adpcmx += jedi_table[ch->adpcmd+data]; Limit( ch->adpcmx,ADPCMA_DECODE_MAX, ADPCMA_DECODE_MIN ); ch->adpcmd += decode_tableA1[data]; Limit( ch->adpcmd, 48*16, 0*16 ); /**** calc pcm * volume data ****/ ch->adpcml = ch->adpcmx * ch->volume; }while(--step); } /* output for work of output channels (out_ch[OPNxxxx])*/ *(ch->pan) += ch->adpcml;}/* ADPCM type A */static void FM_ADPCMAWrite(YM2610 *F2610,int r,int v){ ADPCM_CH *adpcm = F2610->adpcm; UINT8 c = r&0x07; F2610->adpcmreg[r] = v&0xff; /* stock data */ switch( r ){ case 0x00: /* DM,--,C5,C4,C3,C2,C1,C0 */ /* F2610->port1state = v&0xff; */ if( !(v&0x80) ){ /* KEY ON */ for( c = 0; c < 6; c++ ){ if( (1<<c)&v ){ /**** start adpcm ****/ adpcm[c].step = (UINT32)((float)(1<<ADPCM_SHIFT)*((float)F2610->OPN.ST.freqbase)/3.0); adpcm[c].now_addr = adpcm[c].start<<1; adpcm[c].now_step = (1<<ADPCM_SHIFT)-adpcm[c].step; /*adpcm[c].adpcmm = 0;*/ adpcm[c].adpcmx = 0; adpcm[c].adpcmd = 0; adpcm[c].adpcml = 0; adpcm[c].flag = 1; if(F2610->pcmbuf==NULL){ /* Check ROM Mapped *//* LOG(LOG_WAR,("YM2610: ADPCM-A rom not mapped\n")); */ adpcm[c].flag = 0; } else{ if(adpcm[c].end >= F2610->pcm_size){ /* Check End in Range *//* LOG(LOG_WAR,("YM2610: ADPCM-A end out of range: $%08x\n",adpcm[c].end)); */ adpcm[c].end = F2610->pcm_size-1; } if(adpcm[c].start >= F2610->pcm_size) { /* Check Start in Range *//* LOG(LOG_WAR,("YM2610: ADPCM-A start out of range: $%08x\n",adpcm[c].start)); */ adpcm[c].flag = 0; }/*LOG(LOG_WAR,("YM2610: Start %06X : %02X %02X %02X\n",adpcm[c].start,pcmbufA[adpcm[c].start],pcmbufA[adpcm[c].start+1],pcmbufA[adpcm[c].start+2]));*/ } } /*** (1<<c)&v ***/ } /**** for loop ****/ } else{ /* KEY OFF */ for( c = 0; c < 6; c++ ){ if( (1<<c)&v ) adpcm[c].flag = 0; } } break; case 0x01: /* B0-5 = TL 0.75dB step */ F2610->adpcmTL = &(TL_TABLE[((v&0x3f)^0x3f)*(int)(0.75/EG_STEP)]); for( c = 0; c < 6; c++ ) { adpcm[c].volume = F2610->adpcmTL[adpcm[c].IL*(int)(0.75/EG_STEP)] / ADPCMA_DECODE_RANGE / ADPCMA_VOLUME_DIV; /**** calc pcm * volume data ****/ adpcm[c].adpcml = adpcm[c].adpcmx * adpcm[c].volume; } break; default: c = r&0x07; if( c >= 0x06 ) return; switch( r&0x38 ){ case 0x08: /* B7=L,B6=R,B4-0=IL */ adpcm[c].IL = (v&0x1f)^0x1f; adpcm[c].volume = F2610->adpcmTL[adpcm[c].IL*(int)(0.75/EG_STEP)] / ADPCMA_DECODE_RANGE / ADPCMA_VOLUME_DIV; adpcm[c].pan = &out_ch[(v>>6)&0x03]; /**** calc pcm * volume data ****/ adpcm[c].adpcml = adpcm[c].adpcmx * adpcm[c].volume; break; case 0x10: case 0x18: adpcm[c].start = ( (F2610->adpcmreg[0x18 + c]*0x0100 | F2610->adpcmreg[0x10 + c]) << ADPCMA_ADDRESS_SHIFT); break; case 0x20: case 0x28: adpcm[c].end = ( (F2610->adpcmreg[0x28 + c]*0x0100 | F2610->adpcmreg[0x20 + c]) << ADPCMA_ADDRESS_SHIFT); adpcm[c].end += (1<<ADPCMA_ADDRESS_SHIFT) - 1; break; } }}#endif /* BUILD_FM_ADPCMA */#if BUILD_YM2608/*******************************************************************************//* YM2608 local section *//*******************************************************************************/static YM2608 *FM2608=NULL; /* array of YM2608's */static int YM2608NumChips; /* total chip *//* YM2608 Rhythm Number */#define RY_BD 0#define RY_SD 1#define RY_TOP 2#define RY_HH 3#define RY_TOM 4#define RY_RIM 5#if 0/* Get next pcm data */INLINE int YM2608ReadADPCM(int n){ YM2608 *F2608 = &(FM2608[n]); if( F2608->ADMode & 0x20 ) { /* buffer memory */ /* F2203->OPN.ST.status |= 0x04; */ return 0; } else { /* from PCM data register */ FM_STATUS_SET(F2608->OPN.ST,0x08); /* BRDY = 1 */ return F2608->ADData; }}/* Put decoded data */INLINE void YM2608WriteADPCM(int n,int v){ YM2608 *F2608 = &(FM2608[n]); if( F2608->ADMode & 0x20 ) { /* for buffer */ return; } else { /* for PCM data port */ F2608->ADData = v; FM_STATUS_SET(F2608->OPN.ST,0x08) /* BRDY = 1 */ }}#endif/* ---------- IRQ flag Controll Write 0x110 ---------- */INLINE void YM2608IRQFlagWrite(FM_ST *ST,int n,int v){ if( v & 0x80 ) { /* Reset IRQ flag */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -