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 + -
显示快捷键?