mirror of
https://github.com/ZDoom/ZMusic.git
synced 2024-12-04 01:22:32 +00:00
faa997b986
Changelog: * Added an ability to disable the automatical arpeggio * Updated the GENS chip emulator from the 2.10 into GS/II (thanks to @freq-mod for the help) * Added an ability to set number of loops * Added an ability to disable/enable playing of selected MIDI channels * Fixed memory damages and crashes while playing XMI files * Added the chip channels allocation mode option * Fixed the playback of multi-song XMI files * Added an ability to switch the XMI song on the fly And also: * Fixed the work on big endian processors * Fixed ARM64 build on some platforms * Improved support of the EA-MUS files (Thanks to [dashodanger](https://github.com/dashodanger)) * Fixed crash on attempt to change the volume of a blank note
328 lines
14 KiB
C++
328 lines
14 KiB
C++
/***************************************************************************
|
|
* libgens: Gens Emulation Library. *
|
|
* Ym2612.hpp: Yamaha YM2612 FM synthesis chip emulator. (Private class) *
|
|
* *
|
|
* Copyright (c) 1999-2002 by Stéphane Dallongeville *
|
|
* Copyright (c) 2003-2004 by Stéphane Akhoun *
|
|
* Copyright (c) 2008-2015 by David Korth *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify it *
|
|
* under the terms of the GNU General Public License as published by the *
|
|
* Free Software Foundation; either version 2 of the License, or (at your *
|
|
* option) any later version. *
|
|
* *
|
|
* This program 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 General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License along *
|
|
* with this program; if not, write to the Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
|
***************************************************************************/
|
|
|
|
#ifndef __LIBGENS_SOUND_YM2612_P_HPP__
|
|
#define __LIBGENS_SOUND_YM2612_P_HPP__
|
|
|
|
// C includes.
|
|
#include <math.h>
|
|
#ifndef PI
|
|
#define PI 3.14159265358979323846
|
|
#endif
|
|
|
|
namespace LibGens {
|
|
|
|
class Ym2612;
|
|
class Ym2612Private
|
|
{
|
|
public:
|
|
Ym2612Private(Ym2612 *q);
|
|
|
|
protected:
|
|
friend class Ym2612;
|
|
Ym2612 *const q;
|
|
private:
|
|
// Q_DISABLE_COPY() equivalent.
|
|
// TODO: Add LibGens-specific version of Q_DISABLE_COPY().
|
|
Ym2612Private(const Ym2612Private &);
|
|
Ym2612Private &operator=(const Ym2612Private &);
|
|
|
|
public:
|
|
/**
|
|
* Initialize the static tables.
|
|
*/
|
|
static void doStaticInit(void);
|
|
|
|
struct slot_t {
|
|
unsigned int *DT; // paramètre detune
|
|
int MUL; // paramètre "multiple de fréquence"
|
|
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 première phase de régression
|
|
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 calculée par rapport à la fréquence actuelle, elle va influer
|
|
// sur les différents paramètres de l'enveloppe comme l'attaque, le decay ... comme dans la réalité !
|
|
int SEG; // Type enveloppe SSG
|
|
unsigned int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR])
|
|
unsigned int *DR; // Decay Rate (table pointeur) = Taux pour la régression (DR[KSR])
|
|
unsigned int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR])
|
|
unsigned int *RR; // Release Rate (table pointeur) = Taux pour le relâchement (RR[KSR])
|
|
int Fcnt; // Frequency Count = compteur-fréquence pour déterminer l'amplitude actuelle (SIN[Finc >> 16])
|
|
int Finc; // frequency step = pas d'incrémentation du compteur-fréquence
|
|
// plus le pas est grand, plus la fréquence 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'incrémentation du compteur durant la phase d'attaque
|
|
// cette valeur est égal à AR[KSR]
|
|
int EincD; // Envelope step for Decay = pas d'incrémentation du compteur durant la phase de regression
|
|
// cette valeur est égal à DR[KSR]
|
|
int EincS; // Envelope step for Sustain = pas d'incrémentation du compteur durant la phase de maintenue
|
|
// cette valeur est égal à SR[KSR]
|
|
int EincR; // Envelope step for Release = pas d'incrémentation du compteur durant la phase de relâchement
|
|
// cette valeur est égal à RR[KSR]
|
|
int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entrée
|
|
// d'un autre ou carrement à la sortie de la voie
|
|
int INd; // input data of the slot = données en entrée du slot
|
|
int ChgEnM; // Change envelop mask.
|
|
int AMS; // AMS depth level of this SLOT = degré 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 Old_OUTd; // ancienne sortie de la voie (son brut)
|
|
int OUTd; // sortie de la voie (son brut)
|
|
int LEFT; // LEFT enable flag
|
|
int RIGHT; // RIGHT enable flag
|
|
int ALGO; // Algorythm = détermine les connections entre les opérateurs
|
|
int FB; // shift count of self feed back = degré de "Feed-Back" du SLOT 1 (il est son unique entrée)
|
|
int FMS; // Fréquency Modulation Sensitivity of channel = degré de modulation de la fréquence sur la voie par le LFO
|
|
int AMS; // Amplitude Modulation Sensitivity of channel = degré de modulation de l'amplitude sur la voie par le LFO
|
|
int FNUM[4]; // hauteur fréquence de la voie (+ 3 pour le mode spécial)
|
|
int FOCT[4]; // octave de la voie (+ 3 pour le mode spécial)
|
|
int KC[4]; // Key Code = valeur fonction de la fréquence (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
|
|
int PANVolumeL; // Left PCM output channel volume
|
|
int PANVolumeR; // Right PCM output channel volume
|
|
};
|
|
|
|
struct state_t {
|
|
int Clock; // Horloge YM2612
|
|
int Rate; // Sample Rate (11025/22050/44100)
|
|
int TimerBase; // TimerBase calculation
|
|
int status; // YM2612 Status (timer overflow)
|
|
int OPNAadr; // addresse pour l'écriture dans l'OPN A (propre à l'émulateur)
|
|
int OPNBadr; // addresse pour l'écriture dans l'OPN B (propre à l'émulateur)
|
|
int LFOcnt; // LFO counter = compteur-fréquence pour le LFO
|
|
int LFOinc; // LFO step counter = pas d'incrémentation du compteur-fréquence du LFO
|
|
// plus le pas est grand, plus la fréquence est grande
|
|
|
|
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 / spécial)
|
|
int DAC; // DAC enabled flag
|
|
int DACdata; // DAC data
|
|
|
|
int dummy; // MSVC++ enforces 8-byte alignment on doubles. This forces said alignment on gcc.
|
|
double Frequence; // Fréquence de base, se calcul par rapport à l'horlage et au sample rate
|
|
|
|
unsigned int Inter_Cnt; // Interpolation Counter
|
|
unsigned int Inter_Step; // Interpolation Step
|
|
channel_t CHANNEL[6]; // Les 6 voies du YM2612
|
|
|
|
uint8_t REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif
|
|
// cela nous rend le débuggage plus facile
|
|
};
|
|
|
|
// YM2612 state.
|
|
state_t state;
|
|
|
|
// Change it if you need to do long update
|
|
static const int MAX_UPDATE_LENGTH = 2000;
|
|
|
|
// Gens always uses 16 bits sound (in 32 bits buffer) and do the convertion later if needed.
|
|
static const int OUTPUT_BITS = 16;
|
|
|
|
/** Various YM2612 definitions needed for table size. **/
|
|
/** TODO: Rework them to be more C++-esque? **/
|
|
// TODO: Signed or unsigned?
|
|
|
|
// SIN_LBITS <= 16
|
|
// LFO_HBITS <= 16
|
|
// (SIN_LBITS + SIN_HBITS) <= 26
|
|
// (ENV_LBITS + ENV_HBITS) <= 28
|
|
// (LFO_LBITS + LFO_HBITS) <= 28
|
|
|
|
// NOTE: Needs to be a #define due to conditional use in shifting.
|
|
#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
|
|
|
|
// NOTE: Needs to be a #define due to conditional use in shifting.
|
|
#define ENV_HBITS 12 // Env phase counter int part
|
|
#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting)
|
|
|
|
// NOTE: Needs to be a #define due to conditional use in shifting.
|
|
#define LFO_HBITS 10 // LFO phase counter int part
|
|
#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting)
|
|
|
|
static const int SIN_LENGTH = (1 << SIN_HBITS);
|
|
static const int ENV_LENGTH = (1 << ENV_HBITS);
|
|
static const int LFO_LENGTH = (1 << LFO_HBITS);
|
|
|
|
static const int TL_LENGTH = (ENV_LENGTH * 3); // Env + TL scaling + LFO
|
|
|
|
static const int SIN_MASK = (SIN_LENGTH - 1);
|
|
static const int ENV_MASK = (ENV_LENGTH - 1);
|
|
static const int LFO_MASK = (LFO_LENGTH - 1);
|
|
|
|
// TODO: Change to static const double.
|
|
#define ENV_STEP (96.0 / ENV_LENGTH) // ENV_MAX = 96 dB
|
|
|
|
static const int ENV_ATTACK = ((ENV_LENGTH * 0) << ENV_LBITS);
|
|
static const int ENV_DECAY = ((ENV_LENGTH * 1) << ENV_LBITS);
|
|
static const int ENV_END = ((ENV_LENGTH * 2) << ENV_LBITS);
|
|
|
|
static const int MAX_OUT_BITS = (SIN_HBITS + SIN_LBITS + 2); // Modulation = -4 <--> +4
|
|
static const int MAX_OUT = ((1 << MAX_OUT_BITS) - 1);
|
|
|
|
//Just for tests stuff...
|
|
//
|
|
//static const double COEF_MOD = 0.5;
|
|
//static const int MAX_OUT = ((int) (((1 << MAX_OUT_BITS) - 1) * COEF_MOD));
|
|
|
|
static const int OUT_BITS = (OUTPUT_BITS - 2);
|
|
static const int OUT_SHIFT = (MAX_OUT_BITS - OUT_BITS);
|
|
static /*const */int LIMIT_CH_OUT/* = ((int) (((1 << OUT_BITS) * 1.5) - 1))*/;
|
|
|
|
static int PG_CUT_OFF/* = ((int) (78.0 / ENV_STEP))*/;
|
|
static int ENV_CUT_OFF/* = ((int) (68.0 / ENV_STEP))*/;
|
|
|
|
static const int AR_RATE = 399128;
|
|
static const int DR_RATE = 5514396;
|
|
|
|
//static const int AR_RATE = 426136;
|
|
//static const int DR_RATE = (AR_RATE * 12);
|
|
|
|
static const int LFO_FMS_LBITS = 9; // FIXED (LFO_FMS_BASE gives somethink as 1)
|
|
static /*const*/ int LFO_FMS_BASE/* = ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)))*/;
|
|
|
|
enum ADSR {
|
|
ATTACK = 0,
|
|
DECAY = 1,
|
|
SUSTAIN = 2,
|
|
RELEASE = 3,
|
|
|
|
ADSR_MAX
|
|
};
|
|
|
|
enum SlotID {
|
|
S0 = 0,
|
|
S1 = 2, // Stupid typo of the YM2612
|
|
S2 = 1,
|
|
S3 = 3,
|
|
|
|
SlotID_MAX
|
|
};
|
|
|
|
// Static tables.
|
|
static bool isInit; // True if the static tables have been initialized.
|
|
static int *SIN_TAB[SIN_LENGTH]; // SINUS TABLE (pointer on TL TABLE)
|
|
static int TL_TAB[TL_LENGTH * 2]; // TOTAL LEVEL TABLE (plus and minus)
|
|
static unsigned int ENV_TAB[2 * ENV_LENGTH * 8]; // ENV CURVE TABLE (attack & decay)
|
|
//static unsigned int ATTACK_TO_DECAY[ENV_LENGTH]; // Conversion from attack to decay phase
|
|
static unsigned int DECAY_TO_ATTACK[ENV_LENGTH]; // Conversion from decay to attack phase
|
|
|
|
// Member tables.
|
|
unsigned int FINC_TAB[2048]; // Frequency step table
|
|
|
|
// Rate tables.
|
|
// All of these are Member variables, except for NULL_RATE.
|
|
// (NULL_RATE consists of all zeroes.)
|
|
unsigned int AR_TAB[128]; // Attack rate table.
|
|
unsigned int DR_TAB[96]; // Decay rate table.
|
|
unsigned int DT_TAB[8][32]; // Detune table.
|
|
static unsigned int SL_TAB[16]; // Sustain level table. (STATIC)
|
|
static unsigned int NULL_RATE[32]; // Table for NULL rate. (STATIC)
|
|
|
|
// LFO tables. (Static class variables)
|
|
static int LFO_ENV_TAB[LFO_LENGTH]; // LFO AMS TABLE (adjusted for 11.8 dB)
|
|
static int LFO_FREQ_TAB[LFO_LENGTH]; // LFO FMS TABLE
|
|
|
|
// LFO temporary tables. (Member variables)
|
|
int LFO_ENV_UP[MAX_UPDATE_LENGTH]; // Temporary calculated LFO AMS (adjusted for 11.8 dB)
|
|
int LFO_FREQ_UP[MAX_UPDATE_LENGTH]; // Temporary calculated LFO FMS
|
|
|
|
// NOTE: INTER_TAB isn't used...
|
|
//int INTER_TAB[MAX_UPDATE_LENGTH]; // Interpolation table
|
|
|
|
// LFO step table.
|
|
unsigned int LFO_INC_TAB[8]; // LFO step table.
|
|
|
|
// Envelope function declarations.
|
|
static void Env_Attack_Next(slot_t *SL);
|
|
static void Env_Decay_Next(slot_t *SL);
|
|
static void Env_Substain_Next(slot_t *SL);
|
|
static void Env_Release_Next(slot_t *SL);
|
|
static void Env_NULL_Next(slot_t *SL);
|
|
|
|
typedef void (*Env_Event)(slot_t *SL);
|
|
static const Env_Event ENV_NEXT_EVENT[8];
|
|
|
|
// Default detune table.
|
|
// FD == F number
|
|
static const uint8_t DT_DEF_TAB[4][32];
|
|
|
|
static const uint8_t FKEY_TAB[16];
|
|
static const uint8_t LFO_AMS_TAB[4];
|
|
static const uint8_t LFO_FMS_TAB[8];
|
|
|
|
// Interpolation calculation.
|
|
int int_cnt;
|
|
|
|
/** Functions for calculating parameters. **/
|
|
static void CALC_FINC_SL(slot_t *SL, int finc, int kc);
|
|
void CALC_FINC_CH(channel_t *CH);
|
|
|
|
/** Functions for setting values. **/
|
|
static void KEY_ON(channel_t *CH, int nsl);
|
|
static void KEY_OFF(channel_t *CH, int nsl);
|
|
|
|
void CSM_Key_Control(void);
|
|
|
|
int SLOT_SET(int address, uint8_t data);
|
|
int CHANNEL_SET(int address, uint8_t data);
|
|
int YM_SET(int address, uint8_t data);
|
|
|
|
/** Update Channel templates. **/
|
|
template<int algo>
|
|
inline void T_Update_Chan(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
|
|
|
|
template<int algo>
|
|
inline void T_Update_Chan_LFO(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
|
|
|
|
template<int algo>
|
|
inline void T_Update_Chan_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
|
|
|
|
template<int algo>
|
|
inline void T_Update_Chan_LFO_Int(channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
|
|
|
|
void Update_Chan(int algo_type, channel_t *CH, int32_t *bufL, int32_t *bufR, int length);
|
|
};
|
|
|
|
}
|
|
|
|
#endif /* __LIBGENS_SOUND_YM2612_P_HPP__ */
|
|
|