mirror of
https://github.com/libretro/Mesen.git
synced 2024-12-14 12:58:56 +00:00
399 lines
10 KiB
C++
399 lines
10 KiB
C++
//////////////////////////////////////////////////////////////////////////////////////////
|
|
// Adapted from Nintendulator's code, which itself is based on Mitsutaka Okazaki's code //
|
|
// https://www.qmtpro.com/~nes/nintendulator/
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
/* Modified for usage in VRC7 sound emulation
|
|
|
|
Copyright (C) Mitsutaka Okazaki 2004
|
|
|
|
This software is provided 'as-is', without any express or implied warranty.
|
|
In no event will the authors be held liable for any damages arising from
|
|
the use of this software.
|
|
|
|
Permission is granted to anyone to use this software for any purpose,
|
|
including commercial applications, and to alter it and redistribute it
|
|
freely, subject to the following restrictions:
|
|
|
|
1. The origin of this software must not be misrepresented; you must not
|
|
claim that you wrote the original software. If you use this software
|
|
in a product, an acknowledgment in the product documentation would be
|
|
appreciated but is not required.
|
|
2. Altered source versions must be plainly marked as such, and must not
|
|
be misrepresented as being the original software.
|
|
3. This notice may not be removed or altered from any source distribution.
|
|
|
|
*/
|
|
|
|
/***********************************************************************************
|
|
|
|
emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001
|
|
|
|
2003 01-24 : Modified by xodnizel to remove code not needed for the VRC7, among other things.
|
|
|
|
References:
|
|
fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development).
|
|
fmopl.c(fixed) -- (C) 2002 Jarek Burczynski.
|
|
s_opl.c -- 2001 written by Mamiya (NEZplug development).
|
|
fmgen.cpp -- 1999,2000 written by cisc.
|
|
fmpac.ill -- 2000 created by NARUTO.
|
|
MSX-Datapack
|
|
YMU757 data sheet
|
|
YM2143 data sheet
|
|
|
|
**************************************************************************************/
|
|
|
|
#pragma once
|
|
#include "stdafx.h"
|
|
#include "Snapshotable.h"
|
|
|
|
namespace Vrc7Opll {
|
|
/* Size of Sintable ( 8 -- 18 can be used. 9 recommended.)*/
|
|
const int32_t PG_BITS = 9;
|
|
const int32_t PG_WIDTH = 1 << PG_BITS;
|
|
|
|
/* Phase increment counter */
|
|
const int32_t DP_BITS = 18;
|
|
const int32_t DP_WIDTH = 1 << DP_BITS;
|
|
const int32_t DP_BASE_BITS = DP_BITS - PG_BITS;
|
|
|
|
/* Dynamic range (Accuracy of sin table) */
|
|
const int32_t DB_BITS = 8;
|
|
const double DB_STEP = (48.0 / (1 << DB_BITS));
|
|
const int32_t DB_MUTE = 1 << DB_BITS;
|
|
|
|
/* Dynamic range of envelope */
|
|
const double EG_STEP = 0.375;
|
|
const int32_t EG_BITS = 7;
|
|
const int32_t EG_MUTE = (1 << EG_BITS);
|
|
|
|
/* Dynamic range of total level */
|
|
const double TL_STEP = 0.75;
|
|
const int32_t TL_BITS = 6;
|
|
const int32_t TL_MUTE = (1 << TL_BITS);
|
|
|
|
/* Dynamic range of sustine level */
|
|
const double SL_STEP = 3.0;
|
|
const int32_t SL_BITS = 4;
|
|
const int32_t SL_MUTE = (1 << SL_BITS);
|
|
|
|
/* Bits for liner value */
|
|
const int32_t DB2LIN_AMP_BITS = 11;
|
|
const int32_t SLOT_AMP_BITS = DB2LIN_AMP_BITS;
|
|
|
|
/* Bits for envelope phase incremental counter */
|
|
const int32_t EG_DP_BITS = 22;
|
|
const int32_t EG_DP_WIDTH = (1 << EG_DP_BITS);
|
|
|
|
/* Bits for Pitch and Amp modulator */
|
|
const int32_t PM_PG_BITS = 8;
|
|
const int32_t PM_PG_WIDTH = (1 << PM_PG_BITS);
|
|
const int32_t PM_DP_BITS = 16;
|
|
const int32_t PM_DP_WIDTH = (1 << PM_DP_BITS);
|
|
const int32_t AM_PG_BITS = 8;
|
|
const int32_t AM_PG_WIDTH = (1 << AM_PG_BITS);
|
|
const int32_t AM_DP_BITS = 16;
|
|
const int32_t AM_DP_WIDTH = (1 << AM_DP_BITS);
|
|
|
|
/* PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200) */
|
|
const int32_t PM_AMP_BITS = 8;
|
|
const int32_t PM_AMP = (1 << PM_AMP_BITS);
|
|
|
|
/* PM speed(Hz) and depth(cent) */
|
|
const double PM_SPEED = 6.4;
|
|
const double PM_DEPTH = 13.75;
|
|
|
|
/* AM speed(Hz) and depth(dB) */
|
|
const double AM_SPEED = 3.7;
|
|
const double AM_DEPTH = 4.8;
|
|
|
|
const double PI = 3.14159265358979323846;
|
|
|
|
/* Definition of envelope mode */
|
|
enum
|
|
{
|
|
SETTLE, ATTACK, DECAY, SUSHOLD, SUSTINE, RELEASE, FINISH
|
|
};
|
|
|
|
class OpllTables : public Snapshotable
|
|
{
|
|
private:
|
|
/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI). */
|
|
int32_t wave2_4pi(int32_t e)
|
|
{
|
|
return (e) >> (SLOT_AMP_BITS - PG_BITS - 1);
|
|
}
|
|
|
|
/* Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). */
|
|
int32_t wave2_8pi(int32_t e)
|
|
{
|
|
return e >> (SLOT_AMP_BITS - PG_BITS - 2);
|
|
}
|
|
|
|
uint32_t TL2EG(uint32_t d)
|
|
{
|
|
return d*(int32_t)(TL_STEP / EG_STEP);
|
|
}
|
|
|
|
/* Adjust envelope speed which depends on sampling rate. */
|
|
uint32_t rate_adjust(double x)
|
|
{
|
|
return (uint32_t)((rate == 49716 ? x : ((double)(x)*clk / 72 / rate + 0.5))); /* added 0.5 to round the value*/
|
|
}
|
|
|
|
protected:
|
|
void StreamState(bool saving) override
|
|
{
|
|
Stream(clk, rate);
|
|
if(!saving) {
|
|
maketables(clk, rate);
|
|
}
|
|
}
|
|
|
|
public:
|
|
/* Input clock */
|
|
uint32_t clk = 844451141;
|
|
/* Sampling rate */
|
|
uint32_t rate = 3354932;
|
|
|
|
/* WaveTable for each envelope amp */
|
|
uint16_t fullsintable[PG_WIDTH];
|
|
uint16_t halfsintable[PG_WIDTH];
|
|
|
|
/* LFO Table */
|
|
int32_t pmtable[PM_PG_WIDTH];
|
|
int32_t amtable[AM_PG_WIDTH];
|
|
|
|
/* dB to Liner table */
|
|
int16_t DB2LIN_TABLE[(DB_MUTE + DB_MUTE) * 2];
|
|
|
|
/* Liner to Log curve conversion table (for Attack rate). */
|
|
uint16_t AR_ADJUST_TABLE[1 << EG_BITS];
|
|
|
|
/* Phase incr table for Attack */
|
|
uint32_t dphaseARTable[16][16];
|
|
/* Phase incr table for Decay and Release */
|
|
uint32_t dphaseDRTable[16][16];
|
|
|
|
/* KSL + TL Table */
|
|
uint32_t tllTable[16][8][1 << TL_BITS][4];
|
|
int32_t rksTable[2][8][2];
|
|
|
|
/* Phase incr table for PG */
|
|
uint32_t dphaseTable[512][8][16];
|
|
|
|
uint16_t *waveform[2] = { fullsintable, halfsintable };
|
|
|
|
/* Phase delta for LFO */
|
|
uint32_t pm_dphase;
|
|
uint32_t am_dphase;
|
|
|
|
/* Table for AR to LogCurve. */
|
|
void makeAdjustTable()
|
|
{
|
|
int32_t i;
|
|
|
|
AR_ADJUST_TABLE[0] = (1 << EG_BITS);
|
|
for(i = 1; i < 128; i++)
|
|
AR_ADJUST_TABLE[i] = (uint16_t)((double)(1 << EG_BITS) - 1 - (1 << EG_BITS) * log((double)i) / log(128.0));
|
|
}
|
|
|
|
|
|
/* Table for dB(0 -- (1<<DB_BITS)-1) to Liner(0 -- DB2LIN_AMP_WIDTH) */
|
|
void makeDB2LinTable()
|
|
{
|
|
int32_t i;
|
|
|
|
for(i = 0; i < DB_MUTE + DB_MUTE; i++) {
|
|
DB2LIN_TABLE[i] = (int16_t)((double)((1 << DB2LIN_AMP_BITS) - 1) * pow(10.0, -(double)i * DB_STEP / 20));
|
|
if(i >= DB_MUTE) DB2LIN_TABLE[i] = 0;
|
|
DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = (int16_t)(-DB2LIN_TABLE[i]);
|
|
}
|
|
}
|
|
|
|
/* Liner(+0.0 - +1.0) to dB((1<<DB_BITS) - 1 -- 0) */
|
|
int32_t lin2db(double d)
|
|
{
|
|
if(d == 0)
|
|
return (DB_MUTE - 1);
|
|
else
|
|
return Min(-(int32_t)(20.0 * log10(d) / DB_STEP), DB_MUTE - 1); /* 0 -- 127 */
|
|
}
|
|
|
|
|
|
/* Sin Table */
|
|
void makeSinTable(void)
|
|
{
|
|
int32_t i;
|
|
|
|
for(i = 0; i < PG_WIDTH / 4; i++) {
|
|
fullsintable[i] = (uint16_t)lin2db(sin(2.0 * PI * i / PG_WIDTH));
|
|
}
|
|
|
|
for(i = 0; i < PG_WIDTH / 4; i++) {
|
|
fullsintable[PG_WIDTH / 2 - 1 - i] = fullsintable[i];
|
|
}
|
|
|
|
for(i = 0; i < PG_WIDTH / 2; i++) {
|
|
fullsintable[PG_WIDTH / 2 + i] = (uint16_t)(DB_MUTE + DB_MUTE + fullsintable[i]);
|
|
}
|
|
|
|
for(i = 0; i < PG_WIDTH / 2; i++)
|
|
halfsintable[i] = fullsintable[i];
|
|
for(i = PG_WIDTH / 2; i < PG_WIDTH; i++)
|
|
halfsintable[i] = fullsintable[0];
|
|
}
|
|
|
|
/* Table for Pitch Modulator */
|
|
void makePmTable(void)
|
|
{
|
|
int32_t i;
|
|
|
|
for(i = 0; i < PM_PG_WIDTH; i++)
|
|
pmtable[i] = (int32_t)((double)PM_AMP * pow(2.0, (double)PM_DEPTH * sin(2.0 * PI * i / PM_PG_WIDTH) / 1200));
|
|
}
|
|
|
|
/* Table for Amp Modulator */
|
|
void makeAmTable(void)
|
|
{
|
|
int32_t i;
|
|
|
|
for(i = 0; i < AM_PG_WIDTH; i++)
|
|
amtable[i] = (int32_t)((double)AM_DEPTH / 2 / DB_STEP * (1.0 + sin(2.0 * PI * i / PM_PG_WIDTH)));
|
|
}
|
|
|
|
/* Phase increment counter table */
|
|
void makeDphaseTable(void)
|
|
{
|
|
uint32_t fnum, block, ML;
|
|
uint32_t mltable[16] =
|
|
{ 1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2 };
|
|
|
|
for(fnum = 0; fnum < 512; fnum++)
|
|
for(block = 0; block < 8; block++)
|
|
for(ML = 0; ML < 16; ML++)
|
|
dphaseTable[fnum][block][ML] = rate_adjust(((fnum * mltable[ML]) << block) >> (20 - DP_BITS));
|
|
}
|
|
|
|
void makeTllTable(void)
|
|
{
|
|
static double kltable[16] = {
|
|
0.00, 18.00, 24.00, 27.75, 30.00, 32.25, 33.75, 35.25,
|
|
36.00, 37.50, 38.25, 39.00, 39.75, 40.50, 41.25, 42.00
|
|
};
|
|
|
|
int32_t tmp;
|
|
int32_t fnum, block, TL, KL;
|
|
|
|
for(fnum = 0; fnum < 16; fnum++)
|
|
for(block = 0; block < 8; block++)
|
|
for(TL = 0; TL < 64; TL++)
|
|
for(KL = 0; KL < 4; KL++) {
|
|
if(KL == 0) {
|
|
tllTable[fnum][block][TL][KL] = TL2EG(TL);
|
|
} else {
|
|
tmp = (int32_t)(kltable[fnum] - 6.000 * (7 - block));
|
|
if(tmp <= 0)
|
|
tllTable[fnum][block][TL][KL] = TL2EG(TL);
|
|
else
|
|
tllTable[fnum][block][TL][KL] = (uint32_t)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Rate Table for Attack */
|
|
void makeDphaseARTable(void)
|
|
{
|
|
int32_t AR, Rks, RM, RL;
|
|
|
|
for(AR = 0; AR < 16; AR++)
|
|
for(Rks = 0; Rks < 16; Rks++) {
|
|
RM = AR + (Rks >> 2);
|
|
RL = Rks & 3;
|
|
if(RM > 15)
|
|
RM = 15;
|
|
switch(AR) {
|
|
case 0:
|
|
dphaseARTable[AR][Rks] = 0;
|
|
break;
|
|
case 15:
|
|
dphaseARTable[AR][Rks] = 0;/*EG_DP_WIDTH;*/
|
|
break;
|
|
default:
|
|
dphaseARTable[AR][Rks] = rate_adjust((3 * (RL + 4) << (RM + 1)));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Rate Table for Decay and Release */
|
|
void makeDphaseDRTable(void)
|
|
{
|
|
int32_t DR, Rks, RM, RL;
|
|
|
|
for(DR = 0; DR < 16; DR++)
|
|
for(Rks = 0; Rks < 16; Rks++) {
|
|
RM = DR + (Rks >> 2);
|
|
RL = Rks & 3;
|
|
if(RM > 15)
|
|
RM = 15;
|
|
switch(DR) {
|
|
case 0:
|
|
dphaseDRTable[DR][Rks] = 0;
|
|
break;
|
|
default:
|
|
dphaseDRTable[DR][Rks] = rate_adjust((RL + 4) << (RM - 1));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void makeRksTable(void)
|
|
{
|
|
|
|
int32_t fnum8, block, KR;
|
|
|
|
for(fnum8 = 0; fnum8 < 2; fnum8++)
|
|
for(block = 0; block < 8; block++)
|
|
for(KR = 0; KR < 2; KR++) {
|
|
if(KR != 0)
|
|
rksTable[fnum8][block][KR] = (block << 1) + fnum8;
|
|
else
|
|
rksTable[fnum8][block][KR] = block >> 1;
|
|
}
|
|
}
|
|
|
|
int32_t Min(int32_t i, int32_t j)
|
|
{
|
|
if(i < j)
|
|
return i;
|
|
else
|
|
return j;
|
|
}
|
|
|
|
void internal_refresh()
|
|
{
|
|
makeDphaseTable();
|
|
makeDphaseARTable();
|
|
makeDphaseDRTable();
|
|
pm_dphase = (uint32_t)rate_adjust(PM_SPEED * PM_DP_WIDTH / (clk / 72));
|
|
am_dphase = (uint32_t)rate_adjust(AM_SPEED * AM_DP_WIDTH / (clk / 72));
|
|
}
|
|
|
|
void maketables(uint32_t c, uint32_t r)
|
|
{
|
|
clk = c;
|
|
makePmTable();
|
|
makeAmTable();
|
|
makeDB2LinTable();
|
|
makeAdjustTable();
|
|
makeTllTable();
|
|
makeRksTable();
|
|
makeSinTable();
|
|
//makeDefaultPatch ();
|
|
|
|
rate = r;
|
|
internal_refresh();
|
|
}
|
|
};
|
|
} |