/***********************************************************/ /* */ /* YM2612.C : YM2612 emulator */ /* */ /* Almost constantes are taken from the MAME core */ /* */ /* This source is a part of Gens project */ /* Written by Stéphane Dallongeville (gens@consolemul.com) */ /* Copyright (c) 2002 by Stéphane Dallongeville */ /* */ /***********************************************************/ /***********************************************************/ /* */ /* Modified by Maxim, Blargg */ /* - removed non-sound-related functionality */ /* - added high-pass PCM filter */ /* - added per-channel muting control */ /* - made it use a context struct to allow multiple */ /* instances */ /* */ /***********************************************************/ #include // for malloc #include #include #include // for memset() #include "mamedef.h" // for correct INLINE macro #include "ym2612.h" /******************************************** * Partie définition * ********************************************/ #define YM_DEBUG_LEVEL 0 #ifndef PI #define PI 3.14159265358979323846 #endif #define ATTACK 0 #define DECAY 1 #define SUBSTAIN 2 #define RELEASE 3 // SIN_LBITS <= 16 // LFO_HBITS <= 16 // (SIN_LBITS + SIN_HBITS) <= 26 // (ENV_LBITS + ENV_HBITS) <= 28 // (LFO_LBITS + LFO_HBITS) <= 28 #define SIN_HBITS 12 // Sinus phase counter int part #define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) #if(SIN_LBITS > 16) #define SIN_LBITS 16 // Can't be greater than 16 bits #endif #define ENV_HBITS 12 // Env phase counter int part #define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) #define LFO_HBITS 10 // LFO phase counter int part #define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) #define SIN_LENGHT (1 << SIN_HBITS) #define ENV_LENGHT (1 << ENV_HBITS) #define LFO_LENGHT (1 << LFO_HBITS) #define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO #define SIN_MASK (SIN_LENGHT - 1) #define ENV_MASK (ENV_LENGHT - 1) #define LFO_MASK (LFO_LENGHT - 1) #define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB #define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) #define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) #define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) #define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 #define MAX_OUT ((1 << MAX_OUT_BITS) - 1) //Just for tests stuff... // //#define COEF_MOD 0.5 //#define MAX_OUT ((int) (((1 << MAX_OUT_BITS) - 1) * COEF_MOD)) #define OUT_BITS (OUTPUT_BITS - 2) #define OUT_SHIFT (MAX_OUT_BITS - OUT_BITS) #define LIMIT_CH_OUT ((int) (((1 << OUT_BITS) * 1.5) - 1)) #define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) #define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) #define AR_RATE 399128 #define DR_RATE 5514396 //#define AR_RATE 426136 // good rate ? //#define DR_RATE (AR_RATE * 12) #define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) #define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) #define S0 0 // Stupid typo of the YM2612 #define S1 2 #define S2 1 #define S3 3 /******************************************** * Partie variables * ********************************************/ //struct ym2612__ YM2612; int *SIN_TAB[SIN_LENGHT]; // SINUS TABLE (pointer on TL TABLE) int TL_TAB[TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) unsigned int ENV_TAB[2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) //unsigned int ATTACK_TO_DECAY[ENV_LENGHT]; // Conversion from attack to decay phase unsigned int DECAY_TO_ATTACK[ENV_LENGHT]; // Conversion from decay to attack phase unsigned int FINC_TAB[2048]; // Frequency step table unsigned int AR_TAB[128]; // Attack rate table unsigned int DR_TAB[96]; // Decay rate table unsigned int DT_TAB[8][32]; // Detune table unsigned int SL_TAB[16]; // Substain level table unsigned int NULL_RATE[32]; // Table for NULL rate int LFO_ENV_TAB[LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) int LFO_FREQ_TAB[LFO_LENGHT]; // LFO FMS TABLE // int INTER_TAB[MAX_UPDATE_LENGHT]; // Interpolation table int LFO_INC_TAB[8]; // LFO step table void (* const UPDATE_CHAN[8 * 8])(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) = // Update Channel functions pointer table { Update_Chan_Algo0, Update_Chan_Algo1, Update_Chan_Algo2, Update_Chan_Algo3, Update_Chan_Algo4, Update_Chan_Algo5, Update_Chan_Algo6, Update_Chan_Algo7, Update_Chan_Algo0_LFO, Update_Chan_Algo1_LFO, Update_Chan_Algo2_LFO, Update_Chan_Algo3_LFO, Update_Chan_Algo4_LFO, Update_Chan_Algo5_LFO, Update_Chan_Algo6_LFO, Update_Chan_Algo7_LFO, Update_Chan_Algo0_Int, Update_Chan_Algo1_Int, Update_Chan_Algo2_Int, Update_Chan_Algo3_Int, Update_Chan_Algo4_Int, Update_Chan_Algo5_Int, Update_Chan_Algo6_Int, Update_Chan_Algo7_Int, Update_Chan_Algo0_LFO_Int, Update_Chan_Algo1_LFO_Int, Update_Chan_Algo2_LFO_Int, Update_Chan_Algo3_LFO_Int, Update_Chan_Algo4_LFO_Int, Update_Chan_Algo5_LFO_Int, Update_Chan_Algo6_LFO_Int, Update_Chan_Algo7_LFO_Int }; void (* const ENV_NEXT_EVENT[8])(slot_ *SL) = // Next Enveloppe phase functions pointer table { Env_Attack_Next, Env_Decay_Next, Env_Substain_Next, Env_Release_Next, Env_NULL_Next, Env_NULL_Next, Env_NULL_Next, Env_NULL_Next }; const unsigned int DT_DEF_TAB[4 * 32] = { // 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 }; const unsigned int FKEY_TAB[16] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 }; const unsigned int LFO_AMS_TAB[4] = { 31, 4, 1, 0 }; const unsigned int LFO_FMS_TAB[8] = { LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 }; int int_cnt; // Interpolation calculation #if YM_DEBUG_LEVEL > 0 // Debug FILE *debug_file = NULL; #endif /* Gens */ //extern unsigned int Sound_Extrapol[312][2]; //extern int Seg_L[882], Seg_R[882]; //extern int VDP_Current_Line; //extern int GYM_Dumping; //extern int YM2612_Enable; //extern int DAC_Enable=1; //int Update_GYM_Dump(char v0, char v1, char v2); int YM2612_Enable; int YM2612_Improv; //int DAC_Enable = 1; int *YM_Buf[2]; int YM_Len = 0; int YM2612_Enable_SSGEG = 1; // enable SSG-EG envelope (causes inacurate sound sometimes - rodrigo) int DAC_Highpass_Enable = 1; // sometimes it creates a terrible noise /* end */ /*********************************************** * fonctions calcul param * ***********************************************/ INLINE void CALC_FINC_SL(slot_ *SL, int finc, int kc) { int ksr; SL->Finc = (finc + SL->DT[kc]) * SL->MUL; ksr = kc >> SL->KSR_S; // keycode atténuation #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "FINC = %d SL->Finc = %d\n", finc, SL->Finc); #endif if (SL->KSR != ksr) // si le KSR a changé alors { // les différents taux pour l'enveloppe sont mis à jour SL->KSR = ksr; SL->EincA = SL->AR[ksr]; SL->EincD = SL->DR[ksr]; SL->EincS = SL->SR[ksr]; SL->EincR = SL->RR[ksr]; if(SL->Ecurp == ATTACK) SL->Einc = SL->EincA; else if(SL->Ecurp == DECAY) SL->Einc = SL->EincD; else if(SL->Ecnt < ENV_END) { if(SL->Ecurp == SUBSTAIN) SL->Einc = SL->EincS; else if(SL->Ecurp == RELEASE) SL->Einc = SL->EincR; } #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "KSR = %.4X EincA = %.8X EincD = %.8X EincS = %.8X EincR = %.8X\n", ksr, SL->EincA, SL->EincD, SL->EincS, SL->EincR); #endif } } INLINE void CALC_FINC_CH(channel_ *CH) { int finc, kc; finc = FINC_TAB[CH->FNUM[0]] >> (7 - CH->FOCT[0]); kc = CH->KC[0]; CALC_FINC_SL(&CH->SLOT[0], finc, kc); CALC_FINC_SL(&CH->SLOT[1], finc, kc); CALC_FINC_SL(&CH->SLOT[2], finc, kc); CALC_FINC_SL(&CH->SLOT[3], finc, kc); } /*********************************************** * fonctions setting * ***********************************************/ INLINE void KEY_ON(channel_ *CH, int nsl) { slot_ *SL = &(CH->SLOT[nsl]); // on recupère le bon pointeur de slot if(SL->Ecurp == RELEASE) // la touche est-elle relâchée ? { SL->Fcnt = 0; // Fix Ecco 2 splash sound SL->Ecnt = (DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; SL->ChgEnM = 0xFFFFFFFF; // SL->Ecnt = DECAY_TO_ATTACK[ENV_TAB[SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; // SL->Ecnt = 0; SL->Einc = SL->EincA; SL->Ecmp = ENV_DECAY; SL->Ecurp = ATTACK; } } INLINE void KEY_OFF(channel_ *CH, int nsl) { slot_ *SL = &(CH->SLOT[nsl]); // on recupère le bon pointeur de slot if(SL->Ecurp != RELEASE) // la touche est-elle appuyée ? { if(SL->Ecnt < ENV_DECAY) // attack phase ? { SL->Ecnt = (ENV_TAB[SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; } SL->Einc = SL->EincR; SL->Ecmp = ENV_END; SL->Ecurp = RELEASE; } } INLINE void CSM_Key_Control(ym2612_ *YM2612) { KEY_ON(&YM2612->CHANNEL[2], 0); KEY_ON(&YM2612->CHANNEL[2], 1); KEY_ON(&YM2612->CHANNEL[2], 2); KEY_ON(&YM2612->CHANNEL[2], 3); } int SLOT_SET(ym2612_ *YM2612, int Adr, unsigned char data) { channel_ *CH; slot_ *SL; int nch, nsl; if((nch = Adr & 3) == 3) return 1; nsl = (Adr >> 2) & 3; if(Adr & 0x100) nch += 3; CH = &(YM2612->CHANNEL[nch]); SL = &(CH->SLOT[nsl]); switch(Adr & 0xF0) { case 0x30: if((SL->MUL = (data & 0x0F))) SL->MUL <<= 1; else SL->MUL = 1; SL->DT = (int*) DT_TAB[(data >> 4) & 7]; CH->SLOT[0].Finc = -1; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] DTMUL = %.2X\n", nch, nsl, data & 0x7F); #endif break; case 0x40: SL->TL = data & 0x7F; // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... YM2612_Special_Update(YM2612); #if((ENV_HBITS - 7) < 0) SL->TLL = SL->TL >> (7 - ENV_HBITS); #else SL->TLL = SL->TL << (ENV_HBITS - 7); #endif #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] TL = %.2X\n", nch, nsl, SL->TL); #endif break; case 0x50: SL->KSR_S = 3 - (data >> 6); CH->SLOT[0].Finc = -1; if(data &= 0x1F) SL->AR = (int*) &AR_TAB[data << 1]; else SL->AR = (int*) &NULL_RATE[0]; SL->EincA = SL->AR[SL->KSR]; if(SL->Ecurp == ATTACK) SL->Einc = SL->EincA; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] AR = %.2X EincA = %.6X\n", nch, nsl, data, SL->EincA); #endif break; case 0x60: if((SL->AMSon = (data & 0x80))) SL->AMS = CH->AMS; else SL->AMS = 31; if((data &= 0x1F)) SL->DR = (int*) &DR_TAB[data << 1]; else SL->DR = (int*) &NULL_RATE[0]; SL->EincD = SL->DR[SL->KSR]; if(SL->Ecurp == DECAY) SL->Einc = SL->EincD; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] AMS = %d DR = %.2X EincD = %.6X\n", nch, nsl, SL->AMSon, data, SL->EincD); #endif break; case 0x70: if((data &= 0x1F)) SL->SR = (int*) &DR_TAB[data << 1]; else SL->SR = (int*) &NULL_RATE[0]; SL->EincS = SL->SR[SL->KSR]; if((SL->Ecurp == SUBSTAIN) && (SL->Ecnt < ENV_END)) SL->Einc = SL->EincS; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SR = %.2X EincS = %.6X\n", nch, nsl, data, SL->EincS); #endif break; case 0x80: SL->SLL = SL_TAB[data >> 4]; SL->RR = (int*) &DR_TAB[((data & 0xF) << 2) + 2]; SL->EincR = SL->RR[SL->KSR]; if((SL->Ecurp == RELEASE) && (SL->Ecnt < ENV_END)) SL->Einc = SL->EincR; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SL = %.8X\n", nch, nsl, SL->SLL); fprintf(debug_file, "CHANNEL[%d], SLOT[%d] RR = %.2X EincR = %.2X\n", nch, nsl, ((data & 0xF) << 1) | 2, SL->EincR); #endif break; case 0x90: // SSG-EG envelope shapes : // // E At Al H // // 1 0 0 0 \\\\ // // 1 0 0 1 \___ // // 1 0 1 0 \/\/ // ___ // 1 0 1 1 \ // // 1 1 0 0 //// // ___ // 1 1 0 1 / // // 1 1 1 0 /\/\ // // 1 1 1 1 /___ // // E = SSG-EG enable // At = Start negate // Al = Altern // H = Hold if(YM2612_Enable_SSGEG) { if(data & 0x08) SL->SEG = data & 0x0F; else SL->SEG = 0; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d], SLOT[%d] SSG-EG = %.2X\n", nch, nsl, data); #endif } break; } return 0; } int CHANNEL_SET(ym2612_ *YM2612, int Adr, unsigned char data) { channel_ *CH; int num; if((num = Adr & 3) == 3) return 1; switch(Adr & 0xFC) { case 0xA0: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); YM2612_Special_Update(YM2612); CH->FNUM[0] = (CH->FNUM[0] & 0x700) + data; CH->KC[0] = (CH->FOCT[0] << 2) | FKEY_TAB[CH->FNUM[0] >> 7]; CH->SLOT[0].Finc = -1; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d] part1 FNUM = %d KC = %d\n", num, CH->FNUM[0], CH->KC[0]); #endif break; case 0xA4: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); YM2612_Special_Update(YM2612); CH->FNUM[0] = (CH->FNUM[0] & 0x0FF) + ((int) (data & 0x07) << 8); CH->FOCT[0] = (data & 0x38) >> 3; CH->KC[0] = (CH->FOCT[0] << 2) | FKEY_TAB[CH->FNUM[0] >> 7]; CH->SLOT[0].Finc = -1; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d] part2 FNUM = %d FOCT = %d KC = %d\n", num, CH->FNUM[0], CH->FOCT[0], CH->KC[0]); #endif break; case 0xA8: if(Adr < 0x100) { num++; YM2612_Special_Update(YM2612); YM2612->CHANNEL[2].FNUM[num] = (YM2612->CHANNEL[2].FNUM[num] & 0x700) + data; YM2612->CHANNEL[2].KC[num] = (YM2612->CHANNEL[2].FOCT[num] << 2) | FKEY_TAB[YM2612->CHANNEL[2].FNUM[num] >> 7]; YM2612->CHANNEL[2].SLOT[0].Finc = -1; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[2] part1 FNUM[%d] = %d KC[%d] = %d\n", num, YM2612->CHANNEL[2].FNUM[num], num, YM2612->CHANNEL[2].KC[num]); #endif } break; case 0xAC: if(Adr < 0x100) { num++; YM2612_Special_Update(YM2612); YM2612->CHANNEL[2].FNUM[num] = (YM2612->CHANNEL[2].FNUM[num] & 0x0FF) + ((int) (data & 0x07) << 8); YM2612->CHANNEL[2].FOCT[num] = (data & 0x38) >> 3; YM2612->CHANNEL[2].KC[num] = (YM2612->CHANNEL[2].FOCT[num] << 2) | FKEY_TAB[YM2612->CHANNEL[2].FNUM[num] >> 7]; YM2612->CHANNEL[2].SLOT[0].Finc = -1; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[2] part2 FNUM[%d] = %d FOCT[%d] = %d KC[%d] = %d\n", num, YM2612->CHANNEL[2].FNUM[num], num, YM2612->CHANNEL[2].FOCT[num], num, YM2612->CHANNEL[2].KC[num]); #endif } break; case 0xB0: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); if(CH->ALGO != (data & 7)) { // Fix VectorMan 2 heli sound (level 1) YM2612_Special_Update(YM2612); CH->ALGO = data & 7; CH->SLOT[0].ChgEnM = 0; CH->SLOT[1].ChgEnM = 0; CH->SLOT[2].ChgEnM = 0; CH->SLOT[3].ChgEnM = 0; } CH->FB = 9 - ((data >> 3) & 7); // Real thing ? // if(CH->FB = ((data >> 3) & 7)) CH->FB = 9 - CH->FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... // else CH->FB = 31; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "CHANNEL[%d] ALGO = %d FB = %d\n", num, CH->ALGO, CH->FB); #endif break; case 0xB4: if(Adr & 0x100) num += 3; CH = &(YM2612->CHANNEL[num]); YM2612_Special_Update(YM2612); if(data & 0x80) CH->LEFT = 0xFFFFFFFF; else CH->LEFT = 0; if(data & 0x40) CH->RIGHT = 0xFFFFFFFF; else CH->RIGHT = 0; CH->AMS = LFO_AMS_TAB[(data >> 4) & 3]; CH->FMS = LFO_FMS_TAB[data & 7]; if(CH->SLOT[0].AMSon) CH->SLOT[0].AMS = CH->AMS; else CH->SLOT[0].AMS = 31; if(CH->SLOT[1].AMSon) CH->SLOT[1].AMS = CH->AMS; else CH->SLOT[1].AMS = 31; if(CH->SLOT[2].AMSon) CH->SLOT[2].AMS = CH->AMS; else CH->SLOT[2].AMS = 31; if(CH->SLOT[3].AMSon) CH->SLOT[3].AMS = CH->AMS; else CH->SLOT[3].AMS = 31; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "CHANNEL[%d] AMS = %d FMS = %d\n", num, CH->AMS, CH->FMS); #endif break; } return 0; } int YM_SET(ym2612_ *YM2612, int Adr, unsigned char data) { channel_ *CH; int nch; switch(Adr) { case 0x22: if(data & 8) { // Cool Spot music 1, LFO modified severals time which // distord the sound, have to check that on a real genesis... YM2612->LFOinc = LFO_INC_TAB[data & 7]; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\nLFO Enable, LFOinc = %.8X %d\n", YM2612->LFOinc, data & 7); #endif } else { YM2612->LFOinc = YM2612->LFOcnt = 0; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\nLFO Disable\n"); #endif } break; case 0x24: YM2612->TimerA = (YM2612->TimerA & 0x003) | (((int) data) << 2); if(YM2612->TimerAL != (1024 - YM2612->TimerA) << 12) { YM2612->TimerAcnt = YM2612->TimerAL = (1024 - YM2612->TimerA) << 12; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "Timer A Set = %.8X\n", YM2612->TimerAcnt); #endif } break; case 0x25: YM2612->TimerA = (YM2612->TimerA & 0x3fc) | (data & 3); if(YM2612->TimerAL != (1024 - YM2612->TimerA) << 12) { YM2612->TimerAcnt = YM2612->TimerAL = (1024 - YM2612->TimerA) << 12; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "Timer A Set = %.8X\n", YM2612->TimerAcnt); #endif } break; case 0x26: YM2612->TimerB = data; if(YM2612->TimerBL != (256 - YM2612->TimerB) << (4 + 12)) { YM2612->TimerBcnt = YM2612->TimerBL = (256 - YM2612->TimerB) << (4 + 12); #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "Timer B Set = %.8X\n", YM2612->TimerBcnt); #endif } break; case 0x27: // Paramètre divers // b7 = CSM MODE // b6 = 3 slot mode // b5 = reset b // b4 = reset a // b3 = timer enable b // b2 = timer enable a // b1 = load b // b0 = load a if((data ^ YM2612->Mode) & 0x40) { // We changed the channel 2 mode, so recalculate phase step // This fix the punch sound in Street of Rage 2 YM2612_Special_Update(YM2612); YM2612->CHANNEL[2].SLOT[0].Finc = -1; // recalculate phase step } // if((data & 2) && (YM2612->Status & 2)) YM2612->TimerBcnt = YM2612->TimerBL; // if((data & 1) && (YM2612->Status & 1)) YM2612->TimerAcnt = YM2612->TimerAL; // YM2612->Status &= (~data >> 4); // Reset du Status au cas ou c'est demand? YM2612->Status &= (~data >> 4) & (data >> 2); // Reset Status YM2612->Mode = data; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "Mode reg = %.2X\n", data); #endif break; case 0x28: if((nch = data & 3) == 3) return 1; if(data & 4) nch += 3; CH = &(YM2612->CHANNEL[nch]); YM2612_Special_Update(YM2612); if(data & 0x10) KEY_ON(CH, S0); // On appuie sur la touche pour le slot 1 else KEY_OFF(CH, S0); // On relâche la touche pour le slot 1 if(data & 0x20) KEY_ON(CH, S1); // On appuie sur la touche pour le slot 3 else KEY_OFF(CH, S1); // On relâche la touche pour le slot 3 if(data & 0x40) KEY_ON(CH, S2); // On appuie sur la touche pour le slot 2 else KEY_OFF(CH, S2); // On relâche la touche pour le slot 2 if(data & 0x80) KEY_ON(CH, S3); // On appuie sur la touche pour le slot 4 else KEY_OFF(CH, S3); // On relâche la touche pour le slot 4 #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "CHANNEL[%d] KEY %.1X\n", nch, ((data & 0xf0) >> 4)); #endif break; case 0x2A: YM2612->DACdata = ((int) data - 0x80) << DAC_SHIFT; // donnée du DAC break; case 0x2B: if(YM2612->DAC ^ (data & 0x80)) YM2612_Special_Update(YM2612); YM2612->DAC = data & 0x80; // activation/désactivation du DAC break; } return 0; } /*********************************************** * fonctions de génération * ***********************************************/ void Env_NULL_Next(slot_ *SL) { } void Env_Attack_Next(slot_ *SL) { // Verified with Gynoug even in HQ (explode SFX) SL->Ecnt = ENV_DECAY; SL->Einc = SL->EincD; SL->Ecmp = SL->SLL; SL->Ecurp = DECAY; } void Env_Decay_Next(slot_ *SL) { // Verified with Gynoug even in HQ (explode SFX) SL->Ecnt = SL->SLL; SL->Einc = SL->EincS; SL->Ecmp = ENV_END; SL->Ecurp = SUBSTAIN; } void Env_Substain_Next(slot_ *SL) { if(YM2612_Enable_SSGEG) { if(SL->SEG & 8) // SSG envelope type { if(SL->SEG & 1) { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } else { // re KEY ON // SL->Fcnt = 0; // SL->ChgEnM = 0xFFFFFFFF; SL->Ecnt = 0; SL->Einc = SL->EincA; SL->Ecmp = ENV_DECAY; SL->Ecurp = ATTACK; } SL->SEG ^= (SL->SEG & 2) << 1; } else { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } } else { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } } void Env_Release_Next(slot_ *SL) { SL->Ecnt = ENV_END; SL->Einc = 0; SL->Ecmp = ENV_END + 1; } #define GET_CURRENT_PHASE \ YM2612->in0 = CH->SLOT[S0].Fcnt; \ YM2612->in1 = CH->SLOT[S1].Fcnt; \ YM2612->in2 = CH->SLOT[S2].Fcnt; \ YM2612->in3 = CH->SLOT[S3].Fcnt; #define UPDATE_PHASE \ CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc; \ CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc; \ CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc; \ CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc; #define UPDATE_PHASE_LFO \ if((freq_LFO = (CH->FMS * YM2612->LFO_FREQ_UP[i]) >> (LFO_HBITS - 1))) \ { \ CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc + ((CH->SLOT[S0].Finc * freq_LFO) >> LFO_FMS_LBITS); \ CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc + ((CH->SLOT[S1].Finc * freq_LFO) >> LFO_FMS_LBITS); \ CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc + ((CH->SLOT[S2].Finc * freq_LFO) >> LFO_FMS_LBITS); \ CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc + ((CH->SLOT[S3].Finc * freq_LFO) >> LFO_FMS_LBITS); \ } \ else \ { \ CH->SLOT[S0].Fcnt += CH->SLOT[S0].Finc; \ CH->SLOT[S1].Fcnt += CH->SLOT[S1].Finc; \ CH->SLOT[S2].Fcnt += CH->SLOT[S2].Finc; \ CH->SLOT[S3].Fcnt += CH->SLOT[S3].Finc; \ } #define GET_CURRENT_ENV \ if(CH->SLOT[S0].SEG & 4) \ { \ if((YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL) > ENV_MASK) YM2612->en0 = 0; \ else YM2612->en0 ^= ENV_MASK; \ } \ else YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL; \ if(CH->SLOT[S1].SEG & 4) \ { \ if((YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL) > ENV_MASK) YM2612->en1 = 0; \ else YM2612->en1 ^= ENV_MASK; \ } \ else YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL; \ if(CH->SLOT[S2].SEG & 4) \ { \ if((YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL) > ENV_MASK) YM2612->en2 = 0; \ else YM2612->en2 ^= ENV_MASK; \ } \ else YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL; \ if(CH->SLOT[S3].SEG & 4) \ { \ if((YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL) > ENV_MASK) YM2612->en3 = 0; \ else YM2612->en3 ^= ENV_MASK; \ } \ else YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL; #define GET_CURRENT_ENV_LFO \ env_LFO = YM2612->LFO_ENV_UP[i]; \ \ if(CH->SLOT[S0].SEG & 4) \ { \ if((YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL) > ENV_MASK) YM2612->en0 = 0; \ else YM2612->en0 = (YM2612->en0 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S0].AMS); \ } \ else YM2612->en0 = ENV_TAB[(CH->SLOT[S0].Ecnt >> ENV_LBITS)] + CH->SLOT[S0].TLL + (env_LFO >> CH->SLOT[S0].AMS); \ if(CH->SLOT[S1].SEG & 4) \ { \ if((YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL) > ENV_MASK) YM2612->en1 = 0; \ else YM2612->en1 = (YM2612->en1 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S1].AMS); \ } \ else YM2612->en1 = ENV_TAB[(CH->SLOT[S1].Ecnt >> ENV_LBITS)] + CH->SLOT[S1].TLL + (env_LFO >> CH->SLOT[S1].AMS); \ if(CH->SLOT[S2].SEG & 4) \ { \ if((YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL) > ENV_MASK) YM2612->en2 = 0; \ else YM2612->en2 = (YM2612->en2 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S2].AMS); \ } \ else YM2612->en2 = ENV_TAB[(CH->SLOT[S2].Ecnt >> ENV_LBITS)] + CH->SLOT[S2].TLL + (env_LFO >> CH->SLOT[S2].AMS); \ if(CH->SLOT[S3].SEG & 4) \ { \ if((YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL) > ENV_MASK) YM2612->en3 = 0; \ else YM2612->en3 = (YM2612->en3 ^ ENV_MASK) + (env_LFO >> CH->SLOT[S3].AMS); \ } \ else YM2612->en3 = ENV_TAB[(CH->SLOT[S3].Ecnt >> ENV_LBITS)] + CH->SLOT[S3].TLL + (env_LFO >> CH->SLOT[S3].AMS); #define UPDATE_ENV \ if((CH->SLOT[S0].Ecnt += CH->SLOT[S0].Einc) >= CH->SLOT[S0].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S0].Ecurp](&(CH->SLOT[S0])); \ if((CH->SLOT[S1].Ecnt += CH->SLOT[S1].Einc) >= CH->SLOT[S1].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S1].Ecurp](&(CH->SLOT[S1])); \ if((CH->SLOT[S2].Ecnt += CH->SLOT[S2].Einc) >= CH->SLOT[S2].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S2].Ecurp](&(CH->SLOT[S2])); \ if((CH->SLOT[S3].Ecnt += CH->SLOT[S3].Einc) >= CH->SLOT[S3].Ecmp) \ ENV_NEXT_EVENT[CH->SLOT[S3].Ecurp](&(CH->SLOT[S3])); #define DO_LIMIT \ if(CH->OUTd > LIMIT_CH_OUT) CH->OUTd = LIMIT_CH_OUT; \ else if(CH->OUTd < -LIMIT_CH_OUT) CH->OUTd = -LIMIT_CH_OUT; #define DO_FEEDBACK0 \ YM2612->in0 += CH->S0_OUT[0] >> CH->FB; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_FEEDBACK \ YM2612->in0 += (CH->S0_OUT[0] + CH->S0_OUT[1]) >> CH->FB; \ CH->S0_OUT[1] = CH->S0_OUT[0]; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_FEEDBACK2 \ YM2612->in0 += (CH->S0_OUT[0] + (CH->S0_OUT[0] >> 2) + CH->S0_OUT[1]) >> CH->FB; \ CH->S0_OUT[1] = CH->S0_OUT[0] >> 2; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_FEEDBACK3 \ YM2612->in0 += (CH->S0_OUT[0] + CH->S0_OUT[1] + CH->S0_OUT[2] + CH->S0_OUT[3]) >> CH->FB; \ CH->S0_OUT[3] = CH->S0_OUT[2] >> 1; \ CH->S0_OUT[2] = CH->S0_OUT[1] >> 1; \ CH->S0_OUT[1] = CH->S0_OUT[0] >> 1; \ CH->S0_OUT[0] = SIN_TAB[(YM2612->in0 >> SIN_LBITS) & SIN_MASK][YM2612->en0]; #define DO_ALGO_0 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in2 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_1 \ DO_FEEDBACK \ YM2612->in2 += CH->S0_OUT[1] + SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_2 \ DO_FEEDBACK \ YM2612->in2 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]; \ YM2612->in3 += CH->S0_OUT[1] + SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_3 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in3 += SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = (SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3]) >> OUT_SHIFT; #define DO_ALGO_4 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in3 += SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]; \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1]) >> OUT_SHIFT; \ DO_LIMIT #define DO_ALGO_5 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ YM2612->in2 += CH->S0_OUT[1]; \ YM2612->in3 += CH->S0_OUT[1]; \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]) >> OUT_SHIFT; \ DO_LIMIT #define DO_ALGO_6 \ DO_FEEDBACK \ YM2612->in1 += CH->S0_OUT[1]; \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2]) >> OUT_SHIFT; \ DO_LIMIT #define DO_ALGO_7 \ DO_FEEDBACK \ CH->OUTd = ((int) SIN_TAB[(YM2612->in3 >> SIN_LBITS) & SIN_MASK][YM2612->en3] + (int) SIN_TAB[(YM2612->in1 >> SIN_LBITS) & SIN_MASK][YM2612->en1] + (int) SIN_TAB[(YM2612->in2 >> SIN_LBITS) & SIN_MASK][YM2612->en2] + CH->S0_OUT[1]) >> OUT_SHIFT; \ DO_LIMIT #define DO_OUTPUT \ buf[0][i] += CH->OUTd & CH->LEFT; \ buf[1][i] += CH->OUTd & CH->RIGHT; #define DO_OUTPUT_INT0 \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ buf[0][i] += CH->OUTd & CH->LEFT; \ buf[1][i] += CH->OUTd & CH->RIGHT; \ } \ else i--; #define DO_OUTPUT_INT1 \ CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ buf[0][i] += CH->Old_OUTd & CH->LEFT; \ buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ } \ else i--; #define DO_OUTPUT_INT2 \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ CH->Old_OUTd = (CH->OUTd + CH->Old_OUTd) >> 1; \ buf[0][i] += CH->Old_OUTd & CH->LEFT; \ buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ } \ else i--; \ CH->Old_OUTd = CH->OUTd; #define DO_OUTPUT_INT \ if((int_cnt += YM2612->Inter_Step) & 0x04000) \ { \ int_cnt &= 0x3FFF; \ CH->Old_OUTd = (((int_cnt ^ 0x3FFF) * CH->OUTd) + (int_cnt * CH->Old_OUTd)) >> 14; \ buf[0][i] += CH->Old_OUTd & CH->LEFT; \ buf[1][i] += CH->Old_OUTd & CH->RIGHT; \ } \ else i--; \ CH->Old_OUTd = CH->OUTd; void Update_Chan_Algo0(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_0 DO_OUTPUT } } void Update_Chan_Algo1(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_1 DO_OUTPUT } } void Update_Chan_Algo2(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_2 DO_OUTPUT } } void Update_Chan_Algo3(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_3 DO_OUTPUT } } void Update_Chan_Algo4(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_4 DO_OUTPUT } } void Update_Chan_Algo5(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_5 DO_OUTPUT } } void Update_Chan_Algo6(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_6 DO_OUTPUT } } void Update_Chan_Algo7(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_7 DO_OUTPUT } } void Update_Chan_Algo0_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_0 DO_OUTPUT } } void Update_Chan_Algo1_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_1 DO_OUTPUT } } void Update_Chan_Algo2_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_2 DO_OUTPUT } } void Update_Chan_Algo3_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_3 DO_OUTPUT } } void Update_Chan_Algo4_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_4 DO_OUTPUT } } void Update_Chan_Algo5_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_5 DO_OUTPUT } } void Update_Chan_Algo6_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_6 DO_OUTPUT } } void Update_Chan_Algo7_LFO(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 LFO len = %d\n\n", lenght); #endif for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_7 DO_OUTPUT } } /****************************************************** * Interpolated output * *****************************************************/ void Update_Chan_Algo0_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_0 DO_OUTPUT_INT } } void Update_Chan_Algo1_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_1 DO_OUTPUT_INT } } void Update_Chan_Algo2_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_2 DO_OUTPUT_INT } } void Update_Chan_Algo3_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_3 DO_OUTPUT_INT } } void Update_Chan_Algo4_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_4 DO_OUTPUT_INT } } void Update_Chan_Algo5_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_5 DO_OUTPUT_INT } } void Update_Chan_Algo6_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_6 DO_OUTPUT_INT } } void Update_Chan_Algo7_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE GET_CURRENT_ENV UPDATE_ENV DO_ALGO_7 DO_OUTPUT_INT } } void Update_Chan_Algo0_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 0 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_0 DO_OUTPUT_INT } } void Update_Chan_Algo1_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 1 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_1 DO_OUTPUT_INT } } void Update_Chan_Algo2_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 2 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_2 DO_OUTPUT_INT } } void Update_Chan_Algo3_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if(CH->SLOT[S3].Ecnt == ENV_END) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 3 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_3 DO_OUTPUT_INT } } void Update_Chan_Algo4_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 4 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_4 DO_OUTPUT_INT } } void Update_Chan_Algo5_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 5 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_5 DO_OUTPUT_INT } } void Update_Chan_Algo6_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 6 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_6 DO_OUTPUT_INT } } void Update_Chan_Algo7_LFO_Int(ym2612_ *YM2612, channel_ *CH, int **buf, int lenght) { int i, env_LFO, freq_LFO; if((CH->SLOT[S0].Ecnt == ENV_END) && (CH->SLOT[S1].Ecnt == ENV_END) && (CH->SLOT[S2].Ecnt == ENV_END) && (CH->SLOT[S3].Ecnt == ENV_END)) return; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nAlgo 7 LFO len = %d\n\n", lenght); #endif int_cnt = YM2612->Inter_Cnt; for(i = 0; i < lenght; i++) { GET_CURRENT_PHASE UPDATE_PHASE_LFO GET_CURRENT_ENV_LFO UPDATE_ENV DO_ALGO_7 DO_OUTPUT_INT } } /*********************************************** * fonctions publiques * ***********************************************/ // Initialisation de l'émulateur YM2612 ym2612_ *YM2612_Init(int Clock, int Rate, int Interpolation) { ym2612_ *YM2612; int i, j; double x; if((Rate == 0) || (Clock == 0)) return NULL; YM2612 = (ym2612_ *)malloc(sizeof(ym2612_)); memset(YM2612, 0, sizeof(ym2612_)); #if YM_DEBUG_LEVEL > 0 if(debug_file == NULL) { debug_file = fopen("ym2612.log", "w"); fprintf(debug_file, "YM2612 logging :\n\n"); } #endif YM2612->Clock = Clock; YM2612->Rate = Rate; // 144 = 12 * (prescale * 2) = 12 * 6 * 2 // prescale set to 6 by default YM2612->Frequence = ((double) YM2612->Clock / (double) YM2612->Rate) / 144.0; YM2612->TimerBase = (int) (YM2612->Frequence * 4096.0); if((Interpolation) && (YM2612->Frequence > 1.0)) { YM2612->Inter_Step = (unsigned int) ((1.0 / YM2612->Frequence) * (double) (0x4000)); YM2612->Inter_Cnt = 0; // We recalculate rate and frequence after interpolation YM2612->Rate = YM2612->Clock / 144; YM2612->Frequence = 1.0; } else { YM2612->Inter_Step = 0x4000; YM2612->Inter_Cnt = 0; } #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "YM2612 frequence = %g rate = %d interp step = %.8X\n\n", YM2612->Frequence, YM2612->Rate, YM2612->Inter_Step); #endif // Tableau TL : // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) for(i = 0; i < TL_LENGHT; i++) { if(i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) { TL_TAB[TL_LENGHT + i] = TL_TAB[i] = 0; } else { x = MAX_OUT; // Max output x /= pow(10, (ENV_STEP * i) / 20); // Decibel -> Voltage TL_TAB[i] = (int) x; TL_TAB[TL_LENGHT + i] = -TL_TAB[i]; } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "TL_TAB[%d] = %.8X TL_TAB[%d] = %.8X\n", i, TL_TAB[i], TL_LENGHT + i, TL_TAB[TL_LENGHT + i]); #endif } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "\n\n\n\n"); #endif // Tableau SIN : // SIN_TAB[x][y] = sin(x) * y; // x = phase and y = volume SIN_TAB[0] = SIN_TAB[SIN_LENGHT / 2] = &TL_TAB[(int)PG_CUT_OFF]; for(i = 1; i <= SIN_LENGHT / 4; i++) { x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus x = 20 * log10(1 / x); // convert to dB j = (int) (x / ENV_STEP); // Get TL range if(j > PG_CUT_OFF) j = (int) PG_CUT_OFF; SIN_TAB[i] = SIN_TAB[(SIN_LENGHT / 2) - i] = &TL_TAB[j]; SIN_TAB[(SIN_LENGHT / 2) + i] = SIN_TAB[SIN_LENGHT - i] = &TL_TAB[TL_LENGHT + j]; #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X SIN[%d][0] = %.8X\n", i, SIN_TAB[i][0], (SIN_LENGHT / 2) - i, SIN_TAB[(SIN_LENGHT / 2) - i][0], (SIN_LENGHT / 2) + i, SIN_TAB[(SIN_LENGHT / 2) + i][0], SIN_LENGHT - i, SIN_TAB[SIN_LENGHT - i][0]); #endif } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "\n\n\n\n"); #endif // Tableau LFO (LFO wav) : for(i = 0; i < LFO_LENGHT; i++) { x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus x += 1.0; x /= 2.0; // positive only x *= 11.8 / ENV_STEP; // ajusted to MAX enveloppe modulation LFO_ENV_TAB[i] = (int) x; x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus x *= (double) ((1 << (LFO_HBITS - 1)) - 1); LFO_FREQ_TAB[i] = (int) x; #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "LFO[%d] = %.8X\n", i, LFO_ENV_TAB[i]); #endif } #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "\n\n\n\n"); #endif // Tableau Enveloppe : // ENV_TAB[0] -> ENV_TAB[ENV_LENGHT - 1] = attack curve // ENV_TAB[ENV_LENGHT] -> ENV_TAB[2 * ENV_LENGHT - 1] = decay curve for(i = 0; i < ENV_LENGHT; i++) { // Attack curve (x^8 - music level 2 Vectorman 2) x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); x *= ENV_LENGHT; ENV_TAB[i] = (int) x; // Decay curve (just linear) x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); x *= ENV_LENGHT; ENV_TAB[ENV_LENGHT + i] = (int) x; #if YM_DEBUG_LEVEL > 2 fprintf(debug_file, "ATTACK[%d] = %d DECAY[%d] = %d\n", i, ENV_TAB[i], i, ENV_TAB[ENV_LENGHT + i]); #endif } ENV_TAB[ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state // Tableau pour la conversion Attack -> Decay and Decay -> Attack for(i = 0, j = ENV_LENGHT - 1; i < ENV_LENGHT; i++) { while (j && (ENV_TAB[j] < (unsigned) i)) j--; DECAY_TO_ATTACK[i] = j << ENV_LBITS; } // Tableau pour le Substain Level for(i = 0; i < 15; i++) { x = i * 3; // 3 and not 6 (Mickey Mania first music for test) x /= ENV_STEP; j = (int) x; j <<= ENV_LBITS; SL_TAB[i] = j + ENV_DECAY; } j = ENV_LENGHT - 1; // special case : volume off j <<= ENV_LBITS; SL_TAB[15] = j + ENV_DECAY; // Tableau Frequency Step for(i = 0; i < 2048; i++) { x = (double) (i) * YM2612->Frequence; #if((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0) x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)); #else x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))); #endif x /= 2.0; // because MUL = value * 2 FINC_TAB[i] = (unsigned int) x; } // Tableaux Attack & Decay Rate for(i = 0; i < 4; i++) { AR_TAB[i] = 0; DR_TAB[i] = 0; } for(i = 0; i < 60; i++) { x = YM2612->Frequence; x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) x *= (double) (ENV_LENGHT << ENV_LBITS); // on ajuste pour le tableau ENV_TAB AR_TAB[i + 4] = (unsigned int) (x / AR_RATE); DR_TAB[i + 4] = (unsigned int) (x / DR_RATE); } for(i = 64; i < 96; i++) { AR_TAB[i] = AR_TAB[63]; DR_TAB[i] = DR_TAB[63]; NULL_RATE[i - 64] = 0; } // Tableau Detune for(i = 0; i < 4; i++) { for (j = 0; j < 32; j++) { #if((SIN_LBITS + SIN_HBITS - 21) < 0) x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612->Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); #else x = (double) DT_DEF_TAB[(i << 5) + j] * YM2612->Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); #endif DT_TAB[i + 0][j] = (int) x; DT_TAB[i + 4][j] = (int) -x; } } // Tableau LFO j = (YM2612->Rate * YM2612->Inter_Step) / 0x4000; LFO_INC_TAB[0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); LFO_INC_TAB[7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / j); YM2612_Reset(YM2612); return YM2612; } int YM2612_End(ym2612_ *YM2612) { free(YM2612); #if YM_DEBUG_LEVEL > 0 if(debug_file) fclose(debug_file); debug_file = NULL; #endif return 0; } int YM2612_Reset(ym2612_ *YM2612) { int i, j; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\n\nStarting reseting YM2612 ...\n\n"); #endif YM2612->LFOcnt = 0; YM2612->TimerA = 0; YM2612->TimerAL = 0; YM2612->TimerAcnt = 0; YM2612->TimerB = 0; YM2612->TimerBL = 0; YM2612->TimerBcnt = 0; YM2612->DAC = 0; YM2612->DACdata = 0; YM2612->dac_highpass = 0; YM2612->Status = 0; YM2612->OPNAadr = 0; YM2612->OPNBadr = 0; YM2612->Inter_Cnt = 0; for(i = 0; i < 6; i++) { YM2612->CHANNEL[i].Old_OUTd = 0; YM2612->CHANNEL[i].OUTd = 0; YM2612->CHANNEL[i].LEFT = 0xFFFFFFFF; YM2612->CHANNEL[i].RIGHT = 0xFFFFFFFF; YM2612->CHANNEL[i].ALGO = 0;; YM2612->CHANNEL[i].FB = 31; YM2612->CHANNEL[i].FMS = 0; YM2612->CHANNEL[i].AMS = 0; for(j = 0 ;j < 4 ; j++) { YM2612->CHANNEL[i].S0_OUT[j] = 0; YM2612->CHANNEL[i].FNUM[j] = 0; YM2612->CHANNEL[i].FOCT[j] = 0; YM2612->CHANNEL[i].KC[j] = 0; YM2612->CHANNEL[i].SLOT[j].Fcnt = 0; YM2612->CHANNEL[i].SLOT[j].Finc = 0; YM2612->CHANNEL[i].SLOT[j].Ecnt = ENV_END; // Put it at the end of Decay phase... YM2612->CHANNEL[i].SLOT[j].Einc = 0; YM2612->CHANNEL[i].SLOT[j].Ecmp = 0; YM2612->CHANNEL[i].SLOT[j].Ecurp = RELEASE; YM2612->CHANNEL[i].SLOT[j].ChgEnM = 0; } } for(i = 0; i < 0x100; i++) { YM2612->REG[0][i] = -1; YM2612->REG[1][i] = -1; } for(i = 0xB6; i >= 0xB4; i--) { YM2612_Write(YM2612, 0, (unsigned char) i); YM2612_Write(YM2612, 2, (unsigned char) i); YM2612_Write(YM2612, 1, 0xC0); YM2612_Write(YM2612, 3, 0xC0); } for(i = 0xB2; i >= 0x22; i--) { YM2612_Write(YM2612, 0, (unsigned char) i); YM2612_Write(YM2612, 2, (unsigned char) i); YM2612_Write(YM2612, 1, 0); YM2612_Write(YM2612, 3, 0); } YM2612_Write(YM2612, 0, 0x2A); YM2612_Write(YM2612, 1, 0x80); #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "\n\nFinishing reseting YM2612 ...\n\n"); #endif return 0; } int YM2612_Read(ym2612_ *YM2612) { /* static int cnt = 0; if(cnt++ == 50) { cnt = 0; return YM2612->Status; } else return YM2612->Status | 0x80; */ return YM2612->Status; } int YM2612_Write(ym2612_ *YM2612, unsigned char adr, unsigned char data) { int d; data &= 0xFF; adr &= 0x03; switch(adr) { case 0: YM2612->OPNAadr = data; break; case 1: // Trivial optimisation if(YM2612->OPNAadr == 0x2A) { YM2612->DACdata = ((int)data - 0x80) << DAC_SHIFT; return 0; } d = YM2612->OPNAadr & 0xF0; if(d >= 0x30) { if(YM2612->REG[0][YM2612->OPNAadr] == data) return 2; YM2612->REG[0][YM2612->OPNAadr] = data; // if (GYM_Dumping) Update_GYM_Dump(1, YM2612->OPNAadr, data); if(d < 0xA0) // SLOT { SLOT_SET(YM2612, YM2612->OPNAadr, data); } else // CHANNEL { CHANNEL_SET(YM2612, YM2612->OPNAadr, data); } } else // YM2612 { YM2612->REG[0][YM2612->OPNAadr] = data; // if ((GYM_Dumping) && ((YM2612->OPNAadr == 0x22) || (YM2612->OPNAadr == 0x27) || (YM2612->OPNAadr == 0x28))) Update_GYM_Dump(1, YM2612->OPNAadr, data); YM_SET(YM2612, YM2612->OPNAadr, data); } break; case 2: YM2612->OPNBadr = data; break; case 3: d = YM2612->OPNBadr & 0xF0; if(d >= 0x30) { if(YM2612->REG[1][YM2612->OPNBadr] == data) return 2; YM2612->REG[1][YM2612->OPNBadr] = data; // if (GYM_Dumping) Update_GYM_Dump(2, YM2612->OPNBadr, data); if(d < 0xA0) // SLOT { SLOT_SET(YM2612, YM2612->OPNBadr + 0x100, data); } else // CHANNEL { CHANNEL_SET(YM2612, YM2612->OPNBadr + 0x100, data); } } else return 1; break; } return 0; } int YM2612_GetMute(ym2612_ *YM2612) { int i, result = 0; for (i = 0; i < 6; ++i) { result |= YM2612->CHANNEL[i].Mute << i; } result |= YM2612->DAC_Mute << 6; //result &= !(YM2612_Enable_SSGEG); return result; } void YM2612_SetMute(ym2612_ *YM2612, int val) { int i; for (i = 0; i < 6; ++i) { YM2612->CHANNEL[i].Mute = (val >> i) & 1; } YM2612->DAC_Mute = (val >> 6) & 1; //YM2612_Enable_SSGEG = !(val & 1); } void YM2612_SetOptions(int Flags) { DAC_Highpass_Enable = (Flags >> 0) & 0x01; YM2612_Enable_SSGEG = (Flags >> 1) & 0x01; } void YM2612_ClearBuffer(int **buffer, int length) { // the MAME core does this before updating, // but the Gens core does this before mixing int *bufL, *bufR; int i; bufL = buffer[0]; bufR = buffer[1]; for(i = 0; i < length; i++) { bufL[i] = 0x0000; bufR[i] = 0x0000; } } void YM2612_Update(ym2612_ *YM2612, int **buf, int length) { int i, j, algo_type; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nStarting generating sound...\n\n"); #endif // Mise ?jour des pas des compteurs-fréquences s'ils ont ét?modifiés if(YM2612->CHANNEL[0].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[0]); if(YM2612->CHANNEL[1].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[1]); if(YM2612->CHANNEL[2].SLOT[0].Finc == -1) { if(YM2612->Mode & 0x40) { CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S0]), FINC_TAB[YM2612->CHANNEL[2].FNUM[2]] >> (7 - YM2612->CHANNEL[2].FOCT[2]), YM2612->CHANNEL[2].KC[2]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S1]), FINC_TAB[YM2612->CHANNEL[2].FNUM[3]] >> (7 - YM2612->CHANNEL[2].FOCT[3]), YM2612->CHANNEL[2].KC[3]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S2]), FINC_TAB[YM2612->CHANNEL[2].FNUM[1]] >> (7 - YM2612->CHANNEL[2].FOCT[1]), YM2612->CHANNEL[2].KC[1]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[S3]), FINC_TAB[YM2612->CHANNEL[2].FNUM[0]] >> (7 - YM2612->CHANNEL[2].FOCT[0]), YM2612->CHANNEL[2].KC[0]); } else { CALC_FINC_CH(&YM2612->CHANNEL[2]); } } if(YM2612->CHANNEL[3].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[3]); if(YM2612->CHANNEL[4].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[4]); if(YM2612->CHANNEL[5].SLOT[0].Finc == -1) CALC_FINC_CH(&YM2612->CHANNEL[5]); /* CALC_FINC_CH(&YM2612->CHANNEL[0]); CALC_FINC_CH(&YM2612->CHANNEL[1]); if(YM2612->Mode & 0x40) { CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[0]), FINC_TAB[YM2612->CHANNEL[2].FNUM[2]] >> (7 - YM2612->CHANNEL[2].FOCT[2]), YM2612->CHANNEL[2].KC[2]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[1]), FINC_TAB[YM2612->CHANNEL[2].FNUM[3]] >> (7 - YM2612->CHANNEL[2].FOCT[3]), YM2612->CHANNEL[2].KC[3]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[2]), FINC_TAB[YM2612->CHANNEL[2].FNUM[1]] >> (7 - YM2612->CHANNEL[2].FOCT[1]), YM2612->CHANNEL[2].KC[1]); CALC_FINC_SL(&(YM2612->CHANNEL[2].SLOT[3]), FINC_TAB[YM2612->CHANNEL[2].FNUM[0]] >> (7 - YM2612->CHANNEL[2].FOCT[0]), YM2612->CHANNEL[2].KC[0]); } else { CALC_FINC_CH(&YM2612->CHANNEL[2]); } CALC_FINC_CH(&YM2612->CHANNEL[3]); CALC_FINC_CH(&YM2612->CHANNEL[4]); CALC_FINC_CH(&YM2612->CHANNEL[5]); */ if(YM2612->Inter_Step & 0x04000) algo_type = 0; else algo_type = 16; if(YM2612->LFOinc) { // Precalcul LFO wav for(i = 0; i < length; i++) { j = ((YM2612->LFOcnt += YM2612->LFOinc) >> LFO_LBITS) & LFO_MASK; YM2612->LFO_ENV_UP[i] = LFO_ENV_TAB[j]; YM2612->LFO_FREQ_UP[i] = LFO_FREQ_TAB[j]; #if YM_DEBUG_LEVEL > 3 fprintf(debug_file, "LFO_ENV_UP[%d] = %d LFO_FREQ_UP[%d] = %d\n", i, YM2612->LFO_ENV_UP[i], i, YM2612->LFO_FREQ_UP[i]); #endif } algo_type |= 8; } if (!YM2612->CHANNEL[0].Mute) UPDATE_CHAN[YM2612->CHANNEL[0].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[0]), buf, length); if (!YM2612->CHANNEL[1].Mute) UPDATE_CHAN[YM2612->CHANNEL[1].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[1]), buf, length); if (!YM2612->CHANNEL[2].Mute) UPDATE_CHAN[YM2612->CHANNEL[2].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[2]), buf, length); if (!YM2612->CHANNEL[3].Mute) UPDATE_CHAN[YM2612->CHANNEL[3].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[3]), buf, length); if (!YM2612->CHANNEL[4].Mute) UPDATE_CHAN[YM2612->CHANNEL[4].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[4]), buf, length); if (!YM2612->CHANNEL[5].Mute && !(YM2612->DAC)) UPDATE_CHAN[YM2612->CHANNEL[5].ALGO + algo_type](YM2612, &(YM2612->CHANNEL[5]), buf, length); YM2612->Inter_Cnt = int_cnt; #if YM_DEBUG_LEVEL > 1 fprintf(debug_file, "\n\nFinishing generating sound...\n\n"); #endif } /*int YM2612_Save(ym2612_ *YM2612, unsigned char SAVE[0x200]) { int i; for(i = 0; i < 0x100; i++) { SAVE[0x000 + i] = YM2612->REG[0][i]; SAVE[0x100 + i] = YM2612->REG[1][i]; } return 0; } int YM2612_Restore(ym2612_ *YM2612, unsigned char SAVE[0x200]) { int i; YM2612_Reset(YM2612); for(i = 0; i < 0x100; i++) { YM2612_Write(YM2612, 0, (unsigned char) i); YM2612_Write(YM2612, 1, SAVE[0x000 + i]); YM2612_Write(YM2612, 2, (unsigned char) i); YM2612_Write(YM2612, 3, SAVE[0x100 + i]); } return 0; }*/ /* Gens */ enum { highpass_fract = 15 }; enum { highpass_shift = 9 }; // higher values reduce highpass on DAC void YM2612_DacAndTimers_Update(ym2612_ *YM2612, int **buffer, int length) { int *bufL, *bufR; int i; if(YM2612->DAC && YM2612->DACdata && ! YM2612->DAC_Mute) { bufL = buffer[0]; bufR = buffer[1]; for(i = 0; i < length; i++) { long dac = (YM2612->DACdata << highpass_fract) - YM2612->dac_highpass; if (DAC_Highpass_Enable) // else it's left at 0 and doesn't affect the sound YM2612->dac_highpass += dac >> highpass_shift; dac >>= highpass_fract; bufL[i] += dac & YM2612->CHANNEL[5].LEFT; bufR[i] += dac & YM2612->CHANNEL[5].RIGHT; } } i = YM2612->TimerBase * length; if(YM2612->Mode & 1) // Timer A ON ? { // if((YM2612->TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) if((YM2612->TimerAcnt -= i) <= 0) { YM2612->Status |= (YM2612->Mode & 0x04) >> 2; YM2612->TimerAcnt += YM2612->TimerAL; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "Counter A overflow\n"); #endif if(YM2612->Mode & 0x80) CSM_Key_Control(YM2612); } } if(YM2612->Mode & 2) // Timer B ON ? { // if((YM2612->TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) if((YM2612->TimerBcnt -= i) <= 0) { YM2612->Status |= (YM2612->Mode & 0x08) >> 2; YM2612->TimerBcnt += YM2612->TimerBL; #if YM_DEBUG_LEVEL > 0 fprintf(debug_file, "Counter B overflow\n"); #endif } } } /* Gens */ void YM2612_Special_Update(ym2612_ *YM2612) { /* if (YM_Len && YM2612_Enable) { YM2612_Update(YM_Buf, YM_Len); YM_Buf[0] = Seg_L + Sound_Extrapol[VDP_Current_Line + 1][0]; YM_Buf[1] = Seg_R + Sound_Extrapol[VDP_Current_Line + 1][0]; YM_Len = 0; }*/ } /* end */