/*********************************************************************************** emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001 2001 01-08 : Version 0.10 -- 1st version. 2001 01-15 : Version 0.20 -- semi-public version. 2001 01-16 : Version 0.30 -- 1st public version. 2001 01-17 : Version 0.31 -- Fixed bassdrum problem. : Version 0.32 -- LPF implemented. 2001 01-18 : Version 0.33 -- Fixed the drum problem, refine the mix-down method. -- Fixed the LFO bug. 2001 01-24 : Version 0.35 -- Fixed the drum problem, support undocumented EG behavior. 2001 02-02 : Version 0.38 -- Improved the performance. Fixed the hi-hat and cymbal model. Fixed the default percussive datas. Noise reduction. Fixed the feedback problem. 2001 03-03 : Version 0.39 -- Fixed some drum bugs. Improved the performance. 2001 03-04 : Version 0.40 -- Improved the feedback. Change the default table size. Clock and Rate can be changed during play. 2001 06-24 : Version 0.50 -- Improved the hi-hat and the cymbal tone. Added VRC7 patch (OPLL_reset_patch is changed). Fix OPLL_reset() bug. Added OPLL_setMask, OPLL_getMask and OPLL_toggleMask. Added OPLL_writeIO. References: fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development). s_opl.c -- 2001 written by mamiya (NEZplug development). fmgen.cpp -- 1999,2000 written by cisc. fmpac.ill -- 2000 created by NARUTO. MSX-Datapack YMU757 data sheet YM2143 data sheet **************************************************************************************/ #include #include #include #include #include "emu2413.h" #if defined(_MSC_VER) #define INLINE __inline #elif defined(__GNUC__) #define INLINE __inline__ #else #define INLINE #endif #define OPLL_TONE_NUM 2 static unsigned char default_inst[OPLL_TONE_NUM][(16+3)*16]= { { #include "2413tone.h" }, { #include "vrc7tone.h" } }; /* Size of Sintable ( 1 -- 18 can be used, but 7 -- 14 recommended.)*/ #define PG_BITS 9 #define PG_WIDTH (1<>(b)) /* Leave the lower b bit(s). */ #define LOWBITS(c,b) ((c)&((1<<(b))-1)) /* Expand x which is s bits to d bits. */ #define EXPAND_BITS(x,s,d) ((x)<<((d)-(s))) /* Expand x which is s bits to d bits and fill expanded bits '1' */ #define EXPAND_BITS_X(x,s,d) (((x)<<((d)-(s)))|((1<<((d)-(s)))-1)) /* Adjust envelope speed which depends on sampling rate. */ #define rate_adjust(x) (uint32)((double)(x)*clk/72/rate + 0.5) /* +0.5 to round */ #define MOD(x) ch[x]->mod #define CAR(x) ch[x]->car /* Sampling rate */ static uint32 rate ; /* Input clock */ static uint32 clk ; /* WaveTable for each envelope amp */ static uint32 fullsintable[PG_WIDTH] ; static uint32 halfsintable[PG_WIDTH] ; static uint32 snaretable[PG_WIDTH] ; static int32 noiseAtable[64] = { -1,1,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0, -1,1,0,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0 } ; static int32 noiseBtable[8] = { -1,1,-1,1,0,0,0,0 } ; static uint32 *waveform[5] = {fullsintable,halfsintable,snaretable} ; /* LFO Table */ static int32 pmtable[PM_PG_WIDTH] ; static int32 amtable[AM_PG_WIDTH] ; /* Noise and LFO */ static uint32 pm_dphase ; static uint32 am_dphase ; /* dB to Liner table */ static int32 DB2LIN_TABLE[(DB_MUTE + DB_MUTE)*2] ; /* Liner to Log curve conversion table (for Attack rate). */ static uint32 AR_ADJUST_TABLE[1<=DB_MUTE) DB2LIN_TABLE[i] = 0 ; DB2LIN_TABLE[i+ DB_MUTE + DB_MUTE] = -DB2LIN_TABLE[i] ; } } /* Liner(+0.0 - +1.0) to dB((1<0) noiseAtable[i] = DB_POS(0) ; else if(noiseAtable[i]<0) noiseAtable[i] = DB_NEG(0) ; else noiseAtable[i] = DB_MUTE - 1 ; } for( i = 0 ; i < 8 ; i++ ) { if(noiseBtable[i]>0) noiseBtable[i] = DB_POS(0) ; else if(noiseBtable[i]<0) noiseBtable[i] = DB_NEG(0) ; else noiseBtable[i] = DB_MUTE - 1 ; } } /* Table for Pitch Modulator */ static void makePmTable(void) { int i ; for(i = 0 ; i < PM_PG_WIDTH ; i++ ) pmtable[i] = (int32)((double)PM_AMP * pow(2,(double)PM_DEPTH*sin(2.0*PI*i/PM_PG_WIDTH)/1200)) ; } /* Table for Amp Modulator */ static void makeAmTable(void) { int i ; for(i = 0 ; i < AM_PG_WIDTH ; i++ ) amtable[i] = (int32)((double)AM_DEPTH/2/DB_STEP * (1.0 + sin(2.0*PI*i/PM_PG_WIDTH))) ; } /* Phase increment counter table */ static void makeDphaseTable(void) { uint32 fnum, block , ML ; uint32 mltable[16]={ 1,1*2,2*2,3*2,4*2,5*2,6*2,7*2,8*2,9*2,10*2,10*2,12*2,12*2,15*2,15*2 } ; for(fnum=0; fnum<512; fnum++) for(block=0; block<8; block++) for(ML=0; ML<16; ML++) dphaseTable[fnum][block][ML] = rate_adjust(((fnum * mltable[ML])<>(20-DP_BITS)) ; } static void makeTllTable(void) { #define dB2(x) (uint32)((x)*2) static uint32 kltable[16] = { dB2( 0.000),dB2( 9.000),dB2(12.000),dB2(13.875),dB2(15.000),dB2(16.125),dB2(16.875),dB2(17.625), dB2(18.000),dB2(18.750),dB2(19.125),dB2(19.500),dB2(19.875),dB2(20.250),dB2(20.625),dB2(21.000) } ; int32 tmp ; int fnum, block ,TL , KL ; for(fnum=0; fnum<16; fnum++) for(block=0; block<8; block++) for(TL=0; TL<64; TL++) for(KL=0; KL<4; KL++) { if(KL==0) { tllTable[fnum][block][TL][KL] = TL2EG(TL) ; } else { tmp = kltable[fnum] - dB2(3.000) * (7 - block) ; if(tmp <= 0) tllTable[fnum][block][TL][KL] = TL2EG(TL) ; else tllTable[fnum][block][TL][KL] = (uint32)((tmp>>(3-KL))/EG_STEP) + TL2EG(TL) ; } } } /* Rate Table for Attack */ static void makeDphaseARTable(void) { int AR,Rks,RM,RL ; for(AR=0; AR<16; AR++) for(Rks=0; Rks<16; Rks++) { RM = AR + (Rks>>2) ; if(RM>15) RM = 15 ; RL = Rks&3 ; switch(AR) { case 0: dphaseARTable[AR][Rks] = 0 ; break ; case 15: dphaseARTable[AR][Rks] = EG_DP_WIDTH ; break ; default: dphaseARTable[AR][Rks] = rate_adjust(( 3 * (RL + 4) << (RM + 1))) ; break ; } } } /* Rate Table for Decay */ static void makeDphaseDRTable(void) { int DR,Rks,RM,RL ; for(DR=0; DR<16; DR++) for(Rks=0; Rks<16; Rks++) { RM = DR + (Rks>>2) ; RL = Rks&3 ; if(RM>15) RM = 15 ; switch(DR) { case 0: dphaseDRTable[DR][Rks] = 0 ; break ; default: dphaseDRTable[DR][Rks] = rate_adjust((RL + 4) << (RM - 1)); break ; } } } static void makeRksTable(void) { int fnum8, block, KR ; for(fnum8 = 0 ; fnum8 < 2 ; fnum8++) for(block = 0 ; block < 8 ; block++) for(KR = 0; KR < 2 ; KR++) { if(KR!=0) rksTable[fnum8][block][KR] = ( block << 1 ) + fnum8 ; else rksTable[fnum8][block][KR] = block >> 1 ; } } void dump2patch(unsigned char *dump, OPLL_PATCH *patch) { patch[0].AM = (dump[0]>>7)&1 ; patch[1].AM = (dump[1]>>7)&1 ; patch[0].PM = (dump[0]>>6)&1 ; patch[1].PM = (dump[1]>>6)&1 ; patch[0].EG = (dump[0]>>5)&1 ; patch[1].EG = (dump[1]>>5)&1 ; patch[0].KR = (dump[0]>>4)&1 ; patch[1].KR = (dump[1]>>4)&1 ; patch[0].ML = (dump[0])&15 ; patch[1].ML = (dump[1])&15 ; patch[0].KL = (dump[2]>>6)&3 ; patch[1].KL = (dump[3]>>6)&3 ; patch[0].TL = (dump[2])&63 ; patch[0].FB = (dump[3])&7 ; patch[0].WF = (dump[3]>>3)&1 ; patch[1].WF = (dump[3]>>4)&1 ; patch[0].AR = (dump[4]>>4)&15 ; patch[1].AR = (dump[5]>>4)&15 ; patch[0].DR = (dump[4])&15 ; patch[1].DR = (dump[5])&15 ; patch[0].SL = (dump[6]>>4)&15 ; patch[1].SL = (dump[7]>>4)&15 ; patch[0].RR = (dump[6])&15 ; patch[1].RR = (dump[7])&15 ; } static void makeDefaultPatch() { int i, j ; for(i=0;ieg_mode) { case ATTACK: return dphaseARTable[slot->patch->AR][slot->rks] ; case DECAY: return dphaseDRTable[slot->patch->DR][slot->rks] ; case SUSHOLD: return 0 ; case SUSTINE: return dphaseDRTable[slot->patch->RR][slot->rks] ; case RELEASE: if(slot->sustine) return dphaseDRTable[5][slot->rks] ; else if(slot->patch->EG) return dphaseDRTable[slot->patch->RR][slot->rks] ; else return dphaseDRTable[7][slot->rks] ; case FINISH: return 0 ; default: return 0 ; } } /************************************************************* OPLL internal interfaces *************************************************************/ #define SLOT_BD1 12 #define SLOT_BD2 13 #define SLOT_HH 14 #define SLOT_SD 15 #define SLOT_TOM 16 #define SLOT_CYM 17 #define UPDATE_PG(S) (S)->dphase = dphaseTable[(S)->fnum][(S)->block][(S)->patch->ML] #define UPDATE_TLL(S)\ (((S)->type==0)?\ ((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->patch->TL][(S)->patch->KL]):\ ((S)->tll = tllTable[((S)->fnum)>>5][(S)->block][(S)->volume][(S)->patch->KL])) #define UPDATE_RKS(S) (S)->rks = rksTable[((S)->fnum)>>8][(S)->block][(S)->patch->KR] #define UPDATE_WF(S) (S)->sintbl = waveform[(S)->patch->WF] #define UPDATE_EG(S) (S)->eg_dphase = calc_eg_dphase(S) #define UPDATE_ALL(S)\ UPDATE_PG(S);\ UPDATE_TLL(S);\ UPDATE_RKS(S);\ UPDATE_WF(S); \ UPDATE_EG(S) /* EG should be last */ /* Force Refresh (When external program changes some parameters). */ void OPLL_forceRefresh(OPLL *opll) { int i ; if(opll==NULL) return ; for(i=0; i<18 ;i++) { UPDATE_PG(opll->slot[i]) ; UPDATE_RKS(opll->slot[i]) ; UPDATE_TLL(opll->slot[i]) ; UPDATE_WF(opll->slot[i]) ; UPDATE_EG(opll->slot[i]) ; } } /* Slot key on */ INLINE static void slotOn(OPLL_SLOT *slot) { slot->eg_mode = ATTACK ; slot->phase = 0 ; slot->eg_phase = 0 ; } /* Slot key off */ INLINE static void slotOff(OPLL_SLOT *slot) { if(slot->eg_mode == ATTACK) slot->eg_phase = EXPAND_BITS(AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase,EG_DP_BITS-EG_BITS)],EG_BITS,EG_DP_BITS) ; slot->eg_mode = RELEASE ; } /* Channel key on */ INLINE static void keyOn(OPLL *opll, int i) { if(!opll->slot_on_flag[i*2]) slotOn(opll->MOD(i)) ; if(!opll->slot_on_flag[i*2+1]) slotOn(opll->CAR(i)) ; opll->ch[i]->key_status = 1 ; } /* Channel key off */ INLINE static void keyOff(OPLL *opll, int i) { if(opll->slot_on_flag[i*2+1]) slotOff(opll->CAR(i)) ; opll->ch[i]->key_status = 0 ; } INLINE static void keyOn_BD(OPLL *opll){ keyOn(opll,6) ; } INLINE static void keyOn_SD(OPLL *opll){ if(!opll->slot_on_flag[SLOT_SD]) slotOn(opll->CAR(7)) ; } INLINE static void keyOn_TOM(OPLL *opll){ if(!opll->slot_on_flag[SLOT_TOM]) slotOn(opll->MOD(8)) ; } INLINE static void keyOn_HH(OPLL *opll){ if(!opll->slot_on_flag[SLOT_HH]) slotOn(opll->MOD(7)) ; } INLINE static void keyOn_CYM(OPLL *opll){ if(!opll->slot_on_flag[SLOT_CYM]) slotOn(opll->CAR(8)) ; } /* Drum key off */ INLINE static void keyOff_BD(OPLL *opll){ keyOff(opll,6) ; } INLINE static void keyOff_SD(OPLL *opll){ if(opll->slot_on_flag[SLOT_SD]) slotOff(opll->CAR(7)) ; } INLINE static void keyOff_TOM(OPLL *opll){ if(opll->slot_on_flag[SLOT_TOM]) slotOff(opll->MOD(8)) ; } INLINE static void keyOff_HH(OPLL *opll){ if(opll->slot_on_flag[SLOT_HH]) slotOff(opll->MOD(7)) ; } INLINE static void keyOff_CYM(OPLL *opll){ if(opll->slot_on_flag[SLOT_CYM]) slotOff(opll->CAR(8)) ; } /* Change a voice */ INLINE static void setPatch(OPLL *opll, int i, int num) { opll->ch[i]->patch_number = num ; opll->MOD(i)->patch = opll->patch[num*2+0] ; opll->CAR(i)->patch = opll->patch[num*2+1] ; } /* Change a rythm voice */ INLINE static void setSlotPatch(OPLL_SLOT *slot, OPLL_PATCH *patch) { slot->patch = patch ; } /* Set sustine parameter */ INLINE static void setSustine(OPLL *opll, int c, int sustine) { opll->CAR(c)->sustine = sustine ; if(opll->MOD(c)->type) opll->MOD(c)->sustine = sustine ; } /* Volume : 6bit ( Volume register << 2 ) */ INLINE static void setVolume(OPLL *opll, int c, int volume) { opll->CAR(c)->volume = volume ; } INLINE static void setSlotVolume(OPLL_SLOT *slot, int volume) { slot->volume = volume ; } /* Set F-Number ( fnum : 9bit ) */ INLINE static void setFnumber(OPLL *opll, int c, int fnum) { opll->CAR(c)->fnum = fnum ; opll->MOD(c)->fnum = fnum ; } /* Set Block data (block : 3bit ) */ INLINE static void setBlock(OPLL *opll, int c, int block) { opll->CAR(c)->block = block ; opll->MOD(c)->block = block ; } /* Change Rythm Mode */ INLINE static void setRythmMode(OPLL *opll, int mode) { opll->rythm_mode = mode ; if(mode) { opll->ch[6]->patch_number = 16 ; opll->ch[7]->patch_number = 17 ; opll->ch[8]->patch_number = 18 ; setSlotPatch(opll->slot[SLOT_BD1], opll->patch[16*2+0]) ; setSlotPatch(opll->slot[SLOT_BD2], opll->patch[16*2+1]) ; setSlotPatch(opll->slot[SLOT_HH], opll->patch[17*2+0]) ; setSlotPatch(opll->slot[SLOT_SD], opll->patch[17*2+1]) ; opll->slot[SLOT_HH]->type = 1 ; setSlotPatch(opll->slot[SLOT_TOM], opll->patch[18*2+0]) ; setSlotPatch(opll->slot[SLOT_CYM], opll->patch[18*2+1]) ; opll->slot[SLOT_TOM]->type = 1 ; } else { setPatch(opll, 6, opll->reg[0x36]>>4) ; setPatch(opll, 7, opll->reg[0x37]>>4) ; opll->slot[SLOT_HH]->type = 0 ; setPatch(opll, 8, opll->reg[0x38]>>4) ; opll->slot[SLOT_TOM]->type = 0 ; } if(!opll->slot_on_flag[SLOT_BD1]) opll->slot[SLOT_BD1]->eg_mode = FINISH ; if(!opll->slot_on_flag[SLOT_BD2]) opll->slot[SLOT_BD2]->eg_mode = FINISH ; if(!opll->slot_on_flag[SLOT_HH]) opll->slot[SLOT_HH]->eg_mode = FINISH ; if(!opll->slot_on_flag[SLOT_SD]) opll->slot[SLOT_SD]->eg_mode = FINISH ; if(!opll->slot_on_flag[SLOT_TOM]) opll->slot[SLOT_TOM]->eg_mode = FINISH ; if(!opll->slot_on_flag[SLOT_CYM]) opll->slot[SLOT_CYM]->eg_mode = FINISH ; } void OPLL_copyPatch(OPLL *opll, int num, OPLL_PATCH *patch) { memcpy(opll->patch[num],patch,sizeof(OPLL_PATCH)) ; } /*********************************************************** Initializing ***********************************************************/ static void OPLL_SLOT_reset(OPLL_SLOT *slot) { slot->sintbl = waveform[0] ; slot->phase = 0 ; slot->dphase = 0 ; slot->output[0] = 0 ; slot->output[1] = 0 ; slot->feedback = 0 ; slot->eg_mode = SETTLE ; slot->eg_phase = EG_DP_WIDTH ; slot->eg_dphase = 0 ; slot->rks = 0 ; slot->tll = 0 ; slot->sustine = 0 ; slot->fnum = 0 ; slot->block = 0 ; slot->volume = 0 ; slot->pgout = 0 ; slot->egout = 0 ; slot->patch = &null_patch ; } static OPLL_SLOT *OPLL_SLOT_new(void) { OPLL_SLOT *slot ; slot = malloc(sizeof(OPLL_SLOT)) ; if(slot == NULL) return NULL ; return slot ; } static void OPLL_SLOT_delete(OPLL_SLOT *slot) { free(slot) ; } static void OPLL_CH_reset(OPLL_CH *ch) { if(ch->mod!=NULL) OPLL_SLOT_reset(ch->mod) ; if(ch->car!=NULL) OPLL_SLOT_reset(ch->car) ; ch->key_status = 0 ; } static OPLL_CH *OPLL_CH_new(void) { OPLL_CH *ch ; OPLL_SLOT *mod, *car ; mod = OPLL_SLOT_new() ; if(mod == NULL) return NULL ; car = OPLL_SLOT_new() ; if(car == NULL) { OPLL_SLOT_delete(mod) ; return NULL ; } ch = malloc(sizeof(OPLL_CH)) ; if(ch == NULL) { OPLL_SLOT_delete(mod) ; OPLL_SLOT_delete(car) ; return NULL ; } mod->type = 0 ; car->type = 1 ; ch->mod = mod ; ch->car = car ; return ch ; } static void OPLL_CH_delete(OPLL_CH *ch) { OPLL_SLOT_delete(ch->mod) ; OPLL_SLOT_delete(ch->car) ; free(ch) ; } OPLL *OPLL_new(void) { OPLL *opll ; OPLL_CH *ch[9] ; OPLL_PATCH *patch[19*2] ; int i, j ; for( i = 0 ; i < 19*2 ; i++ ) { patch[i] = calloc(sizeof(OPLL_PATCH),1) ; if(patch[i] == NULL) { for ( j = i ; i > 0 ; i++ ) free(patch[j-1]) ; return NULL ; } } for( i = 0 ; i < 9 ; i++ ) { ch[i] = OPLL_CH_new() ; if(ch[i]==NULL) { for ( j = i ; i > 0 ; i++ ) OPLL_CH_delete(ch[j-1]) ; for ( j = 0 ; j < 19*2 ; j++ ) free(patch[j]) ; return NULL ; } } opll = malloc(sizeof(OPLL)) ; if(opll == NULL) return NULL ; for ( i = 0 ; i < 19*2 ; i++ ) opll->patch[i] = patch[i] ; for ( i = 0 ; i <9 ; i++) { opll->ch[i] = ch[i] ; opll->slot[i*2+0] = opll->ch[i]->mod ; opll->slot[i*2+1] = opll->ch[i]->car ; } for ( i = 0 ; i < 18 ; i++) { opll->slot[i]->plfo_am = &opll->lfo_am ; opll->slot[i]->plfo_pm = &opll->lfo_pm ; } opll->mask = 0 ; OPLL_reset(opll) ; OPLL_reset_patch(opll,0) ; opll->masterVolume = 32 ; return opll ; } void OPLL_delete(OPLL *opll) { int i ; for ( i = 0 ; i < 9 ; i++ ) OPLL_CH_delete(opll->ch[i]) ; for ( i = 0 ; i < 19*2 ; i++ ) free(opll->patch[i]) ; free(opll) ; } /* Reset patch datas by system default. */ void OPLL_reset_patch(OPLL *opll, int type) { int i ; for ( i = 0 ; i < 19*2 ; i++ ) OPLL_copyPatch(opll, i, &default_patch[type%OPLL_TONE_NUM][i]) ; } /* Reset whole of OPLL except patch datas. */ void OPLL_reset(OPLL *opll) { int i ; if(!opll) return ; opll->adr = 0 ; opll->output[0] = 0 ; opll->output[1] = 0 ; opll->pm_phase = 0 ; opll->am_phase = 0 ; opll->noise_seed =0xffff ; opll->noiseA = 0 ; opll->noiseB = 0 ; opll->noiseA_phase = 0 ; opll->noiseB_phase = 0 ; opll->noiseA_dphase = 0 ; opll->noiseB_dphase = 0 ; opll->noiseA_idx = 0 ; opll->noiseB_idx = 0 ; for(i = 0; i < 9 ; i++) { OPLL_CH_reset(opll->ch[i]) ; setPatch(opll,i,0) ; } for ( i = 0 ; i < 0x40 ; i++ ) OPLL_writeReg(opll, i, 0) ; } void OPLL_setClock(uint32 c, uint32 r) { clk = c ; rate = r ; makeDphaseTable() ; makeDphaseARTable() ; makeDphaseDRTable() ; pm_dphase = (uint32)rate_adjust(PM_SPEED * PM_DP_WIDTH / (clk/72) ) ; am_dphase = (uint32)rate_adjust(AM_SPEED * AM_DP_WIDTH / (clk/72) ) ; } void OPLL_init(uint32 c, uint32 r) { makePmTable() ; makeAmTable() ; makeDB2LinTable() ; makeAdjustTable() ; makeTllTable() ; makeRksTable() ; makeSinTable() ; makeDefaultPatch() ; OPLL_setClock(c,r) ; } void OPLL_close(void) { } /********************************************************* Generate wave data *********************************************************/ /* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI). */ #if ( SLOT_AMP_BITS - PG_BITS ) > 0 #define wave2_2pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS )) #else #define wave2_2pi(e) ( (e) << ( PG_BITS - SLOT_AMP_BITS )) #endif /* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */ #if ( SLOT_AMP_BITS - PG_BITS - 1 ) == 0 #define wave2_4pi(e) (e) #elif ( SLOT_AMP_BITS - PG_BITS - 1 ) > 0 #define wave2_4pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 1 )) #else #define wave2_4pi(e) ( (e) << ( 1 + PG_BITS - SLOT_AMP_BITS )) #endif /* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */ #if ( SLOT_AMP_BITS - PG_BITS - 2 ) == 0 #define wave2_8pi(e) (e) #elif ( SLOT_AMP_BITS - PG_BITS - 2 ) > 0 #define wave2_8pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 2 )) #else #define wave2_8pi(e) ( (e) << ( 2 + PG_BITS - SLOT_AMP_BITS )) #endif /* 16bit rand */ INLINE static uint32 mrand(uint32 seed) { return ((seed>>15)^((seed>>12)&1)) | ((seed<<1)&0xffff) ; } INLINE static uint32 DEC(uint32 db) { if(dbnoise_seed = mrand(opll->noise_seed) ; opll->whitenoise = opll->noise_seed & 1 ; opll->noiseA_phase = (opll->noiseA_phase + opll->noiseA_dphase) ; opll->noiseB_phase = (opll->noiseB_phase + opll->noiseB_dphase) ; if(opll->noiseA_phase<(1<<11)) { if(opll->noiseA_phase>16) opll->noiseA = DB_MUTE - 1 ; } else { opll->noiseA_phase &= (1<<11)-1 ; opll->noiseA_idx = (opll->noiseA_idx+1)&63 ; opll->noiseA = noiseAtable[opll->noiseA_idx] ; } if(opll->noiseB_phase<(1<<12)) { if(opll->noiseB_phase>16) opll->noiseB = DB_MUTE - 1 ; } else { opll->noiseB_phase &= (1<<12)-1 ; opll->noiseB_idx = (opll->noiseB_idx+1)&7 ; opll->noiseB = noiseBtable[opll->noiseB_idx] ; } } /* Update AM, PM unit */ INLINE static void update_ampm(OPLL *opll) { opll->pm_phase = (opll->pm_phase + pm_dphase)&(PM_DP_WIDTH - 1) ; opll->am_phase = (opll->am_phase + am_dphase)&(AM_DP_WIDTH - 1) ; opll->lfo_am = amtable[HIGHBITS(opll->am_phase, AM_DP_BITS - AM_PG_BITS)] ; opll->lfo_pm = pmtable[HIGHBITS(opll->pm_phase, PM_DP_BITS - PM_PG_BITS)] ; } /* PG */ INLINE static uint32 calc_phase(OPLL_SLOT *slot) { if(slot->patch->PM) slot->phase += (slot->dphase * (*(slot->plfo_pm))) >> PM_AMP_BITS ; else slot->phase += slot->dphase ; slot->phase &= (DP_WIDTH - 1) ; return HIGHBITS(slot->phase, DP_BASE_BITS) ; } /* EG */ INLINE static uint32 calc_envelope(OPLL_SLOT *slot) { #define S2E(x) (SL2EG((int)(x/SL_STEP))<<(EG_DP_BITS-EG_BITS)) static uint32 SL[16] = { S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21), S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(48) } ; uint32 egout ; switch(slot->eg_mode) { case ATTACK: slot->eg_phase += slot->eg_dphase ; if(EG_DP_WIDTH & slot->eg_phase) { egout = 0 ; slot->eg_phase= 0 ; slot->eg_mode = DECAY ; UPDATE_EG(slot) ; } else { egout = AR_ADJUST_TABLE[HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS)] ; } break; case DECAY: slot->eg_phase += slot->eg_dphase ; egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS) ; if(slot->eg_phase >= SL[slot->patch->SL]) { if(slot->patch->EG) { slot->eg_phase = SL[slot->patch->SL] ; slot->eg_mode = SUSHOLD ; UPDATE_EG(slot) ; } else { slot->eg_phase = SL[slot->patch->SL] ; slot->eg_mode = SUSTINE ; UPDATE_EG(slot) ; } egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS) ; } break; case SUSHOLD: egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS) ; if(slot->patch->EG == 0) { slot->eg_mode = SUSTINE ; UPDATE_EG(slot) ; } break; case SUSTINE: case RELEASE: slot->eg_phase += slot->eg_dphase ; egout = HIGHBITS(slot->eg_phase, EG_DP_BITS - EG_BITS) ; if(egout >= (1<eg_mode = FINISH ; egout = (1<patch->AM) egout = EG2DB(egout+slot->tll) + *(slot->plfo_am) ; else egout = EG2DB(egout+slot->tll) ; if(egout >= DB_MUTE) egout = DB_MUTE-1; return egout ; } INLINE static int32 calc_slot_car(OPLL_SLOT *slot, int32 fm) { slot->egout = calc_envelope(slot) ; slot->pgout = calc_phase(slot) ; if(slot->egout>=(DB_MUTE-1)) return 0 ; return DB2LIN_TABLE[slot->sintbl[(slot->pgout+wave2_8pi(fm))&(PG_WIDTH-1)] + slot->egout] ; } INLINE static int32 calc_slot_mod(OPLL_SLOT *slot) { int32 fm ; slot->output[1] = slot->output[0] ; slot->egout = calc_envelope(slot) ; slot->pgout = calc_phase(slot) ; if(slot->egout>=(DB_MUTE-1)) { slot->output[0] = 0 ; } else if(slot->patch->FB!=0) { fm = wave2_4pi(slot->feedback) >> (7 - slot->patch->FB) ; slot->output[0] = DB2LIN_TABLE[slot->sintbl[(slot->pgout+fm)&(PG_WIDTH-1)] + slot->egout] ; } else { slot->output[0] = DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout] ; } slot->feedback = (slot->output[1] + slot->output[0])>>1 ; return slot->feedback ; } INLINE static int32 calc_slot_tom(OPLL_SLOT *slot) { slot->egout = calc_envelope(slot) ; slot->pgout = calc_phase(slot) ; if(slot->egout>=(DB_MUTE-1)) return 0 ; return DB2LIN_TABLE[slot->sintbl[slot->pgout] + slot->egout] ; } /* calc SNARE slot */ INLINE static int32 calc_slot_snare(OPLL_SLOT *slot, uint32 whitenoise) { slot->egout = calc_envelope(slot) ; slot->pgout = calc_phase(slot) ; if(slot->egout>=(DB_MUTE-1)) return 0 ; if(whitenoise) return DB2LIN_TABLE[snaretable[slot->pgout] + slot->egout] + DB2LIN_TABLE[slot->egout + 6] ; else return DB2LIN_TABLE[snaretable[slot->pgout] + slot->egout] ; } INLINE static int32 calc_slot_cym(OPLL_SLOT *slot, int32 a, int32 b, int32 c) { slot->egout = calc_envelope(slot) ; if(slot->egout>=(DB_MUTE-1)) return 0 ; return DB2LIN_TABLE[slot->egout+a] + (( DB2LIN_TABLE[slot->egout+b] + DB2LIN_TABLE[slot->egout+c] ) >> 2 ); } INLINE static int32 calc_slot_hat(OPLL_SLOT *slot, int32 a, int32 b, int32 c, uint32 whitenoise) { slot->egout = calc_envelope(slot) ; if(slot->egout>=(DB_MUTE-1)) return 0 ; if(whitenoise) { return DB2LIN_TABLE[slot->egout+a] + (( DB2LIN_TABLE[slot->egout+b] + DB2LIN_TABLE[slot->egout+c] ) >> 2 ); } else { return 0 ; } } int16 OPLL_calc(OPLL *opll) { int32 inst = 0 , perc = 0 , out = 0 ; int32 rythmC = 0, rythmH = 0; int i ; update_ampm(opll) ; update_noise(opll) ; for(i = 0 ; i < 6 ; i++) if(!(opll->mask&OPLL_MASK_CH(i))&&(opll->CAR(i)->eg_mode!=FINISH)) inst += calc_slot_car(opll->CAR(i),calc_slot_mod(opll->MOD(i))) ; if(!opll->rythm_mode) { for(i = 6 ; i < 9 ; i++) if(!(opll->mask&OPLL_MASK_CH(i))&&(opll->CAR(i)->eg_mode!=FINISH)) inst += calc_slot_car(opll->CAR(i),calc_slot_mod(opll->MOD(i))) ; } else { opll->MOD(7)->pgout = calc_phase(opll->MOD(7)) ; opll->CAR(8)->pgout = calc_phase(opll->CAR(8)) ; if(opll->MOD(7)->phase<256) rythmH = DB_NEG(12.0) ; else rythmH = DB_MUTE - 1 ; if(opll->CAR(8)->phase<256) rythmC = DB_NEG(12.0) ; else rythmC = DB_MUTE - 1 ; if(!(opll->mask&OPLL_MASK_BD)&&(opll->CAR(6)->eg_mode!=FINISH)) perc += calc_slot_car(opll->CAR(6),calc_slot_mod(opll->MOD(6))) ; if(!(opll->mask&OPLL_MASK_HH)&&(opll->MOD(7)->eg_mode!=FINISH)) perc += calc_slot_hat(opll->MOD(7), opll->noiseA, opll->noiseB, rythmH, opll->whitenoise) ; if(!(opll->mask&OPLL_MASK_SD)&&(opll->CAR(7)->eg_mode!=FINISH)) perc += calc_slot_snare(opll->CAR(7), opll->whitenoise) ; if(!(opll->mask&OPLL_MASK_TOM)&&(opll->MOD(8)->eg_mode!=FINISH)) perc += calc_slot_tom(opll->MOD(8)) ; if(!(opll->mask&OPLL_MASK_CYM)&&(opll->CAR(8)->eg_mode!=FINISH)) perc += calc_slot_cym(opll->CAR(8), opll->noiseA, opll->noiseB, rythmC) ; } #if SLOT_AMP_BITS > 8 inst = (inst >> (SLOT_AMP_BITS - 8)) ; perc = (perc >> (SLOT_AMP_BITS - 9)) ; #else inst = (inst << (8 - SLOT_AMP_BITS)) ; perc = (perc << (9 - SLOT_AMP_BITS)) ; #endif out = ((inst + perc) * opll->masterVolume ) >> 2 ; if(out>32767) return 32767 ; if(out<-32768) return -32768 ; return (int16)out ; } uint32 OPLL_setMask(OPLL *opll, uint32 mask) { uint32 ret ; if(opll) { ret = opll->mask ; opll->mask = mask ; return ret ; } else return 0 ; } uint32 OPLL_toggleMask(OPLL *opll, uint32 mask) { uint32 ret ; if(opll) { ret = opll->mask ; opll->mask ^= mask ; return ret ; } else return 0 ; } /**************************************************** Interfaces *****************************************************/ void OPLL_writeReg(OPLL *opll, uint32 reg, uint32 data){ int i,v,ch ; data = data&0xff ; reg = reg&0x3f ; switch(reg) { case 0x00: opll->patch[0]->AM = (data>>7)&1 ; opll->patch[0]->PM = (data>>6)&1 ; opll->patch[0]->EG = (data>>5)&1 ; opll->patch[0]->KR = (data>>4)&1 ; opll->patch[0]->ML = (data)&15 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_PG(opll->MOD(i)) ; UPDATE_RKS(opll->MOD(i)) ; UPDATE_EG(opll->MOD(i)) ; } } break ; case 0x01: opll->patch[1]->AM = (data>>7)&1 ; opll->patch[1]->PM = (data>>6)&1 ; opll->patch[1]->EG = (data>>5)&1 ; opll->patch[1]->KR = (data>>4)&1 ; opll->patch[1]->ML = (data)&15 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_PG(opll->CAR(i)) ; UPDATE_RKS(opll->CAR(i)) ; UPDATE_EG(opll->CAR(i)) ; } } break; case 0x02: opll->patch[0]->KL = (data>>6)&3 ; opll->patch[0]->TL = (data)&63 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_TLL(opll->MOD(i)) ; } } break ; case 0x03: opll->patch[1]->KL = (data>>6)&3 ; opll->patch[1]->WF = (data>>4)&1 ; opll->patch[0]->WF = (data>>3)&1 ; opll->patch[0]->FB = (data)&7 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_WF(opll->MOD(i)) ; UPDATE_WF(opll->CAR(i)) ; } } break ; case 0x04: opll->patch[0]->AR = (data>>4)&15 ; opll->patch[0]->DR = (data)&15 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_EG(opll->MOD(i)) ; } } break ; case 0x05: opll->patch[1]->AR = (data>>4)&15 ; opll->patch[1]->DR = (data)&15 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_EG(opll->CAR(i)) ; } } break ; case 0x06: opll->patch[0]->SL = (data>>4)&15 ; opll->patch[0]->RR = (data)&15 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_EG(opll->MOD(i)) ; } } break ; case 0x07: opll->patch[1]->SL = (data>>4)&15 ; opll->patch[1]->RR = (data)&15 ; for(i=0;i<9;i++) { if(opll->ch[i]->patch_number==0) { UPDATE_EG(opll->CAR(i)) ; } } break ; case 0x0e: if(opll->rythm_mode) { opll->slot_on_flag[SLOT_BD1] = (opll->reg[0x0e]&0x10) | (opll->reg[0x26]&0x10) ; opll->slot_on_flag[SLOT_BD2] = (opll->reg[0x0e]&0x10) | (opll->reg[0x26]&0x10) ; opll->slot_on_flag[SLOT_SD] = (opll->reg[0x0e]&0x08) | (opll->reg[0x27]&0x10) ; opll->slot_on_flag[SLOT_HH] = (opll->reg[0x0e]&0x01) | (opll->reg[0x27]&0x10) ; opll->slot_on_flag[SLOT_TOM] = (opll->reg[0x0e]&0x04) | (opll->reg[0x28]&0x10) ; opll->slot_on_flag[SLOT_CYM] = (opll->reg[0x0e]&0x02) | (opll->reg[0x28]&0x10) ; } else { opll->slot_on_flag[SLOT_BD1] = (opll->reg[0x26]&0x10) ; opll->slot_on_flag[SLOT_BD2] = (opll->reg[0x26]&0x10) ; opll->slot_on_flag[SLOT_SD] = (opll->reg[0x27]&0x10) ; opll->slot_on_flag[SLOT_HH] = (opll->reg[0x27]&0x10) ; opll->slot_on_flag[SLOT_TOM] = (opll->reg[0x28]&0x10) ; opll->slot_on_flag[SLOT_CYM] = (opll->reg[0x28]&0x10) ; } if(((data>>5)&1)^(opll->rythm_mode)) { setRythmMode(opll,(data&32)>>5) ; } if(opll->rythm_mode) { if(data&0x10) keyOn_BD(opll) ; else keyOff_BD(opll) ; if(data&0x8) keyOn_SD(opll) ; else keyOff_SD(opll) ; if(data&0x4) keyOn_TOM(opll) ; else keyOff_TOM(opll) ; if(data&0x2) keyOn_CYM(opll) ; else keyOff_CYM(opll) ; if(data&0x1) keyOn_HH(opll) ; else keyOff_HH(opll) ; } UPDATE_ALL(opll->MOD(6)) ; UPDATE_ALL(opll->CAR(6)) ; UPDATE_ALL(opll->MOD(7)) ; UPDATE_ALL(opll->CAR(7)) ; UPDATE_ALL(opll->MOD(8)) ; UPDATE_ALL(opll->CAR(8)) ; break ; case 0x0f: break ; case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: ch = reg-0x10 ; setFnumber(opll, ch, data + ((opll->reg[0x20+ch]&1)<<8)) ; UPDATE_ALL(opll->MOD(ch)); UPDATE_ALL(opll->CAR(ch)); switch(reg) { case 0x17: opll->noiseA_dphase = (data + ((opll->reg[0x27]&1)<<8)) << ((opll->reg[0x27]>>1)&7) ; break ; case 0x18: opll->noiseB_dphase = (data + ((opll->reg[0x28]&1)<<8)) << ((opll->reg[0x28]>>1)&7) ; break; default: break ; } break ; case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: ch = reg - 0x20 ; setFnumber(opll, ch, ((data&1)<<8) + opll->reg[0x10+ch]) ; setBlock(opll, ch, (data>>1)&7 ) ; opll->slot_on_flag[ch*2] = opll->slot_on_flag[ch*2+1] = (opll->reg[reg])&0x10 ; if(opll->rythm_mode) { switch(reg) { case 0x26: opll->slot_on_flag[SLOT_BD1] |= (opll->reg[0x0e])&0x10 ; opll->slot_on_flag[SLOT_BD2] |= (opll->reg[0x0e])&0x10 ; break ; case 0x27: opll->noiseA_dphase = (((data&1)<<8) + opll->reg[0x17] ) << ((data>>1)&7) ; opll->slot_on_flag[SLOT_SD] |= (opll->reg[0x0e])&0x08 ; opll->slot_on_flag[SLOT_HH] |= (opll->reg[0x0e])&0x01 ; break; case 0x28: opll->noiseB_dphase = (((data&1)<<8) + opll->reg[0x18] ) << ((data>>1)&7); opll->slot_on_flag[SLOT_TOM] |= (opll->reg[0x0e])&0x04 ; opll->slot_on_flag[SLOT_CYM] |= (opll->reg[0x0e])&0x02 ; break ; default: break ; } } if((opll->reg[reg]^data)&0x20) setSustine(opll, ch, (data>>5)&1) ; if(data&0x10) keyOn(opll, ch) ; else keyOff(opll, ch) ; UPDATE_ALL(opll->MOD(ch)) ; UPDATE_ALL(opll->CAR(ch)) ; break ; case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: i = (data>>4)&15 ; v = data&15 ; if((opll->rythm_mode)&&(reg>=0x36)) { switch(reg) { case 0x37 : setSlotVolume(opll->MOD(7), i<<2) ; break ; case 0x38 : setSlotVolume(opll->MOD(8), i<<2) ; break ; } } else { setPatch(opll, reg-0x30, i) ; } setVolume(opll, reg-0x30, v<<2) ; UPDATE_ALL(opll->MOD(reg-0x30)) ; UPDATE_ALL(opll->CAR(reg-0x30)) ; break ; default: break ; } opll->reg[reg] = (unsigned char)data ; } void OPLL_writeIO(OPLL *opll, uint32 adr, uint32 val) { adr &= 0xff ; if(adr == 0x7C) opll->adr = val ; else if(adr == 0x7D) OPLL_writeReg(opll, opll->adr, val) ; }