fm.c

来自「DGen源码最后版本」· C语言 代码 · 共 2,003 行 · 第 1/5 页

C
2,003
字号
        }
    }
    /* 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,(double)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 */
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,(double)ST->TAC,ST->TimerBase);
    }
    else ST->TAC = 0;
}
/* Timer B Overflow */
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,(double)ST->TBC,ST->TimerBase);
    }
    else ST->TBC = 0;
}
/* CSM Key Controll */
INLINE void CSMKeyControll(FM_CH *CH)
{
    int ksl = KSL[CH->kcode];
    /* 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 + ksl;
    CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + ksl;
    CH->SLOT[SLOT3].TLL = CH->SLOT[SLOT3].TL + ksl;
    CH->SLOT[SLOT4].TLL = CH->SLOT[SLOT4].TL + ksl;
    /* all key on */
    FM_KEYON(CH,SLOT1);
    FM_KEYON(CH,SLOT2);
    FM_KEYON(CH,SLOT3);
    FM_KEYON(CH,SLOT4);
}

#ifdef INTERNAL_TIMER
/* ---------- calcrate timer A ---------- */
INLINE void CALC_TIMER_A( FM_ST *ST , FM_CH *CSM_CH ){
  if( ST->TAC &&  (ST->Timer_Handler==0) )
    if( (ST->TAC -= ST->freqbase) <= 0 ){
      TimerAOver( ST );
      /* CSM mode key,TL controll */
      if( ST->mode & 0x80 ){    /* CSM mode total level latch and auto key on */
    CSMKeyControll( CSM_CH );
      }
    }
}
/* ---------- calcrate timer B ---------- */
INLINE void CALC_TIMER_B( FM_ST *ST,int step){
  if( ST->TBC && (ST->Timer_Handler==0) )
    if( (ST->TBC -= ST->freqbase*step) <= 0 ){
      TimerBOver( ST );
    }
}
#endif /* INTERNAL_TIMER */

#if BUILD_OPN
/* ---------- priscaler set(and make time tables) ---------- */
void OPNSetPris(FM_OPN *OPN , int pris , int TimerPris, int SSGpris)
{
    int fn;

    /* frequency base */
    OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock * 4096.0 / OPN->ST.rate) / pris : 0;
    /* Timer base time */
    OPN->ST.TimerBase = (OPN->ST.rate) ? 1.0/((double)OPN->ST.clock / (double)TimerPris) : 0;
    /* 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( fn=0 ; fn < 2048 ; fn++ )
    {
        /* it is freq table for octave 7 */
        /* opn freq counter = 20bit */
        OPN->FN_TABLE[fn] = (double)fn * OPN->ST.freqbase / 4096  * FREQ_RATE * (1<<7) / 2;
    }
/*  Log(LOG_INF,"OPN %d set priscaler %d\n",OPN->ST.index,pris);*/
}

/* ---------- write a OPN mode register 0x20-0x2f ---------- */
static void OPNWriteMode(FM_OPN *OPN, int r, int v)
{
    unsigned char c;
    FM_CH *CH;

    switch(r){
    case 0x21:  /* Test */
        break;
    case 0x22:  /* LFO FREQ (YM2608/YM2612) */
        /* 3.98Hz,5.56Hz,6.02Hz,6.37Hz,6.88Hz,9.63Hz,48.1Hz,72.2Hz */
        /* FM2608[n].LFOIncr = FM2608[n].LFO_TABLE[v&0x0f]; */
        break;
    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);
/*      Log(LOG_INF,"OPN %d:%d : KEY %02X\n",n,c,v&0xf0);*/
        break;
    }
}

/* ---------- write a OPN register (0x30-0xff) ---------- */
static void OPNWriteReg(FM_OPN *OPN, int r, int v)
{
    unsigned char 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 ENABLE(YM2612) */
        set_dr(SLOT,v,OPN->ST.DR_TABLE);
        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 */
#ifndef SEG_SUPPORT
        if(v&0x08) Log(LOG_ERR,"OPN %d,%d,%d :SSG-TYPE envelope selected (not supported )\n",OPN->ST.index,c,OPN_SLOT(r));
#endif
        SLOT->SEG = v&0x0f;
        break;
    case 0xa0:
        switch( OPN_SLOT(r) ){
        case 0:     /* 0xa0-0xa2 : FNUM1 */
            {
                unsigned int fn  = (((unsigned int)( (CH->fn_h)&7))<<8) + v;
                unsigned char 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)
            {
                unsigned int fn  = (((unsigned int)(OPN->SL3.fn_h[c]&7))<<8) + v;
                unsigned char 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 - feedback : 0;
                set_algorythm( CH );
            }
            break;
        case 1:     /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2608) */
            if( OPN->type & TYPE_LFOPAN)
            {
                /* b0-2 PMS */
                /* 0,3.4,6.7,10,14,20,40,80(cent) */
                SLOT->pms = (v>>4) & 0x07;
                /* b4-5 AMS */
                /* 0,1.4,5.9,11.8(dB) */
                SLOT->ams = v & 0x03;
                /* PAN */
                CH->PAN = (v>>6)&0x03; /* PAN : b6 = R , b7 = L */
                set_algorythm( CH );
                /* Log(LOG_INF,"OPN %d,%d : PAN %d\n",n,c,CH->PAN);*/
            }
            break;
        }
        break;
    }
}

#endif /* BUILD_OPN */

#if BUILD_YM2203
/*******************************************************************************/
/*      YM2203 local section                                                   */
/*******************************************************************************/
static YM2203 *FM2203=NULL; /* array of YM2203's */

/* ---------- update one of chip ----------- */
void YM2203UpdateOne(int num, void *buffer, int length)
{
    YM2203 *F2203 = &(FM2203[num]);
    FM_OPN *OPN =   &(FM2203[num].OPN);
    int i,ch;
    int data;
    FMSAMPLE *buf = (FMSAMPLE *)buffer;

    State = &F2203->OPN.ST;
    cch[0]   = &F2203->CH[0];
    cch[1]   = &F2203->CH[1];
    cch[2]   = &F2203->CH[2];

    /* frequency counter channel A */
    CALC_FCOUNT( cch[0] );
    /* frequency counter channel B */
    CALC_FCOUNT( cch[1] );
    /* frequency counter channel C */
    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] );

    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 */
        buf[i] = data >> OPN_OUTSB;
#ifdef INTERNAL_TIMER
        /* timer controll */
        CALC_TIMER_A( State , cch[2] );
#endif
    }
#ifdef INTERNAL_TIMER
    CALC_TIMER_B( 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->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);
}
#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,
               FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler)
{
    int i;

    if (FM2203) return (-1);    /* duplicate init. */
    cur_chip = NULL;    /* hiro-shi!! */

    FMNumChips = num;

    /* 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.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_SINGLE;
        /* 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();

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?