📄 fm.c
字号:
#define YM2610B_WARNING/* YM2608 rhythm data is PCM ,not an ADPCM */#define YM2608_RHYTHM_PCM/***** File: fm.c -- software implementation of FM sound generator**** Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmurator development**** Version 0.37***//***** change log. (hiro-shi) ****** 08-12-98:** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA** move ROM limit check.(CALC_CH? -> 2610Write1/2)** test program (ADPCMB_TEST)** move ADPCM A/B end check.** ADPCMB repeat flag(no check)** change ADPCM volume rate (8->16) (32->48).**** 09-12-98:** change ADPCM volume. (8->16, 48->64)** replace ym2610 ch0/3 (YM-2610B)** init cur_chip (restart bug fix)** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff.** add ADPCM_SHIFT_MASK** change ADPCMA_DECODE_MIN/MAX.*//* no check: YM2608 rhythm sound OPN SSG type envelope (SEG) YM2151 CSM speech mode no support: status BUSY flag (everytime not busy) YM2608 status mask (register :0x110) YM2608 RYTHM sound YM2608 PCM memory data access , DELTA-T-ADPCM with PCM port YM2151 CSM speech mode with internal timer preliminary : key scale level rate (?) attack rate time rate , curve decay rate time rate , curve self-feedback algorythm YM2610 ADPCM-A mixing level , decode algorythm YM2151 noise mode (CH7.OP4) LFO contoller (YM2612/YM2610/YM2608/YM2151) note: OPN OPM fnum fM * 2^20 / (fM/(12*n)) TimerOverA ( 12*n)*(1024-NA)/fM 64*(1024-Na)/fM TimerOverB (192*n)*(256-NB)/fM 1024*(256-Nb)/fM output bits 10bit<<3bit 16bit * 2ch (YM3012=10bit<<3bit) sampling rate fFM / (12*priscaler) fM / 64 lfo freq ( fM*2^(LFRQ/16) ) / (4295*10^6)*//************************************************************************//* comment of hiro-shi(Hiromitsu Shioya) *//* YM2610(B) = (OPN-B *//* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch *//* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch *//************************************************************************/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>#include <math.h>#ifndef __RAINE__#include "driver.h" /* use M.A.M.E. */#else#include "deftypes.h" /* use RAINE */#include "mame/driver.h" /* use RAINE */#include "sasound.h"#endif#include "ay8910.h"#include "fm.h"#ifndef PI#define PI 3.14159265358979323846#endif/***** shared function building option ****/#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2612)#define BUILD_OPNB (BUILD_YM2610||BUILD_YM2610B)#define BUILD_FM_ADPCMA (BUILD_YM2608||BUILD_OPNB)#define BUILD_FM_ADPCMB (BUILD_YM2608||BUILD_OPNB)#define BUILD_STEREO (BUILD_YM2608||BUILD_YM2610||BUILD_YM2612||BUILD_YM2151)#define BUILD_LFO (BUILD_YM2608||BUILD_YM2610||BUILD_YM2612||BUILD_YM2151)#if BUILD_FM_ADPCMB/* include external DELTA-T ADPCM unit */#include "ymdeltat.h" /* DELTA-T ADPCM UNIT */#define DELTAT_MIXING_LEVEL (4) /* DELTA-T ADPCM MIXING LEVEL */#endif/* -------------------- sound quality define selection --------------------- *//* sinwave entries *//* used static memory = SIN_ENT * 4 (byte) */#define SIN_ENT 2048/* lower bits of envelope counter */#define ENV_BITS 16/* envelope output entries */#define EG_ENT 4096#define EG_STEP (96.0/EG_ENT) /* OPL == 0.1875 dB */#if FM_LFO_SUPPORT/* LFO table entries */#define LFO_ENT 512#define LFO_SHIFT (32-9)#define LFO_RATE 0x10000#endif/* -------------------- preliminary define section --------------------- *//* attack/decay rate time rate */#define OPM_ARRATE 399128#define OPM_DRRATE 5514396/* It is not checked , because I haven't YM2203 rate */#define OPN_ARRATE OPM_ARRATE#define OPN_DRRATE OPM_DRRATE/* PG output cut off level : 78dB(14bit)? */#define PG_CUT_OFF ((int)(78.0/EG_STEP))/* EG output cut off level : 68dB? */#define EG_CUT_OFF ((int)(68.0/EG_STEP))#define FREQ_BITS 24 /* frequency turn *//* PG counter is 21bits @oct.7 */#define FREQ_RATE (1<<(FREQ_BITS-21))#define TL_BITS (FREQ_BITS+2)/* OPbit = 14(13+sign) : TL_BITS+1(sign) / output = 16bit */#define TL_SHIFT (TL_BITS+1-(14-16))/* output final shift */#define FM_OUTSB (TL_SHIFT-FM_OUTPUT_BIT)#define FM_MAXOUT ((1<<(TL_SHIFT-1))-1)#define FM_MINOUT (-(1<<(TL_SHIFT-1)))/* -------------------- local defines , macros --------------------- *//* envelope counter position */#define EG_AST 0 /* start of Attack phase */#define EG_AED (EG_ENT<<ENV_BITS) /* end of Attack phase */#define EG_DST EG_AED /* start of Decay/Sustain/Release phase */#define EG_DED (EG_DST+(EG_ENT<<ENV_BITS)-1) /* end of Decay/Sustain/Release phase */#define EG_OFF EG_DED /* off */#if FM_SEG_SUPPORT#define EG_UST ((2*EG_ENT)<<ENV_BITS) /* start of SEG UPSISE */#define EG_UED ((3*EG_ENT)<<ENV_BITS) /* end of SEG UPSISE */#endif/* register number to channel number , slot offset */#define OPN_CHAN(N) (N&3)#define OPN_SLOT(N) ((N>>2)&3)#define OPM_CHAN(N) (N&7)#define OPM_SLOT(N) ((N>>3)&3)/* slot number */#define SLOT1 0#define SLOT2 2#define SLOT3 1#define SLOT4 3/* bit0 = Right enable , bit1 = Left enable */#define OUTD_RIGHT 1#define OUTD_LEFT 2#define OUTD_CENTER 3/* FM timer model */#define FM_TIMER_SINGLE (0)#define FM_TIMER_INTERVAL (1)/* ---------- OPN / OPM one channel ---------- */typedef struct fm_slot { INT32 *DT; /* detune :DT_TABLE[DT] */ int DT2; /* multiple,Detune2:(DT2<<4)|ML for OPM*/ int TL; /* total level :TL << 8 */ UINT8 KSR; /* key scale rate :3-KSR */ const INT32 *AR; /* attack rate :&AR_TABLE[AR<<1] */ const INT32 *DR; /* decay rate :&DR_TABLE[DR<<1] */ const INT32 *SR; /* sustin rate :&DR_TABLE[SR<<1] */ int SL; /* sustin level :SL_TABLE[SL] */ const INT32 *RR; /* release rate :&DR_TABLE[RR<<2+2] */ UINT8 SEG; /* SSG EG type :SSGEG */ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ UINT32 mul; /* multiple :ML_TABLE[ML] */ /* Phase Generator */ UINT32 Cnt; /* frequency count : */ UINT32 Incr; /* frequency step : */ /* Envelope Generator */ void (*eg_next)(struct fm_slot *SLOT); /* pointer of phase handler */ INT32 evc; /* envelope counter */ INT32 eve; /* envelope counter end point */ INT32 evs; /* envelope counter step */ INT32 evsa; /* envelope step for Attack */ INT32 evsd; /* envelope step for Decay */ INT32 evss; /* envelope step for Sustain */ INT32 evsr; /* envelope step for Release */ INT32 TLL; /* adjusted TotalLevel */ /* LFO */ UINT8 amon; /* AMS enable flag */ UINT32 ams; /* AMS depth level of this SLOT */}FM_SLOT;typedef struct fm_chan { FM_SLOT SLOT[4]; UINT8 PAN; /* PAN :NONE,LEFT,RIGHT or CENTER */ UINT8 ALGO; /* Algorythm */ UINT8 FB; /* shift count of self feed back */ INT32 op1_out[2]; /* op1 output for beedback */ /* Algorythm (connection) */ INT32 *connect1; /* pointer of SLOT1 output */ INT32 *connect2; /* pointer of SLOT2 output */ INT32 *connect3; /* pointer of SLOT3 output */ INT32 *connect4; /* pointer of SLOT4 output */ /* LFO */ INT32 pms; /* PMS depth level of channel */ UINT32 ams; /* AMS depth level of channel */ /* Phase Generator */ UINT32 fc; /* fnum,blk :adjusted to sampling rate */ UINT8 fn_h; /* freq latch : */ UINT8 kcode; /* key code : */} FM_CH;/* OPN/OPM common state */typedef struct fm_state { UINT8 index; /* chip index (number of chip) */ int clock; /* master clock (Hz) */ int rate; /* sampling rate (Hz) */ double freqbase; /* frequency base */ double TimerBase; /* Timer base time */ UINT8 address; /* address register */ UINT8 irq; /* interrupt level */ UINT8 irqmask; /* irq mask */ UINT8 status; /* status flag */ UINT32 mode; /* mode CSM / 3SLOT */ int TA; /* timer a */ int TAC; /* timer a counter */ UINT8 TB; /* timer b */ int TBC; /* timer b counter */ /* speedup customize */ /* local time tables */ INT32 DT_TABLE[8][32]; /* DeTune tables */ INT32 AR_TABLE[94]; /* Atttack rate tables */ INT32 DR_TABLE[94]; /* Decay rate tables */ /* Extention Timer and IRQ handler */ FM_TIMERHANDLER Timer_Handler; FM_IRQHANDLER IRQ_Handler; /* timer model single / interval */ UINT8 timermodel;}FM_ST;/* -------------------- tables --------------------- *//* 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) (int)((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/* size of TL_TABLE = sinwave(max cut_off) + cut_off(tl + ksr + envelope + ams) */#define TL_MAX (PG_CUT_OFF+EG_CUT_OFF+1)/* 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 INT32 *TL_TABLE;/* pointers to TL_TABLE with sinwave output offset */static INT32 *SIN_TABLE[SIN_ENT];/* envelope output curve table */#if FM_SEG_SUPPORT/* attack + decay + SSG upside + OFF */static INT32 ENV_CURVE[3*EG_ENT+1];#else/* attack + decay + OFF */static INT32 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_DTTABLEstatic UINT8 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(n) (int)(n*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 */ ML(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),/* DT2=1 *SQL(2) */ ML(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),/* DT2=2 *SQL(2.5) */ ML( 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),/* DT2=3 *SQL(3) */ ML( 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)};#undef ML#if FM_LFO_SUPPORT#define PMS_RATE 0x400/* LFO runtime work */static UINT32 lfo_amd;static INT32 lfo_pmd;#endif/* Dummy table of Attack / Decay rate ( use when rate == 0 ) */static const INT32 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 /* two ADPCM unit */#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)/* current chip state */static void *cur_chip = 0; /* pointer of current chip struct */static FM_ST *State; /* basic status */static FM_CH *cch[8]; /* pointer of FM channels */#if (BUILD_LFO)#if FM_LFO_SUPPORTstatic UINT32 LFOCnt,LFOIncr; /* LFO PhaseGenerator */#endif#endif/* runtime work */static INT32 out_ch[4]; /* channel output NONE,LEFT,RIGHT or CENTER */static INT32 pg_in1,pg_in2,pg_in3,pg_in4; /* PG input of SLOTs *//* -------------------- log output -------------------- *//* 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__#define LOG(n,x) if( (n)>=LOG_LEVEL ) logerror x#endif/* ----- limitter ----- */#define Limit(val, max,min) { \ if ( val > max ) val = max; \ else if ( val < min ) val = min; \}/* ----- buffering one of data(STEREO chip) ----- */#if FM_STEREO_MIX/* stereo mixing */#define FM_BUFFERING_STEREO \{ \ /* get left & right output with clipping */ \ out_ch[OUTD_LEFT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_LEFT] , FM_MAXOUT, FM_MINOUT ); \ out_ch[OUTD_RIGHT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_RIGHT], FM_MAXOUT, FM_MINOUT ); \ /* buffering */ \ *bufL++ = out_ch[OUTD_LEFT] >>FM_OUTSB; \ *bufL++ = out_ch[OUTD_RIGHT]>>FM_OUTSB; \}#else/* stereo separate */#define FM_BUFFERING_STEREO \{ \ /* get left & right output with clipping */ \ out_ch[OUTD_LEFT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_LEFT] , FM_MAXOUT, FM_MINOUT ); \ out_ch[OUTD_RIGHT] += out_ch[OUTD_CENTER]; \ Limit( out_ch[OUTD_RIGHT], FM_MAXOUT, FM_MINOUT ); \ /* buffering */ \ bufL[i] = out_ch[OUTD_LEFT] >>FM_OUTSB; \ bufR[i] = out_ch[OUTD_RIGHT]>>FM_OUTSB; \}#endif#if FM_INTERNAL_TIMER/* ----- internal timer mode , update timer *//* ---------- calcrate timer A ---------- */#define INTERNAL_TIMER_A(ST,CSM_CH) \{ \ if( ST->TAC && (ST->Timer_Handler==0) ) \ if( (ST->TAC -= (int)(ST->freqbase*4096)) <= 0 ) \ { \ TimerAOver( ST ); \ /* CSM mode total level latch and auto key on */ \ if( ST->mode & 0x80 ) \ CSMKeyControll( CSM_CH ); \ } \}/* ---------- calcrate timer B ---------- */#define INTERNAL_TIMER_B(ST,step) \{ \ if( ST->TBC && (ST->Timer_Handler==0) ) \ if( (ST->TBC -= (int)(ST->freqbase*4096*step)) <= 0 ) \ TimerBOver( ST ); \}#else /* FM_INTERNAL_TIMER *//* external timer mode */#define INTERNAL_TIMER_A(ST,CSM_CH)#define INTERNAL_TIMER_B(ST,step)#endif /* FM_INTERNAL_TIMER *//* --------------------- subroutines --------------------- *//* 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -