fm.c
来自「DGen源码最后版本」· C语言 代码 · 共 2,003 行 · 第 1/5 页
C
2,003 行
/* !!!!! preliminary !!!!! */
#define DV (1/EG_STEP)
static const unsigned char KSL[32]=
{
#if 1
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
#else
0.000/DV , 0.000/DV , 0.000/DV , 0.000/DV , /* OCT 0 */
0.000/DV , 0.000/DV , 0.000/DV , 1.875/DV , /* OCT 1 */
0.000/DV , 0.000/DV , 3.000/DV , 4.875/DV , /* OCT 2 */
0.000/DV , 3.000/DV , 6.000/DV , 7.875/DV , /* OCT 3 */
0.000/DV , 6.000/DV , 9.000/DV ,10.875/DV , /* OCT 4 */
0.000/DV , 9.000/DV ,12.000/DV ,13.875/DV , /* OCT 5 */
0.000/DV ,12.000/DV ,15.000/DV ,16.875/DV , /* OCT 6 */
0.000/DV ,15.000/DV ,18.000/DV ,19.875/DV /* OCT 7 */
#endif
};
#undef DV
/* OPN key frequency number -> key code follow table */
/* fnum higher 4bit -> keycode lower 2bit */
static const char OPN_FKTABLE[16]={0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3};
static const int KC_TO_SEMITONE[16]={
/*translate note code KC into more usable number of semitone*/
0*64, 1*64, 2*64, 3*64,
3*64, 4*64, 5*64, 6*64,
6*64, 7*64, 8*64, 9*64,
9*64,10*64,11*64,12*64
};
static const int DT2_TABLE[4]={ /* 4 DT2 values */
/*
* DT2 defines offset in cents from base note
*
* The table below defines offset in deltas table...
* User's Manual page 22
* Values below were calculated using formula: value = orig.val * 1.5625
*
* DT2=0 DT2=1 DT2=2 DT2=3
* 0 600 781 950
*/
0, 384, 500, 608
};
/* 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) (db*((3/EG_STEP)*(1<<ENV_BITS)))+EG_DST
static const int SL_TABLE[16]={
SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
};
#undef SC
#ifdef TL_SAVE_MEM
#define TL_MAX (EG_ENT*2) /* limit(tl + ksr + envelope) + sinwave */
#else
#define TL_MAX (EG_ENT*4) /* tl + ksr + envelope + sinwave */
#endif
/* TotalLevel : 48 24 12 6 3 1.5 0.75 (dB) */
/* TL_TABLE[ 0 to TL_MAX ] : plus section */
/* TL_TABLE[ TL_MAX to TL_MAX+TL_MAX-1 ] : minus section */
static int *TL_TABLE;
/* pointers to TL_TABLE with sinwave output offset */
static signed int *SIN_TABLE[SIN_ENT];
/* envelope output curve table */
#ifdef SEG_SUPPORT
/* attack + decay + SSG upside + OFF */
static int ENV_CURVE[3*EG_ENT+1];
#else
/* attack + decay + OFF */
static int ENV_CURVE[2*EG_ENT+1];
#endif
/* envelope counter conversion table when change Decay to Attack phase */
static int DRAR_TABLE[EG_ENT];
#define OPM_DTTABLE OPN_DTTABLE
static char OPN_DTTABLE[4 * 32]={
/* this table is YM2151 and YM2612 data */
/* FD=0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* FD=1 */
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
/* FD=2 */
1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
/* FD=3 */
2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
};
/* multiple table */
#define ML 2
static const int MUL_TABLE[4*16]= {
/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 */
0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
8.00*ML, 9.00*ML,10.00*ML,11.00*ML,12.00*ML,13.00*ML,14.00*ML,15.00*ML,
/* DT2=1 *SQL(2) */
0.71*ML, 1.41*ML, 2.82*ML, 4.24*ML, 5.65*ML, 7.07*ML, 8.46*ML, 9.89*ML,
11.30*ML,12.72*ML,14.10*ML,15.55*ML,16.96*ML,18.37*ML,19.78*ML,21.20*ML,
/* DT2=2 *SQL(2.5) */
0.78*ML, 1.57*ML, 3.14*ML, 4.71*ML, 6.28*ML, 7.85*ML, 9.42*ML,10.99*ML,
12.56*ML,14.13*ML,15.70*ML,17.27*ML,18.84*ML,20.41*ML,21.98*ML,23.55*ML,
/* DT2=3 *SQL(3) */
0.87*ML, 1.73*ML, 3.46*ML, 5.19*ML, 6.92*ML, 8.65*ML,10.38*ML,12.11*ML,
13.84*ML,15.57*ML,17.30*ML,19.03*ML,20.76*ML,22.49*ML,24.22*ML,25.95*ML
};
#undef ML
#ifdef LFO_SUPPORT
/* LFO frequency timer table */
static int OPM_LFO_TABLE[256];
#endif
/* dummy attack / decay rate ( when rate == 0 ) */
static int RATE_0[32]=
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/* -------------------- state --------------------- */
/* some globals */
#define TYPE_SSG 0x01 /* SSG support */
#define TYPE_OPN 0x02 /* OPN device */
#define TYPE_LFOPAN 0x04 /* OPN type LFO and PAN */
#define TYPE_6CH 0x08 /* FM 6CH / 3CH */
#define TYPE_DAC 0x10 /* YM2612's DAC device */
#define TYPE_ADPCM 0x20 /* ADPCM device */
#define TYPE_YM2203 (TYPE_SSG)
#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
#define TYPE_YM2612 (TYPE_6CH |TYPE_LFOPAN |TYPE_DAC)
static int FMNumChips; /* total # of FM emulated */
/* work table */
static void *cur_chip = 0; /* current chip point */
/* currenct chip state */
static FM_ST *State;
static FMSAMPLE *bufL,*bufR;
static FM_CH *cch[8];
static signed int outd[4];
/* operator connection work */
static int feedback2; /* connect for operator 2 */
static int feedback3; /* connect for operator 3 */
static int feedback4; /* connect for operator 4 */
/* log output level */
#define LOG_ERR 3 /* ERROR */
#define LOG_WAR 2 /* WARNING */
#define LOG_INF 1 /* INFORMATION */
#define LOG_LEVEL LOG_INF
#ifndef __RAINE__
static void Log(int level,char *format,...)
{
int i;
va_list argptr;
if( level < LOG_LEVEL ) return;
va_start(argptr,format);
/* */
//if (errorlog) vfprintf( errorlog, format , argptr);
}
#endif
/* --------------- Customize External interface port (SSG,Timer,etc) ---------------*/
//#include "fmext.c"
/* --------------------- subroutines --------------------- */
INLINE int Limit( int val, int max, int min ) {
if ( val > max )
val = max;
else if ( val < min )
val = min;
return val;
}
/* status set and IRQ handling */
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 */
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 */
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);
}
/* ----- key on ----- */
INLINE void FM_KEYON(FM_CH *CH , int s )
{
FM_SLOT *SLOT = &CH->SLOT[s];
if( SLOT->evm<= ENV_MOD_RR)
{
/* set envelope counter from envleope output */
/* sin wave restart */
SLOT->Cnt = 0;
if( s == SLOT1 ) CH->op1_out = 0;
/* set attack */
#ifdef SEG_SUPPORT
if( SLOT->SEG&8 ) ENV_SSG_AR;
else
#endif
SLOT->evm = ENV_MOD_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 ----- */
INLINE void FM_KEYOFF(FM_CH *CH , int s )
{
FM_SLOT *SLOT = &CH->SLOT[s];
if( SLOT->evm > ENV_MOD_RR)
{
/* set envelope counter from envleope output */
SLOT->evm = ENV_MOD_RR;
if( !(SLOT->evc&EG_DST) )
SLOT->evc = (ENV_CURVE[SLOT->evc>>ENV_BITS]<<ENV_BITS) + EG_DST;
SLOT->eve = EG_DED;
SLOT->evs = SLOT->evsr;
}
}
/* ---------- calcrate Envelope Generator & Phase Generator ---------- */
/* return : envelope output */
INLINE signed int FM_CALC_SLOT( FM_SLOT *SLOT )
{
/* calcrate phage generator */
SLOT->Cnt += SLOT->Incr;
/* calcrate envelope generator */
if( (SLOT->evc+=SLOT->evs) >= SLOT->eve )
{
switch( SLOT->evm ){
case ENV_MOD_AR: /* ATTACK -> DECAY1 */
/* next DR */
SLOT->evm = ENV_MOD_DR;
SLOT->evc = EG_DST;
SLOT->eve = SLOT->SL;
SLOT->evs = SLOT->evsd;
break;
case ENV_MOD_DR: /* DECAY -> SUSTAIN */
SLOT->evm = ENV_MOD_SR;
SLOT->evc = SLOT->SL;
SLOT->eve = EG_DED;
SLOT->evs = SLOT->evss;
break;
case ENV_MOD_RR: /* RR -> OFF & STOP */
SLOT->evm = ENV_MOD_OFF;
case ENV_MOD_SR: /* SR -> OFF & STOP */
SLOT->evc = EG_OFF;
SLOT->eve = EG_OFF+1;
SLOT->evs = 0;
break;
#ifdef SEG_SUPPORT
case ENV_SSG_AR: /* SSG ATTACK */
if( SLOT->SEG&4){ /* start direction */
/* next SSG-SR (upside start ) */
SLOT->evm = ENV_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->evm = ENV_SSG_DR;
SLOT->evc = EG_DST;
SLOT->eve = EG_DED;
SLOT->evs = SLOT->evsd;
}
break;
case ENV_SSG_DR: /* SEG down side */
if( SLOT->SEG&2){
/* reverce */
SLOT->evm = ENV_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;
break;
case ENV_SSG_SR: /* upside */
if( SLOT->SEG&2){
/* reverce */
SLOT->evm = ENV_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;
break;
#endif
}
}
/* calcrate envelope */
#if 0 /* ifdef TL_SAVE_MEM */
signed int env_out = SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]; /* LFO_out[SLOT->AMS] */
if(env_out >= (EG_ENT-1) ) return EG_ENT-1;
return env_out;
#else
return SLOT->TLL+ENV_CURVE[SLOT->evc>>ENV_BITS]; /* LFO_out[SLOT->AMS] */
#endif
}
/* set algorythm connection */
static void set_algorythm( FM_CH *CH )
{
signed int *carrier = &outd[CH->PAN];
/* setup connect algorythm */
switch( CH->ALGO ){
case 0:
/* PG---S1---S2---S3---S4---OUT */
CH->connect1 = &feedback2;
CH->connect2 = &feedback3;
CH->connect3 = &feedback4;
break;
case 1:
/* PG---S1-+-S3---S4---OUT */
/* PG---S2-+ */
CH->connect1 = &feedback3;
CH->connect2 = &feedback3;
CH->connect3 = &feedback4;
break;
case 2:
/* PG---S1------+-S4---OUT */
/* PG---S2---S3-+ */
CH->connect1 = &feedback4;
CH->connect2 = &feedback3;
CH->connect3 = &feedback4;
break;
case 3:
/* PG---S1---S2-+-S4---OUT */
/* PG---S3------+ */
CH->connect1 = &feedback2;
CH->connect2 = &feedback4;
CH->connect3 = &feedback4;
break;
case 4:
/* PG---S1---S2-+--OUT */
/* PG---S3---S4-+ */
CH->connect1 = &feedback2;
CH->connect2 = carrier;
CH->connect3 = &feedback4;
break;
case 5:
/* +-S2-+ */
/* PG---S1-+-S3-+-OUT */
/* +-S4-+ */
CH->connect1 = 0; /* special mark */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?