📄 fm.c
字号:
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 */
#ifndef FM_OUTPUT_PROC
if( sample_16bit ) ((unsigned short *)buf)[i] = data >> OPN_OUTSB;
else ((unsigned char *)buf)[i] = data >> OPN_OUTSB_8;
#else
outputproc( (void **)&buf, data );
#endif
#ifdef INTERNAL_TIMER
/* timer controll */
CALC_TIMER_A( num, State , cch[2] );
#endif
}
#ifdef INTERNAL_TIMER
CALC_TIMER_B( num, 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->index);
/* status clear */
OPN->ST.irqmask = 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, int bitsize ,
FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler)
{
int i;
if (FM2203) return (-1); /* duplicate init. */
FMNumChips = num;
if( bitsize == 16 ) sample_16bit = 1;
else sample_16bit = 0;
/* 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.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;
/* Extend handler */
FM2203[i].OPN.ST.Timer_Handler = TimerHandler;
FM2203[i].OPN.ST.IRQ_Handler = IRQHandler;
YM2203ResetChip(i);
}
#ifdef FM_OUTPUT_PROC
if( sample_16bit ){
outputproc = (void (*)( void **, int ))bufout16;
} else{
outputproc = (void (*)( void **, int ))bufout8;
}
#endif
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,int 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;
}
unsigned char 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( n,&(F2203->OPN.ST) );
}
else
{ /* Timer A */
YM2203UpdateReq(n);
/* timer update */
TimerAOver( n,&(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 */
#ifdef BUILD_YM2608
/*******************************************************************************/
/* YM2608 local section */
/*******************************************************************************/
static YM2608 *FM2608=NULL; /* array of YM2608's */
/* 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 */
F2608->OPN.ST.status |= 0x08; /* BRDY = 1 */
if( !F2608->OPN.ST.irq && (F2608->OPN.ST.status & F2608->OPN.ST.irqmask) )
{
F2608->OPN.ST.irq = 1;
/* callback user interrupt handler */
if(EXIRQHandler) EXIRQHandler(n,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;
F2608->OPN.ST.status |= 0x08; /* BRDY = 1 */
if( !F2608->OPN.ST.irq && (F2608->OPN.ST.status & F2608->OPN.ST.irqmask) )
{
F2608->OPN.ST.irq = 1;
/* callback user interrupt handler */
if(EXIRQHandler) EXIRQHandler(n,1);
}
}
}
/* ---------- ADPCM Register Write 0x100-0x10f ---------- */
INLINE void YM2608ADPCMWrite(int n,int r,int v)
{
YM2608 *F2608 = &(FM2608[n]);
switch(r)
{
case 0x00:
{
/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
int cng = F2608->ADMode ^ v;
F2608->ADMode = v;
switch( v & 0xe0 )
{
case 0x80: /* ADPCM play from PCM data port after write PCM data */
break;
case 0xa0: /* ADPCM play from buffer memory */
break;
case 0x60: /* write buffer MEMORY from PCM data port */
break;
case 0x20: /* read buffer MEMORY to PCM data port */
break;
}
}
break;
case 0x01:
/* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
F2608->ADPAN = v>>6;
break;
case 0x02: /* Start Address L */
F2608->ADStart = (F2608->ADStart & 0xff00) | v;
break;
case 0x03: /* Start Address H */
F2608->ADStart = (F2608->ADStart & 0x00ff) | (v<<8);
break;
case 0x04: /* Stop Address L */
F2608->ADStop = (F2608->ADStop & 0xff00) | v;
break;
case 0x05: /* Stop Address H */
F2608->ADStop = (F2608->ADStop & 0x00ff) | (v<<8);
break;
case 0x06: /* Prescale L (PCM and Recoard frq) */
case 0x07: /* Proscale H */
break;
case 0x08: /* ADPCM data */
break;
case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */
F2608->ADDelta = (F2608->ADDelta & 0xff00) | v;
break;
case 0x0a: /* DELTA-N H */
F2608->ADDelta = (F2608->ADDelta & 0x00ff) | (v<<8);
break;
case 0x0b: /* Level control (volume) */
F2608->ADTL = v;
break;
case 0x0c: /* Limit address L */
F2608->ADLimit = (F2608->ADLimit & 0xff00) | v;
break;
case 0x0d: /* Limit address H */
F2608->ADLimit = (F2608->ADLimit & 0x00ff) | (v<<8);
break;
case 0x0e: /* DAC data */
break;
case 0x0f: /* PCM data port */
F2608->ADData = v;
F2608->OPN.ST.status &= ~0x08;
if( F2608->OPN.ST.irq && !(F2608->OPN.ST.status & F2608->OPN.ST.irqmask) )
{
F2608->OPN.ST.irq = 0;
/* callback user interrupt handler */
if(EXIRQHandler) EXIRQHandler(n,0);
}
break;
}
}
/* ---------- Rhythm Register Write 0x10-0x1f ---------- */
INLINE void YM2608RhythmWrite(int n,int r,int v)
{
YM2608 *F2608 = &(FM2608[n]);
int c;
switch( r ){
case 0x10: /* DM,--,RIM,TOM,HH,TOP,SD,BD */
if( v & 0x80 )
{ /* dump */
if( v & 0x20 ) RhythmStop(n,RY_RIM);
if( v & 0x10 ) RhythmStop(n,RY_TOM);
if( v & 0x08 ) RhythmStop(n,RY_HH);
if( v & 0x04 ) RhythmStop(n,RY_TOP);
if( v & 0x02 ) RhythmStop(n,RY_SD);
if( v & 0x01 ) RhythmStop(n,RY_BD);
}
else
{ /* key on */
if( v & 0x20 ) RhythmStart(n,RY_RIM);
if( v & 0x10 ) RhythmStart(n,RY_TOM);
if( v & 0x08 ) RhythmStart(n,RY_HH);
if( v & 0x04 ) RhythmStart(n,RY_TOP);
if( v & 0x02 ) RhythmStart(n,RY_SD);
if( v & 0x01 ) RhythmStart(n,RY_BD);
}
break;
case 0x11: /* B0-5 = RTL */
F2608->RTL = (v & 0x3f);
break;
case 0x18: /* BD : B7=L,B6=R,B4-0=IL */
case 0x19: /* SD : B7=L,B6=R,B4-0=IL */
case 0x1A: /* TOP : B7=L,B6=R,B4-0=IL */
case 0x1B: /* HH : B7=L,B6=R,B4-0=IL */
case 0x1C: /* TOM : B7=L,B6=R,B4-0=IL */
case 0x1D: /* RIM : B7=L,B6=R,B4-0=IL */
c = r & 0x18;
/* B7=L,B6=R,B4-0=BD IL */
F2608->RPAN[c] = v>>6;
F2608->RIL[c] = v&0x1f;
break;
}
}
/* ---------- IRQ flag Controll Write 0x110 ---------- */
INLINE void YM2608IRQFlagWrite(FM_ST *ST,int n,int v)
{
if( v & 0x80 )
{ /* Reset IRQ flag */
ST->status = 0x00;
}
else
{ /* Set IRQ mask */
ST->irqmask = v & 0x1f;
}
/* IRQ handling */
if( ST->status & ST->irqmask )
{
if( !(ST->irq) )
{
ST->irq = 1;
/* callback user interrupt handler */
if(EXIRQHandler) EXIRQHandler(n,1);
}
}
else
{
if( ST->irq )
{
ST->irq = 0;
/* callback user interrupt handler */
if(EXIRQHandler) EXIRQHandler(n,0);
}
}
}
/* ---------- update one of chip ----------- */
void YM2608UpdateOne(int num, FMSAMPLE **buffer, int length)
{
YM2608 *F2608 = &(FM2608[num]);
FM_OPN *OPN = &(FM2608[num].OPN);
int dataR,dataL;
int i,ch;
/* set bufer */
bufL = buffer[0];
bufR = buffer[1];
if( (void *)F2608 != cur_chip ){
cur_chip = (void *)F2608;
State = &OPN->ST;
cch[0] = &F2608->CH[0];
cch[1] = &F2608->CH[1];
cch[2] = &F2608->CH[2];
cch[3] = &F2608->CH[3];
cch[4] = &F2608->CH[4];
cch[5] = &F2608->CH[5];
}
/* update frequency counter */
CALC_FCOUNT( cch[0] );
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 CALC_FCOUNT( cch[2] );
CALC_FCOUNT( cch[3] );
CALC_FCOUNT( cch[4] );
CALC_FCOUNT( cch[5] );
/* buffering */
for( i=0; i < length ; i++ )
{
/* clear output acc. */
outd[OPN_LEFT] = outd[OPN_RIGHT]= outd[OPN_CENTER] = 0;
/* calcrate FM */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -