mirror of
https://github.com/DrBeef/Raze.git
synced 2024-12-16 15:40:58 +00:00
718112a8fe
Currently none of these is being used, but eventually they will, once more code gets ported over. So it's better to have them right away and avoid editing the project file too much, only to revert that later.
1319 lines
35 KiB
C++
1319 lines
35 KiB
C++
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
|
|
|
|
// Based on Gens 2.10 ym2612.c
|
|
|
|
#include "Ym2612_GENS.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
|
|
/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */
|
|
/* Copyright (C) 2004-2006 Shay Green. This module is free software; you
|
|
can redistribute it and/or modify it under the terms of the GNU Lesser
|
|
General Public License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version. This
|
|
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
details. You should have received a copy of the GNU Lesser General Public
|
|
License along with this module; if not, write to the Free Software Foundation,
|
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
|
|
// This is mostly the original source in its C style and all.
|
|
//
|
|
// Somewhat optimized and simplified. Uses a template to generate the many
|
|
// variants of Update_Chan. Rewrote header file. In need of full rewrite by
|
|
// someone more familiar with FM sound and the YM2612. Has some inaccuracies
|
|
// compared to the Sega Genesis sound, particularly being mixed at such a
|
|
// high sample accuracy (the Genesis sounds like it has only 8 bit samples).
|
|
// - Shay
|
|
|
|
#ifdef BLARGG_ENABLE_OPTIMIZER
|
|
#include BLARGG_ENABLE_OPTIMIZER
|
|
#endif
|
|
|
|
const int output_bits = 14;
|
|
|
|
struct slot_t
|
|
{
|
|
const int *DT; // parametre detune
|
|
int MUL; // parametre "multiple de frequence"
|
|
int TL; // Total Level = volume lorsque l'enveloppe est au plus haut
|
|
int TLL; // Total Level ajusted
|
|
int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression
|
|
int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe
|
|
int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer
|
|
// sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite !
|
|
int SEG; // Type enveloppe SSG
|
|
int env_xor;
|
|
int env_max;
|
|
|
|
const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR])
|
|
const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR[KSR])
|
|
const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR])
|
|
const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR])
|
|
int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16])
|
|
int Finc; // frequency step = pas d'incrementation du compteur-frequence
|
|
// plus le pas est grand, plus la frequence est aïgu (ou haute)
|
|
int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase
|
|
// de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ...
|
|
// en fonction de la valeur de cette variable, on va appeler une fonction permettant
|
|
// de mettre à jour l'enveloppe courante.
|
|
int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe
|
|
int Einc; // Envelope step courant
|
|
int Ecmp; // Envelope counter limite pour la prochaine phase
|
|
int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque
|
|
// cette valeur est egal à AR[KSR]
|
|
int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression
|
|
// cette valeur est egal à DR[KSR]
|
|
int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue
|
|
// cette valeur est egal à SR[KSR]
|
|
int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement
|
|
// cette valeur est egal à RR[KSR]
|
|
int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree
|
|
// d'un autre ou carrement à la sortie de la voie
|
|
int INd; // input data of the slot = donnees en entree du slot
|
|
int ChgEnM; // Change envelop mask.
|
|
int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO
|
|
int AMSon; // AMS enable flag = drapeau d'activation de l'AMS
|
|
};
|
|
|
|
struct channel_t
|
|
{
|
|
int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back)
|
|
int LEFT; // LEFT enable flag
|
|
int RIGHT; // RIGHT enable flag
|
|
int ALGO; // Algorythm = determine les connections entre les operateurs
|
|
int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree)
|
|
int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO
|
|
int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO
|
|
int FNUM[4]; // hauteur frequence de la voie (+ 3 pour le mode special)
|
|
int FOCT[4]; // octave de la voie (+ 3 pour le mode special)
|
|
int KC[4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S)
|
|
slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie
|
|
int FFlag; // Frequency step recalculation flag
|
|
};
|
|
|
|
struct state_t
|
|
{
|
|
int TimerBase; // TimerBase calculation
|
|
int Status; // YM2612 Status (timer overflow)
|
|
int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter
|
|
int TimerAL;
|
|
int TimerAcnt; // timerA counter = valeur courante du Timer A
|
|
int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter
|
|
int TimerBL;
|
|
int TimerBcnt; // timerB counter = valeur courante du Timer B
|
|
int Mode; // Mode actuel des voie 3 et 6 (normal / special)
|
|
int DAC; // DAC enabled flag
|
|
channel_t CHANNEL[Ym2612_GENS_Emu::channel_count]; // Les 6 voies du YM2612
|
|
int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif
|
|
// cela nous rend le debuggage plus facile
|
|
};
|
|
|
|
#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)
|
|
|
|
#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
|
|
//#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
|
|
|
|
inline void set_seg( slot_t& s, int seg )
|
|
{
|
|
s.env_xor = 0;
|
|
s.env_max = INT_MAX;
|
|
s.SEG = seg;
|
|
if ( seg & 4 )
|
|
{
|
|
s.env_xor = ENV_MASK;
|
|
s.env_max = ENV_MASK;
|
|
}
|
|
}
|
|
|
|
struct tables_t
|
|
{
|
|
short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE)
|
|
int LFOcnt; // LFO counter = compteur-frequence pour le LFO
|
|
int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO
|
|
// plus le pas est grand, plus la frequence est grande
|
|
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_INC_TAB [8]; // LFO step table
|
|
|
|
short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay)
|
|
|
|
short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB)
|
|
short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE
|
|
int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus)
|
|
unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase
|
|
unsigned int FINC_TAB [2048]; // Frequency step table
|
|
};
|
|
|
|
static const unsigned char 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
|
|
};
|
|
|
|
static const unsigned char FKEY_TAB [16] =
|
|
{
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 1,
|
|
2, 3, 3, 3,
|
|
3, 3, 3, 3
|
|
};
|
|
|
|
static const unsigned char LFO_AMS_TAB [4] =
|
|
{
|
|
31, 4, 1, 0
|
|
};
|
|
|
|
static const unsigned char 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
|
|
};
|
|
|
|
inline void YM2612_Special_Update() { }
|
|
|
|
struct Ym2612_GENS_Impl
|
|
{
|
|
enum { channel_count = Ym2612_GENS_Emu::channel_count };
|
|
|
|
state_t YM2612;
|
|
int mute_mask;
|
|
tables_t g;
|
|
|
|
void KEY_ON( channel_t&, int );
|
|
void KEY_OFF( channel_t&, int );
|
|
int SLOT_SET( int, int );
|
|
int CHANNEL_SET( int, int );
|
|
int YM_SET( int, int );
|
|
|
|
void set_rate( double sample_rate, double clock_factor );
|
|
void reset();
|
|
void write0( int addr, int data );
|
|
void write1( int addr, int data );
|
|
void run_timer( int );
|
|
void run( int pair_count, Ym2612_GENS_Emu::sample_t* );
|
|
};
|
|
|
|
void Ym2612_GENS_Impl::KEY_ON( channel_t& ch, int nsl)
|
|
{
|
|
slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
|
|
|
|
if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ?
|
|
{
|
|
SL->Fcnt = 0;
|
|
|
|
// Fix Ecco 2 splash sound
|
|
|
|
SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM;
|
|
SL->ChgEnM = ~0;
|
|
|
|
// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK;
|
|
// SL->Ecnt = 0;
|
|
|
|
SL->Einc = SL->EincA;
|
|
SL->Ecmp = ENV_DECAY;
|
|
SL->Ecurp = ATTACK;
|
|
}
|
|
}
|
|
|
|
|
|
void Ym2612_GENS_Impl::KEY_OFF(channel_t& ch, int nsl)
|
|
{
|
|
slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
|
|
|
|
if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ?
|
|
{
|
|
if (SL->Ecnt < ENV_DECAY) // attack phase ?
|
|
{
|
|
SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY;
|
|
}
|
|
|
|
SL->Einc = SL->EincR;
|
|
SL->Ecmp = ENV_END;
|
|
SL->Ecurp = RELEASE;
|
|
}
|
|
}
|
|
|
|
|
|
int Ym2612_GENS_Impl::SLOT_SET( int Adr, int data )
|
|
{
|
|
int nch = Adr & 3;
|
|
if ( nch == 3 )
|
|
return 1;
|
|
|
|
channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)];
|
|
slot_t& sl = ch.SLOT [(Adr >> 2) & 3];
|
|
|
|
switch ( Adr & 0xF0 )
|
|
{
|
|
case 0x30:
|
|
if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1;
|
|
else sl.MUL = 1;
|
|
|
|
sl.DT = (int*) g.DT_TAB [(data >> 4) & 7];
|
|
|
|
ch.SLOT [0].Finc = -1;
|
|
|
|
break;
|
|
|
|
case 0x40:
|
|
sl.TL = data & 0x7F;
|
|
|
|
// SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound...
|
|
YM2612_Special_Update();
|
|
|
|
#if ((ENV_HBITS - 7) < 0)
|
|
sl.TLL = sl.TL >> (7 - ENV_HBITS);
|
|
#else
|
|
sl.TLL = sl.TL << (ENV_HBITS - 7);
|
|
#endif
|
|
|
|
break;
|
|
|
|
case 0x50:
|
|
sl.KSR_S = 3 - (data >> 6);
|
|
|
|
ch.SLOT [0].Finc = -1;
|
|
|
|
if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1];
|
|
else sl.AR = (int*) &g.NULL_RATE [0];
|
|
|
|
sl.EincA = sl.AR [sl.KSR];
|
|
if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA;
|
|
break;
|
|
|
|
case 0x60:
|
|
if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS;
|
|
else sl.AMS = 31;
|
|
|
|
if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1];
|
|
else sl.DR = (int*) &g.NULL_RATE [0];
|
|
|
|
sl.EincD = sl.DR [sl.KSR];
|
|
if (sl.Ecurp == DECAY) sl.Einc = sl.EincD;
|
|
break;
|
|
|
|
case 0x70:
|
|
if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1];
|
|
else sl.SR = (int*) &g.NULL_RATE [0];
|
|
|
|
sl.EincS = sl.SR [sl.KSR];
|
|
if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS;
|
|
break;
|
|
|
|
case 0x80:
|
|
sl.SLL = g.SL_TAB [data >> 4];
|
|
|
|
sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2];
|
|
|
|
sl.EincR = sl.RR [sl.KSR];
|
|
if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR;
|
|
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 */
|
|
|
|
set_seg( sl, (data & 8) ? (data & 0x0F) : 0 );
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Ym2612_GENS_Impl::CHANNEL_SET( int Adr, int data )
|
|
{
|
|
int num = Adr & 3;
|
|
if ( num == 3 )
|
|
return 1;
|
|
|
|
channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)];
|
|
|
|
switch ( Adr & 0xFC )
|
|
{
|
|
case 0xA0:
|
|
YM2612_Special_Update();
|
|
|
|
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;
|
|
break;
|
|
|
|
case 0xA4:
|
|
YM2612_Special_Update();
|
|
|
|
ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((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;
|
|
break;
|
|
|
|
case 0xA8:
|
|
if ( Adr < 0x100 )
|
|
{
|
|
num++;
|
|
|
|
YM2612_Special_Update();
|
|
|
|
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;
|
|
}
|
|
break;
|
|
|
|
case 0xAC:
|
|
if ( Adr < 0x100 )
|
|
{
|
|
num++;
|
|
|
|
YM2612_Special_Update();
|
|
|
|
YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((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;
|
|
}
|
|
break;
|
|
|
|
case 0xB0:
|
|
if ( ch.ALGO != (data & 7) )
|
|
{
|
|
// Fix VectorMan 2 heli sound (level 1)
|
|
YM2612_Special_Update();
|
|
|
|
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;
|
|
break;
|
|
|
|
case 0xB4: {
|
|
YM2612_Special_Update();
|
|
|
|
ch.LEFT = 0 - ((data >> 7) & 1);
|
|
ch.RIGHT = 0 - ((data >> 6) & 1);
|
|
|
|
ch.AMS = LFO_AMS_TAB [(data >> 4) & 3];
|
|
ch.FMS = LFO_FMS_TAB [data & 7];
|
|
|
|
for ( int i = 0; i < 4; i++ )
|
|
{
|
|
slot_t& sl = ch.SLOT [i];
|
|
sl.AMS = (sl.AMSon ? ch.AMS : 31);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Ym2612_GENS_Impl::YM_SET(int Adr, int data)
|
|
{
|
|
switch ( Adr )
|
|
{
|
|
case 0x22:
|
|
if (data & 8) // LFO enable
|
|
{
|
|
// Cool Spot music 1, LFO modified severals time which
|
|
// distord the sound, have to check that on a real genesis...
|
|
|
|
g.LFOinc = g.LFO_INC_TAB [data & 7];
|
|
}
|
|
else
|
|
{
|
|
g.LFOinc = g.LFOcnt = 0;
|
|
}
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
break;
|
|
|
|
case 0x26:
|
|
YM2612.TimerB = data;
|
|
|
|
if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12))
|
|
{
|
|
YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12);
|
|
}
|
|
break;
|
|
|
|
case 0x27:
|
|
// Parametre 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.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 demande
|
|
YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status
|
|
|
|
YM2612.Mode = data;
|
|
break;
|
|
|
|
case 0x28: {
|
|
int nch = data & 3;
|
|
if ( nch == 3 )
|
|
return 1;
|
|
if ( data & 4 )
|
|
nch += 3;
|
|
channel_t& ch = YM2612.CHANNEL [nch];
|
|
|
|
YM2612_Special_Update();
|
|
|
|
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
|
|
break;
|
|
}
|
|
|
|
case 0x2B:
|
|
if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update();
|
|
|
|
YM2612.DAC = data & 0x80; // activation/desactivation du DAC
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Ym2612_GENS_Impl::set_rate( double sample_rate, double clock_rate )
|
|
{
|
|
assert( sample_rate );
|
|
assert( clock_rate > sample_rate );
|
|
|
|
int i;
|
|
|
|
// 144 = 12 * (prescale * 2) = 12 * 6 * 2
|
|
// prescale set to 6 by default
|
|
|
|
double Frequence = clock_rate / sample_rate / 144.0;
|
|
if ( fabs( Frequence - 1.0 ) < 0.0000001 )
|
|
Frequence = 1.0;
|
|
YM2612.TimerBase = int (Frequence * 4096.0);
|
|
|
|
// 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 ?)
|
|
{
|
|
g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0;
|
|
}
|
|
else
|
|
{
|
|
double x = MAX_OUT; // Max output
|
|
x /= pow( 10.0, (ENV_STEP * i) / 20.0 ); // Decibel -> Voltage
|
|
|
|
g.TL_TAB [i] = (int) x;
|
|
g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i];
|
|
}
|
|
}
|
|
|
|
// Tableau SIN :
|
|
// g.SIN_TAB [x] [y] = sin(x) * y;
|
|
// x = phase and y = volume
|
|
|
|
g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF;
|
|
|
|
for(i = 1; i <= SIN_LENGHT / 4; i++)
|
|
{
|
|
double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus
|
|
x = 20 * log10(1 / x); // convert to dB
|
|
|
|
int j = (int) (x / ENV_STEP); // Get TL range
|
|
|
|
if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF;
|
|
|
|
g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j;
|
|
g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j;
|
|
}
|
|
|
|
// Tableau LFO (LFO wav) :
|
|
|
|
for(i = 0; i < LFO_LENGHT; i++)
|
|
{
|
|
double 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
|
|
|
|
g.LFO_ENV_TAB [i] = (int) x;
|
|
|
|
x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus
|
|
x *= (double) ((1 << (LFO_HBITS - 1)) - 1);
|
|
|
|
g.LFO_FREQ_TAB [i] = (int) x;
|
|
|
|
}
|
|
|
|
// Tableau Enveloppe :
|
|
// g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve
|
|
// g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve
|
|
|
|
for(i = 0; i < ENV_LENGHT; i++)
|
|
{
|
|
// Attack curve (x^8 - music level 2 Vectorman 2)
|
|
double x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8);
|
|
x *= ENV_LENGHT;
|
|
|
|
g.ENV_TAB [i] = (int) x;
|
|
|
|
// Decay curve (just linear)
|
|
x = pow(((double) (i) / (double) (ENV_LENGHT)), 1);
|
|
x *= ENV_LENGHT;
|
|
|
|
g.ENV_TAB [ENV_LENGHT + i] = (int) x;
|
|
}
|
|
for ( i = 0; i < 8; i++ )
|
|
g.ENV_TAB [i + ENV_LENGHT * 2] = 0;
|
|
|
|
g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state
|
|
|
|
// Tableau pour la conversion Attack -> Decay and Decay -> Attack
|
|
|
|
int j = ENV_LENGHT - 1;
|
|
for ( i = 0; i < ENV_LENGHT; i++ )
|
|
{
|
|
while ( j && g.ENV_TAB [j] < i )
|
|
j--;
|
|
|
|
g.DECAY_TO_ATTACK [i] = j << ENV_LBITS;
|
|
}
|
|
|
|
// Tableau pour le Substain Level
|
|
|
|
for(i = 0; i < 15; i++)
|
|
{
|
|
double x = i * 3; // 3 and not 6 (Mickey Mania first music for test)
|
|
x /= ENV_STEP;
|
|
|
|
g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY;
|
|
}
|
|
|
|
g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off
|
|
|
|
// Tableau Frequency Step
|
|
|
|
for(i = 0; i < 2048; i++)
|
|
{
|
|
double x = (double) (i) * 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
|
|
|
|
g.FINC_TAB [i] = (unsigned int) x;
|
|
}
|
|
|
|
// Tableaux Attack & Decay Rate
|
|
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
g.AR_TAB [i] = 0;
|
|
g.DR_TAB [i] = 0;
|
|
}
|
|
|
|
for(i = 0; i < 60; i++)
|
|
{
|
|
double x = 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 g.ENV_TAB
|
|
|
|
g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE);
|
|
g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE);
|
|
}
|
|
|
|
for(i = 64; i < 96; i++)
|
|
{
|
|
g.AR_TAB [i] = g.AR_TAB [63];
|
|
g.DR_TAB [i] = g.DR_TAB [63];
|
|
|
|
g.NULL_RATE [i - 64] = 0;
|
|
}
|
|
|
|
for ( i = 96; i < 128; i++ )
|
|
g.AR_TAB [i] = 0;
|
|
|
|
// Tableau Detune
|
|
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
for (int j = 0; j < 32; j++)
|
|
{
|
|
#if ((SIN_LBITS + SIN_HBITS - 21) < 0)
|
|
double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS));
|
|
#else
|
|
double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21));
|
|
#endif
|
|
|
|
g.DT_TAB [i + 0] [j] = (int) y;
|
|
g.DT_TAB [i + 4] [j] = (int) -y;
|
|
}
|
|
}
|
|
|
|
// Tableau LFO
|
|
g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
|
|
|
|
reset();
|
|
}
|
|
|
|
const char* Ym2612_GENS_Emu::set_rate( double sample_rate, double clock_rate )
|
|
{
|
|
if ( !impl )
|
|
{
|
|
impl = (Ym2612_GENS_Impl*) malloc( sizeof *impl );
|
|
if ( !impl )
|
|
return "Out of memory";
|
|
impl->mute_mask = 0;
|
|
}
|
|
memset( &impl->YM2612, 0, sizeof impl->YM2612 );
|
|
|
|
impl->set_rate( sample_rate, clock_rate );
|
|
|
|
return 0;
|
|
}
|
|
|
|
Ym2612_GENS_Emu::~Ym2612_GENS_Emu()
|
|
{
|
|
free( impl );
|
|
}
|
|
|
|
inline void Ym2612_GENS_Impl::write0( int opn_addr, int data )
|
|
{
|
|
assert( (unsigned) data <= 0xFF );
|
|
|
|
if ( opn_addr < 0x30 )
|
|
{
|
|
YM2612.REG [0] [opn_addr] = data;
|
|
YM_SET( opn_addr, data );
|
|
}
|
|
else if ( YM2612.REG [0] [opn_addr] != data )
|
|
{
|
|
YM2612.REG [0] [opn_addr] = data;
|
|
|
|
if ( opn_addr < 0xA0 )
|
|
SLOT_SET( opn_addr, data );
|
|
else
|
|
CHANNEL_SET( opn_addr, data );
|
|
}
|
|
}
|
|
|
|
inline void Ym2612_GENS_Impl::write1( int opn_addr, int data )
|
|
{
|
|
assert( (unsigned) data <= 0xFF );
|
|
|
|
if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data )
|
|
{
|
|
YM2612.REG [1] [opn_addr] = data;
|
|
|
|
if ( opn_addr < 0xA0 )
|
|
SLOT_SET( opn_addr + 0x100, data );
|
|
else
|
|
CHANNEL_SET( opn_addr + 0x100, data );
|
|
}
|
|
}
|
|
|
|
void Ym2612_GENS_Emu::reset()
|
|
{
|
|
impl->reset();
|
|
}
|
|
|
|
void Ym2612_GENS_Impl::reset()
|
|
{
|
|
g.LFOcnt = 0;
|
|
YM2612.TimerA = 0;
|
|
YM2612.TimerAL = 0;
|
|
YM2612.TimerAcnt = 0;
|
|
YM2612.TimerB = 0;
|
|
YM2612.TimerBL = 0;
|
|
YM2612.TimerBcnt = 0;
|
|
YM2612.DAC = 0;
|
|
|
|
YM2612.Status = 0;
|
|
|
|
int i;
|
|
for ( i = 0; i < channel_count; i++ )
|
|
{
|
|
channel_t& ch = YM2612.CHANNEL [i];
|
|
|
|
ch.LEFT = ~0;
|
|
ch.RIGHT = ~0;
|
|
ch.ALGO = 0;
|
|
ch.FB = 31;
|
|
ch.FMS = 0;
|
|
ch.AMS = 0;
|
|
|
|
for ( int j = 0 ;j < 4 ; j++ )
|
|
{
|
|
ch.S0_OUT [j] = 0;
|
|
ch.FNUM [j] = 0;
|
|
ch.FOCT [j] = 0;
|
|
ch.KC [j] = 0;
|
|
|
|
ch.SLOT [j].Fcnt = 0;
|
|
ch.SLOT [j].Finc = 0;
|
|
ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase...
|
|
ch.SLOT [j].Einc = 0;
|
|
ch.SLOT [j].Ecmp = 0;
|
|
ch.SLOT [j].Ecurp = RELEASE;
|
|
|
|
ch.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-- )
|
|
{
|
|
write0( i, 0xC0 );
|
|
write1( i, 0xC0 );
|
|
}
|
|
|
|
for ( i = 0xB2; i >= 0x22; i-- )
|
|
{
|
|
write0( i, 0 );
|
|
write1( i, 0 );
|
|
}
|
|
|
|
write0( 0x2A, 0x80 );
|
|
}
|
|
|
|
void Ym2612_GENS_Emu::write0( int addr, int data )
|
|
{
|
|
impl->write0( addr, data );
|
|
}
|
|
|
|
void Ym2612_GENS_Emu::write1( int addr, int data )
|
|
{
|
|
impl->write1( addr, data );
|
|
}
|
|
|
|
void Ym2612_GENS_Emu::mute_voices( int mask ) { impl->mute_mask = mask; }
|
|
|
|
static void update_envelope_( slot_t* sl )
|
|
{
|
|
switch ( sl->Ecurp )
|
|
{
|
|
case 0:
|
|
// Env_Attack_Next
|
|
|
|
// Verified with Gynoug even in HQ (explode SFX)
|
|
sl->Ecnt = ENV_DECAY;
|
|
|
|
sl->Einc = sl->EincD;
|
|
sl->Ecmp = sl->SLL;
|
|
sl->Ecurp = DECAY;
|
|
break;
|
|
|
|
case 1:
|
|
// Env_Decay_Next
|
|
|
|
// Verified with Gynoug even in HQ (explode SFX)
|
|
sl->Ecnt = sl->SLL;
|
|
|
|
sl->Einc = sl->EincS;
|
|
sl->Ecmp = ENV_END;
|
|
sl->Ecurp = SUBSTAIN;
|
|
break;
|
|
|
|
case 2:
|
|
// Env_Substain_Next(slot_t *SL)
|
|
if (sl->SEG & 8) // SSG envelope type
|
|
{
|
|
int release = sl->SEG & 1;
|
|
|
|
if ( !release )
|
|
{
|
|
// re KEY ON
|
|
|
|
// sl->Fcnt = 0;
|
|
// sl->ChgEnM = ~0;
|
|
|
|
sl->Ecnt = 0;
|
|
sl->Einc = sl->EincA;
|
|
sl->Ecmp = ENV_DECAY;
|
|
sl->Ecurp = ATTACK;
|
|
}
|
|
|
|
set_seg( *sl, (sl->SEG << 1) & 4 );
|
|
|
|
if ( !release )
|
|
break;
|
|
}
|
|
// fall through
|
|
|
|
case 3:
|
|
// Env_Release_Next
|
|
sl->Ecnt = ENV_END;
|
|
sl->Einc = 0;
|
|
sl->Ecmp = ENV_END + 1;
|
|
break;
|
|
|
|
// default: no op
|
|
}
|
|
}
|
|
|
|
inline void update_envelope( slot_t& sl )
|
|
{
|
|
int ecmp = sl.Ecmp;
|
|
if ( (sl.Ecnt += sl.Einc) >= ecmp )
|
|
update_envelope_( &sl );
|
|
}
|
|
|
|
template<int algo>
|
|
struct ym2612_update_chan {
|
|
static void func( tables_t&, channel_t&, Ym2612_GENS_Emu::sample_t*, int );
|
|
};
|
|
|
|
typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_GENS_Emu::sample_t*, int );
|
|
|
|
template<int algo>
|
|
void ym2612_update_chan<algo>::func( tables_t& g, channel_t& ch,
|
|
Ym2612_GENS_Emu::sample_t* buf, int length )
|
|
{
|
|
int not_end = ch.SLOT [S3].Ecnt - ENV_END;
|
|
|
|
// algo is a compile-time constant, so all conditions based on it are resolved
|
|
// during compilation
|
|
|
|
// special cases
|
|
if ( algo == 7 )
|
|
not_end |= ch.SLOT [S0].Ecnt - ENV_END;
|
|
|
|
if ( algo >= 5 )
|
|
not_end |= ch.SLOT [S2].Ecnt - ENV_END;
|
|
|
|
if ( algo >= 4 )
|
|
not_end |= ch.SLOT [S1].Ecnt - ENV_END;
|
|
|
|
int CH_S0_OUT_1 = ch.S0_OUT [1];
|
|
|
|
int in0 = ch.SLOT [S0].Fcnt;
|
|
int in1 = ch.SLOT [S1].Fcnt;
|
|
int in2 = ch.SLOT [S2].Fcnt;
|
|
int in3 = ch.SLOT [S3].Fcnt;
|
|
|
|
int YM2612_LFOinc = g.LFOinc;
|
|
int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc;
|
|
|
|
if ( !not_end )
|
|
return;
|
|
|
|
do
|
|
{
|
|
// envelope
|
|
int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK];
|
|
|
|
short const* const ENV_TAB = g.ENV_TAB;
|
|
|
|
#define CALC_EN( x ) \
|
|
int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS] + ch.SLOT [S##x].TLL; \
|
|
int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) & \
|
|
((temp##x - ch.SLOT [S##x].env_max) >> 31);
|
|
|
|
CALC_EN( 0 )
|
|
CALC_EN( 1 )
|
|
CALC_EN( 2 )
|
|
CALC_EN( 3 )
|
|
|
|
int const* const TL_TAB = g.TL_TAB;
|
|
|
|
#define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)])
|
|
|
|
// feedback
|
|
int CH_S0_OUT_0 = ch.S0_OUT [0];
|
|
{
|
|
int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB);
|
|
CH_S0_OUT_1 = CH_S0_OUT_0;
|
|
CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 );
|
|
}
|
|
|
|
int CH_OUTd;
|
|
if ( algo == 0 )
|
|
{
|
|
int temp = in1 + CH_S0_OUT_1;
|
|
temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 );
|
|
temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
|
|
CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
|
|
}
|
|
else if ( algo == 1 )
|
|
{
|
|
int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
|
|
temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
|
|
CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
|
|
}
|
|
else if ( algo == 2 )
|
|
{
|
|
int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
|
|
temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
|
|
CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
|
|
}
|
|
else if ( algo == 3 )
|
|
{
|
|
int temp = in1 + CH_S0_OUT_1;
|
|
temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) +
|
|
SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
|
|
CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
|
|
}
|
|
else if ( algo == 4 )
|
|
{
|
|
int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
|
|
CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) +
|
|
SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 );
|
|
//DO_LIMIT
|
|
}
|
|
else if ( algo == 5 )
|
|
{
|
|
int temp = CH_S0_OUT_1;
|
|
CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) +
|
|
SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) +
|
|
SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 );
|
|
//DO_LIMIT
|
|
}
|
|
else if ( algo == 6 )
|
|
{
|
|
CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
|
|
SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) +
|
|
SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
|
|
//DO_LIMIT
|
|
}
|
|
else if ( algo == 7 )
|
|
{
|
|
CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
|
|
SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) +
|
|
SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1;
|
|
//DO_LIMIT
|
|
}
|
|
|
|
CH_OUTd >>= MAX_OUT_BITS - output_bits + 2;
|
|
|
|
// update phase
|
|
unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] *
|
|
ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1));
|
|
YM2612_LFOcnt += YM2612_LFOinc;
|
|
in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
|
|
in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
|
|
in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
|
|
in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
|
|
|
|
int t0 = buf [0] + (CH_OUTd & ch.LEFT);
|
|
int t1 = buf [1] + (CH_OUTd & ch.RIGHT);
|
|
|
|
update_envelope( ch.SLOT [0] );
|
|
update_envelope( ch.SLOT [1] );
|
|
update_envelope( ch.SLOT [2] );
|
|
update_envelope( ch.SLOT [3] );
|
|
|
|
ch.S0_OUT [0] = CH_S0_OUT_0;
|
|
buf [0] = t0;
|
|
buf [1] = t1;
|
|
buf += 2;
|
|
}
|
|
while ( --length );
|
|
|
|
ch.S0_OUT [1] = CH_S0_OUT_1;
|
|
|
|
ch.SLOT [S0].Fcnt = in0;
|
|
ch.SLOT [S1].Fcnt = in1;
|
|
ch.SLOT [S2].Fcnt = in2;
|
|
ch.SLOT [S3].Fcnt = in3;
|
|
}
|
|
|
|
static const ym2612_update_chan_t UPDATE_CHAN [8] = {
|
|
&ym2612_update_chan<0>::func,
|
|
&ym2612_update_chan<1>::func,
|
|
&ym2612_update_chan<2>::func,
|
|
&ym2612_update_chan<3>::func,
|
|
&ym2612_update_chan<4>::func,
|
|
&ym2612_update_chan<5>::func,
|
|
&ym2612_update_chan<6>::func,
|
|
&ym2612_update_chan<7>::func
|
|
};
|
|
|
|
void Ym2612_GENS_Impl::run_timer( int length )
|
|
{
|
|
int const step = 6;
|
|
int remain = length;
|
|
do
|
|
{
|
|
int n = step;
|
|
if ( n > remain )
|
|
n = remain;
|
|
remain -= n;
|
|
|
|
long i = n * YM2612.TimerBase;
|
|
if (YM2612.Mode & 1) // Timer A ON ?
|
|
{
|
|
// if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL)
|
|
if ((YM2612.TimerAcnt -= i) <= 0)
|
|
{
|
|
// timer a overflow
|
|
|
|
YM2612.Status |= (YM2612.Mode & 0x04) >> 2;
|
|
YM2612.TimerAcnt += YM2612.TimerAL;
|
|
|
|
if (YM2612.Mode & 0x80)
|
|
{
|
|
KEY_ON( YM2612.CHANNEL [2], 0 );
|
|
KEY_ON( YM2612.CHANNEL [2], 1 );
|
|
KEY_ON( YM2612.CHANNEL [2], 2 );
|
|
KEY_ON( YM2612.CHANNEL [2], 3 );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (YM2612.Mode & 2) // Timer B ON ?
|
|
{
|
|
// if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL)
|
|
if ((YM2612.TimerBcnt -= i) <= 0)
|
|
{
|
|
// timer b overflow
|
|
YM2612.Status |= (YM2612.Mode & 0x08) >> 2;
|
|
YM2612.TimerBcnt += YM2612.TimerBL;
|
|
}
|
|
}
|
|
}
|
|
while ( remain > 0 );
|
|
}
|
|
|
|
void Ym2612_GENS_Impl::run( int pair_count, Ym2612_GENS_Emu::sample_t* out )
|
|
{
|
|
if ( pair_count <= 0 )
|
|
return;
|
|
|
|
if ( YM2612.Mode & 3 )
|
|
run_timer( pair_count );
|
|
|
|
// Mise à jour des pas des compteurs-frequences s'ils ont ete modifies
|
|
|
|
for ( int chi = 0; chi < channel_count; chi++ )
|
|
{
|
|
channel_t& ch = YM2612.CHANNEL [chi];
|
|
if ( ch.SLOT [0].Finc != -1 )
|
|
continue;
|
|
|
|
int i2 = 0;
|
|
if ( chi == 2 && (YM2612.Mode & 0x40) )
|
|
i2 = 2;
|
|
|
|
for ( int i = 0; i < 4; i++ )
|
|
{
|
|
// static int seq [4] = { 2, 1, 3, 0 };
|
|
// if ( i2 ) i2 = seq [i];
|
|
|
|
slot_t& sl = ch.SLOT [i];
|
|
int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]);
|
|
int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation
|
|
sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL;
|
|
if (sl.KSR != ksr) // si le KSR a change alors
|
|
{ // les differents 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 ( i2 )
|
|
i2 = (i2 ^ 2) ^ (i2 >> 1);
|
|
}
|
|
}
|
|
|
|
for ( int i = 0; i < channel_count; i++ )
|
|
{
|
|
if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) )
|
|
UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count );
|
|
}
|
|
|
|
g.LFOcnt += g.LFOinc * pair_count;
|
|
}
|
|
|
|
void Ym2612_GENS_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); }
|