📄 nes_vrc7.cpp
字号:
/*
** Konami VRC7 ExSound by TAKEDA, toshiya
**
** original: s_vrc7.c in nezp0922
*/
#define OPLL_CH_MAX 6/* 9 */
/* PG_BITS + PG_SHIFT < 32 */
#define PG_BITS 10
#define AM_BITS 8
#define PM_BITS 8
#define AM_DEPTH 4.8 /* 1.0 */ /* dB */
#define PM_DEPTH 14.0 /* 7.0 */ /* cent */
#define PM_SHIFT 16
#define PG_SHIFT (20 - PG_BITS)
#define EG_SHIFT 7/*8*/
#define LOG_KEYOFF (32 << LOG_BITS)
#define EG_KEYOFF (LOG_KEYOFF << EG_SHIFT)
typedef enum {
EGM_OFF,
EGM_A,
EGM_D,
EGM_S,
EGM_SS,
EGM_R
} OPLL_EGMODE;
#define OPLL_UPDATE_FREQH (1 << 0)
#define OPLL_UPDATE_FREQL (1 << 1)
#define OPLL_UPDATE_TONE (1 << 2)
static uint32 amtbl[1 << AM_BITS];
static uint32 pmtbl[1 << PM_BITS];
static uint32 sintbl[1 << PG_BITS];
static uint32 sintbld[1 << PG_BITS];
static uint8 multbl[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,
};
static uint8 fbtbl[8] =
{
31,6,5,4,3,2,1,0,
};
static uint8 oplltone[][8] =
{
{ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
#if 1
/* VRC7ED2(00/06/26) vrc7tone.bin */
/* AM1 AM2 KS1 KS2 AD1 AD2 SR1 SR2 */
{ 0x53,0x02,0x10,0x07,0xF3,0xF0,0x48,0x1E },
{ 0x03,0x51,0x5E,0x07,0xF1,0xF1,0x0E,0x0C },
{ 0x01,0x51,0x4F,0x04,0xD8,0xE0,0x0F,0x0F },
{ 0x01,0x41,0x1A,0x01,0xA0,0xC0,0xFF,0xFF },
{ 0x02,0x01,0x18,0x07,0xD2,0xA1,0xA7,0xE8 },
{ 0x34,0x21,0xD0,0x07,0x44,0x84,0xF4,0xEE },
{ 0x21,0x61,0x17,0x07,0x71,0x81,0xCE,0x9B },
{ 0x33,0x31,0x58,0x06,0xF0,0xF0,0x0E,0x0E },
{ 0x07,0x01,0x2F,0x00,0xE0,0xE0,0xFF,0xFF },
{ 0x37,0xA1,0x1B,0x07,0xF1,0xF1,0xF4,0xEE },
{ 0x07,0x81,0x24,0x07,0xFF,0xFF,0xF4,0xFF },
{ 0x61,0x81,0x08,0x72,0xF0,0xF0,0xBD,0xAC },
{ 0x01,0x02,0xD6,0x07,0xF8,0xE4,0xFF,0xCF },
{ 0x21,0x61,0x00,0x00,0xF3,0xE2,0xB6,0xD9 },
{ 0x11,0x11,0xD4,0x07,0xFF,0xFF,0xFF,0xFF },
#endif
{ 0x13,0x11,0x25,0x00,0xd7,0xb7,0xf4,0xf4 }, /* 16 Rhythm 1: */
{ 0x13,0x11,0x25,0x00,0xd7,0xb7,0xf4,0xf4 }, /* 17 Rhythm 2: */
{ 0x13,0x11,0x25,0x00,0xd7,0xb7,0xf4,0xf4 }, /* 18 Rhythm 3: */
};
__inline static uint32 PGCALC(OPLL_OP *pop)
{
if (pop->pg_vib)
{
pop->vib_cycles -= 1 << PM_SHIFT;
while (pop->vib_cycles < 0)
{
pop->vib_cycles += apu->ym2413s.lfo[1].output;
pop->pg_phase += pop->pg_spd;
}
}
else
{
pop->pg_phase += pop->pg_spd;
}
return (pop->input + (pop->pg_phase >> PG_SHIFT)) & ((1 << PG_BITS) - 1);
}
__inline static void OPKEY(OPLL_OP *pop, uint8 key)
{
if (key)
{
pop->eg_mode = EGM_A;
pop->eg_phase = 63 << (EG_SHIFT + LOG_BITS - 3);
}
else
{
if (pop->eg_mode == EGM_OFF) return;
pop->eg_mode = EGM_R;
}
}
__inline static uint32 EGCALC(OPLL_OP *pop)
{
uint32 egout = pop->tl << (LOG_BITS - 3);
if (pop->eg_am) egout += apu->ym2413s.lfo[0].output;
switch (pop->eg_mode)
{
case EGM_A:
{
uint32 attack;
attack = 1 + (pop->eg_phase >> pop->eg_arr);
if (pop->eg_phase > attack)
{
pop->eg_phase -= attack;
break;
}
}
pop->eg_phase = 0;
pop->eg_mode = EGM_D;
case EGM_D:
if (pop->eg_phase < pop->eg_sl)
{
pop->eg_phase += pop->eg_drr;
break;
}
if (pop->eg_type)
{
pop->eg_mode = EGM_SS;
break;
}
pop->eg_mode = EGM_S;
break;
case EGM_S:
if (pop->eg_phase < EG_KEYOFF)
{
pop->eg_phase += pop->eg_rrr;
}
else
{
pop->eg_mode = EGM_OFF;
}
break;
case EGM_SS:
break;
case EGM_R:
if (pop->eg_phase < EG_KEYOFF)
{
if (pop->su_type)
{
pop->eg_phase += (5 << 2);
}
else if (pop->eg_type)
pop->eg_phase += pop->eg_rrr;
else
pop->eg_phase += (13 << 2);
break;
}
pop->eg_mode = EGM_OFF;
break;
case EGM_OFF:
return EG_KEYOFF;
}
return ((pop->eg_phase >> EG_SHIFT) + egout) << 1;
}
static uint32 OPCALC(OPLL_OP *pop)
{
return pop->sintblp[PGCALC(pop)] + EGCALC(pop);
}
__inline static void EGSET(OPLL_CH *pch)
{
pch->op[0].eg_am = pch->tone[0] & 0x80;
pch->op[1].eg_am = pch->tone[1] & 0x80;
pch->op[0].tl = pch->tone[2] & 0x3F;
pch->op[0].sintblp = (pch->tone[3] & 0x08) ? sintbld : sintbl;
pch->op[1].sintblp = (pch->tone[3] & 0x10) ? sintbld : sintbl;
pch->op[0].eg_ks = pch->tone[2] >> 6;
pch->op[1].eg_ks = pch->tone[3] >> 6;
pch->op[0].eg_type = pch->tone[0] & 0x20;
pch->op[1].eg_type = pch->tone[1] & 0x20;
pch->op[0].eg_ar = (pch->tone[4] & 0xF0) >> 2;
pch->op[1].eg_ar = (pch->tone[5] & 0xF0) >> 2;
pch->op[0].eg_dr = (pch->tone[4] & 0x0F) << 2;
pch->op[1].eg_dr = (pch->tone[5] & 0x0F) << 2;
pch->op[0].eg_rr = (pch->tone[6] & 0x0F) << 2;
pch->op[1].eg_rr = (pch->tone[7] & 0x0F) << 2;
pch->op[0].eg_sl = (pch->tone[6] & 0xF0) << (EG_SHIFT + LOG_BITS - 3 - 2);
pch->op[1].eg_sl = (pch->tone[7] & 0xF0) << (EG_SHIFT + LOG_BITS - 3 - 2);
}
__inline static void EGSET2(OPLL_OP *pop)
{
pop->eg_arr = (15 - (pop->eg_ar >> 2));
pop->eg_drr = pop->eg_dr;
pop->eg_rrr = pop->eg_rr;
}
__inline static void EGSET2KSR(OPLL_OP *pop, uint8 freqh)
{
uint8 ksr = (freqh & 0x0F) >> (3 - pop->eg_ks);
if (pop->eg_ar)
{
uint8 arr = pop->eg_ar + ksr;
if (arr > 63) arr = 63;
pop->eg_arr = (15 - (arr >> 2));
}
else
{
pop->eg_arr = 15;
}
if (pop->eg_dr)
{
pop->eg_drr = pop->eg_dr + ksr;
if (pop->eg_drr > 63) pop->eg_drr = 63;
}
else
{
pop->eg_drr = 0;
}
if (pop->eg_rr)
{
pop->eg_rrr = pop->eg_rr + ksr;
if (pop->eg_rrr > 63) pop->eg_rrr = 63;
}
else
{
pop->eg_rrr = 0;
}
}
__inline static void PGSET(OPLL_CH *pch)
{
uint32 fnum, block;
fnum = ((pch->freqh << 8) + pch->freql) & 0x1FF;
block = (pch->freqh >> 1) & 7;
pch->op[0].pg_spd = (multbl[pch->tone[0] & 0xF] * fnum) << block;
pch->op[1].pg_spd = (multbl[pch->tone[1] & 0xF] * fnum) << block;
}
__inline static int32 CHCALC(OPLL_CH *pch)
{
if (pch->update)
{
if (pch->update & (OPLL_UPDATE_FREQL | OPLL_UPDATE_FREQH | OPLL_UPDATE_TONE))
{
PGSET(pch);
}
if (pch->update & OPLL_UPDATE_TONE)
{
pch->fb = fbtbl[pch->tone[3] & 0x7] + (LOG_LIN_BITS - PG_BITS);
pch->fbbuf[0] = pch->fbbuf[1] = 0;
if (pch->fb > 31) pch->fb = 31;
pch->op[0].pg_vib = pch->tone[0] & 0x40;
pch->op[1].pg_vib = pch->tone[1] & 0x40;
EGSET(pch);
}
if (pch->update & OPLL_UPDATE_FREQH)
{
if ((pch->key ^ pch->freqh) & 0x10)
{
pch->key ^= 0x10;
OPKEY(&pch->op[0], (uint8)(pch->key & 0x10));
OPKEY(&pch->op[1], (uint8)(pch->key & 0x10));
}
pch->op[0].su_type = pch->freqh & 0x20;
pch->op[1].su_type = pch->freqh & 0x20;
}
if (pch->update & (OPLL_UPDATE_FREQH | OPLL_UPDATE_TONE))
{
int op;
for (op = 0; op < 2; op++)
{
if (pch->tone[op] & 0x10)
EGSET2KSR(&pch->op[op], pch->freqh);
else
EGSET2(&pch->op[op]);
}
}
pch->update = 0;
}
pch->cycles -= pch->cps;
while (pch->cycles < 0)
{
uint32 opout;
pch->cycles += 1 << 18;
#if 0
pch->op[0].input = pch->fbbuf[0] + pch->fbbuf[1];
opout = OPCALC(&pch->op[0]);
pch->fbbuf[1] = pch->fbbuf[0];
pch->fbbuf[0] = LogToLinear(opout, pch->fb + 1);
pch->op[1].input = LogToLinear(opout, (LOG_LIN_BITS - PG_BITS - 1));
#else
pch->op[0].input = pch->fbbuf[0];
opout = OPCALC(&pch->op[0]);
pch->fbbuf[0] = LogToLinear(opout, pch->fb);
pch->op[1].input = LogToLinear(opout, (LOG_LIN_BITS - PG_BITS - 1));
#endif
pch->output = OPCALC(&pch->op[1]);
}
return LogToLinear(pch->output + apu->ym2413s.mastervolume, (LOG_LIN_BITS - 21));
}
__inline static int32 LFOCALC(OPLL_LFO *pch)
{
pch->cycles -= pch->cps;
while (pch->cycles < 0)
{
pch->cycles += pch->spd;
pch->adr++;
}
pch->adr &= pch->adrmask;
pch->output = pch->table[pch->adr];
return pch->output;
}
static int32 OPLLSoundRender(void)
{
int32 accum = 0;
uint8 ch;
#if OPLL_CH_MAX == 9
uint8 chmax = (apu->ym2413s.rhythmc & 0x20) ? 6 : 9;
#else
const uint8 chmax = 6; /* VRCVII */
#endif
for (ch = 0; ch < 2; ch++) LFOCALC(&apu->ym2413s.lfo[ch]);
for (ch = 0; ch < chmax; ch++)
{
accum += CHCALC(&apu->ym2413s.ch[ch]);
}
return accum;
}
static void OPLLSoundVolume(uint32 volume)
{
apu->ym2413s.mastervolume = (volume << (LOG_BITS - 8)) << 1;
}
static void OPLLSoundWriteAddr(uint32 address, uint8 value)
{
apu->ym2413s.adr = value;
}
static void OPLLSoundWriteData(uint32 address, uint8 value)
{
int ch = apu->ym2413s.adr & 0xF;
switch (apu->ym2413s.adr & 0xF8)
{
case 0x00:
apu->ym2413s.usertone[apu->ym2413s.adr] = value;
apu->ym2413s.toneupdate = ~0;
break;
#if OPLL_CH_MAX == 9
case 0x08:
if (ch != 0xE) break;
apu->ym2413s.rhythmc = value;
/* rhythmmode */
break;
#endif
case 0x10:
case 0x18:
if (ch >= OPLL_CH_MAX) break;
apu->ym2413s.ch[ch].freql = value;
apu->ym2413s.ch[ch].update |= OPLL_UPDATE_FREQL;
break;
case 0x20:
case 0x28:
if (ch >= OPLL_CH_MAX) break;
apu->ym2413s.ch[ch].freqh = value;
apu->ym2413s.ch[ch].update |= OPLL_UPDATE_FREQH;
break;
case 0x30:
case 0x38:
if (ch >= OPLL_CH_MAX) break;
#if OPLL_CH_MAX == 9
if ((apu->ym2413s.rhythmc & 0x20) && (ch > 5))
{
if (ch != 6)
apu->ym2413s.ch[ch].op[0].tl = ((value >> 4) & 0xF) << 2;
apu->ym2413s.ch[ch].op[1].tl = (value & 0xF) << 2;
}
else
#endif
{
if(apu->ym2413s.ch[ch].toneno != (value >> 4))
{
if (value >> 4)
{
apu->ym2413s.ch[ch].toneno = value >> 4;
memcpy(apu->ym2413s.ch[ch].tone, oplltone[value >> 4], 8);
apu->ym2413s.ch[ch].update |= OPLL_UPDATE_TONE;
}
else if ((apu->ym2413s.ch[ch].toneno != 0x10) || (apu->ym2413s.toneupdate & (1 << ch)))
{
apu->ym2413s.toneupdate &= ~(1 << ch);
apu->ym2413s.ch[ch].toneno = 0x10;
memcpy(apu->ym2413s.ch[ch].tone, apu->ym2413s.usertone, 8);
apu->ym2413s.ch[ch].update |= OPLL_UPDATE_TONE;
}
}
apu->ym2413s.ch[ch].op[1].tl = (value & 0xF) << 2;
}
break;
}
}
static void OPLLSoundWrite(uint32 address, uint8 value)
{
if(address == 0x9010)
{
OPLLSoundWriteAddr(address, value);
}
else if(address == 0x9030)
{
OPLLSoundWriteData(address, value);
}
}
static void OPLLSoundReset(void)
{
uint32 i, w;
double a;
uint32 cps;
memset(&apu->ym2413s, 0, sizeof(OPLLSOUND));
cps = DivFix(NES_BASECYCLES, 6 * 72 * SAMPLE_RATE, 18);
for (i = 0; i < OPLL_CH_MAX; i++)
{
apu->ym2413s.ch[i].cps = cps;
apu->ym2413s.ch[i].toneno = 0x10;
apu->ym2413s.ch[i].fb = 31;
apu->ym2413s.ch[i].output = 0 + (LOG_KEYOFF << 1);
apu->ym2413s.ch[i].op[0].sintblp = sintbl;
apu->ym2413s.ch[i].op[1].sintblp = sintbl;
}
w = 1 << (PG_BITS - 2);
sintbl[w * 0] = 0 + (LOG_KEYOFF << 1);
sintbl[w * 1] = 0;
sintbl[w * 2] = 1 + (LOG_KEYOFF << 1);
sintbl[w * 3] = 1;
sintbld[w * 0] = 0 + (LOG_KEYOFF << 1);
sintbld[w * 1] = 0;
sintbld[w * 2] = 0 + (LOG_KEYOFF << 1);
sintbld[w * 3] = 0 + (LOG_KEYOFF << 1);
for (i = 1; i < w; i++)
{
uint32 ua;
a = sin((2 * M_PI * i) / (double)(w << 2)) * (1 << LOG_LIN_BITS);
ua = (uint32)((LOG_LIN_BITS - (log(a) / log(2))) * (1 << LOG_BITS));
sintbl[w * 0 + i] = sintbl[w * 2 - i] = 0 + (ua << 1);
sintbl[w * 2 + i] = sintbl[w * 4 - i] = 1 + (ua << 1);
sintbld[w * 0 + i] = sintbld[w * 2 - i] = 0 + (ua << 1);
sintbld[w * 2 + i] = sintbld[w * 4 - i] = 0 + (LOG_KEYOFF << 1);
}
/* AM(tremoro) */
w = 1 << AM_BITS;
for (i = 0; i < w; i++)
{
a = (1 + sin((2 * M_PI * i) / w)) * ((1 << LOG_BITS) * AM_DEPTH / 12);
amtbl[i] = (uint32)a;
}
apu->ym2413s.lfo[0].cps = cps;
apu->ym2413s.lfo[0].spd = DivFix(NES_BASECYCLES, (uint32)((6 * 72 * (1 << AM_BITS)) * 3.7), 18);
apu->ym2413s.lfo[0].adrmask = w - 1;
apu->ym2413s.lfo[0].table = amtbl;
apu->ym2413s.lfo[0].output = apu->ym2413s.lfo[0].table[0];
/* VIB */
w = 1 << PM_BITS;
for (i = 0; i < w; i++)
{
a = pow(2, PM_DEPTH * sin((2 * M_PI * i) / w) / 1200.0) * (1 << PM_SHIFT);
pmtbl[i] = (uint32)a;
}
apu->ym2413s.lfo[1].cps = cps;
apu->ym2413s.lfo[1].spd = DivFix(NES_BASECYCLES, (uint32)((6 * 72 * (1 << PM_BITS)) * 6.4), 18);
apu->ym2413s.lfo[1].adrmask = w - 1;
apu->ym2413s.lfo[1].table = pmtbl;
apu->ym2413s.lfo[1].output = apu->ym2413s.lfo[1].table[0];
}
void VRC7SetTone(uint8 *p)
{
int i, j;
for (i = 1; i < 16; i++)
{
for (j = 0; j < 8; j++)
{
oplltone[i][j] = *p++;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -