sound, improve ym2612 accuracy (NB noticeably slower for low bitrates)

This commit is contained in:
kub 2021-12-23 00:42:11 +01:00
parent c3fcdf3f8d
commit d127b3f3d5
3 changed files with 361 additions and 438 deletions

View file

@ -8,7 +8,6 @@
** **
** updated with fixes from mame 0.216 (file version 1.5.1) (kub) ** updated with fixes from mame 0.216 (file version 1.5.1) (kub)
** SSG-EG readded from GenPlus (kub) ** SSG-EG readded from GenPlus (kub)
** linear sample interpolation for chip to output rate adaption (kub)
*/ */
/* /*
@ -909,7 +908,7 @@ typedef struct
UINT32 eg_timer; UINT32 eg_timer;
UINT32 eg_timer_add; UINT32 eg_timer_add;
UINT32 pack; // 4c: stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16] UINT32 pack; // 4c: stereo, lastchan, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16]
UINT32 algo; /* 50: algo[3], was_update */ UINT32 algo; /* 50: algo[3], was_update, unsued, upd_cnt[2], dac */
INT32 op1_out; INT32 op1_out;
#ifdef _MIPS_ARCH_ALLEGREX #ifdef _MIPS_ARCH_ALLEGREX
UINT32 pad1[3+8]; UINT32 pad1[3+8];
@ -921,10 +920,211 @@ typedef struct
#include <limits.h> #include <limits.h>
static int clip(int n) static int clip(int n)
{ {
unsigned b = 14, s = n < 0; unsigned b = 14, s = n < 0;
int m = s + INT_MAX; int m = s + INT_MAX;
if (s + (n>>(b-1))) n = m >> (8*sizeof(int)-b); if (s + (n>>(b-1))) n = m >> (8*sizeof(int)-b);
return n; return n;
}
static void update_ssg_eg_channel(chan_rend_context *ct)
{
FM_SLOT *SLOT;
SLOT = &ct->CH->SLOT[SLOT1];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase1 = update_ssg_eg_phase(SLOT, ct->phase1);
SLOT = &ct->CH->SLOT[SLOT2];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase2 = update_ssg_eg_phase(SLOT, ct->phase2);
SLOT = &ct->CH->SLOT[SLOT3];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase3 = update_ssg_eg_phase(SLOT, ct->phase3);
SLOT = &ct->CH->SLOT[SLOT4];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase4 = update_ssg_eg_phase(SLOT, ct->phase4);
}
static void update_eg_phase_channel(chan_rend_context *ct)
{
FM_SLOT *SLOT;
SLOT = &ct->CH->SLOT[SLOT1];
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);
SLOT = &ct->CH->SLOT[SLOT2];
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);
SLOT = &ct->CH->SLOT[SLOT3];
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);
SLOT = &ct->CH->SLOT[SLOT4];
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2);
}
static int update_algo_channel(chan_rend_context *ct, unsigned int eg_out, unsigned int eg_out2, unsigned int eg_out4)
{
int m2,c1,c2=0; /* Phase Modulation input for operators 2,3,4 */
int smp = 0;
switch( ct->algo&0x7 )
{
case 0:
{
/* M1---C1---MEM---M2---C2---OUT */
m2 = ct->mem;
c1 = ct->op1_out>>16;
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem = op_calc(ct->phase2, eg_out2, c1);
}
else ct->mem = 0;
if (ct->eg_timer >= (1<<EG_SH)) break;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 = op_calc(ct->phase3, eg_out, m2);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 1:
{
/* M1------+-MEM---M2---C2---OUT */
/* C1-+ */
m2 = ct->mem;
ct->mem = ct->op1_out>>16;
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem+= op_calc(ct->phase2, eg_out2, 0);
}
if (ct->eg_timer >= (1<<EG_SH)) break;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 = op_calc(ct->phase3, eg_out, m2);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 2:
{
/* M1-----------------+-C2---OUT */
/* C1---MEM---M2-+ */
m2 = ct->mem;
c2 = ct->op1_out>>16;
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem = op_calc(ct->phase2, eg_out2, 0);
}
else ct->mem = 0;
if (ct->eg_timer >= (1<<EG_SH)) break;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 += op_calc(ct->phase3, eg_out, m2);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 3:
{
/* M1---C1---MEM------+-C2---OUT */
/* M2-+ */
c2 = ct->mem;
c1 = ct->op1_out>>16;
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem = op_calc(ct->phase2, eg_out2, c1);
}
else ct->mem = 0;
if (ct->eg_timer >= (1<<EG_SH)) break;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 += op_calc(ct->phase3, eg_out, 0);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 4:
{
/* M1---C1-+-OUT */
/* M2---C2-+ */
/* MEM: not used */
if (ct->eg_timer >= (1<<EG_SH)) break;
c1 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 = op_calc(ct->phase3, eg_out, 0);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp = op_calc(ct->phase2, eg_out2, c1);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp+= op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 5:
{
/* +----C1----+ */
/* M1-+-MEM---M2-+-OUT */
/* +----C2----+ */
if (ct->eg_timer >= (1<<EG_SH)) break;
m2 = ct->mem;
ct->mem = c1 = c2 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
smp = op_calc(ct->phase3, eg_out, m2);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp+= op_calc(ct->phase2, eg_out2, c1);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp+= op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 6:
{
/* M1---C1-+ */
/* M2-+-OUT */
/* C2-+ */
/* MEM: not used */
if (ct->eg_timer >= (1<<EG_SH)) break;
c1 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
smp = op_calc(ct->phase3, eg_out, 0);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp+= op_calc(ct->phase2, eg_out2, c1);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp+= op_calc(ct->phase4, eg_out4, 0);
}
break;
}
case 7:
{
/* M1-+ */
/* C1-+-OUT */
/* M2-+ */
/* C2-+ */
/* MEM: not used*/
if (ct->eg_timer >= (1<<EG_SH)) break;
smp = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
smp += op_calc(ct->phase3, eg_out, 0);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp += op_calc(ct->phase2, eg_out2, 0);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp += op_calc(ct->phase4, eg_out4, 0);
}
break;
}
}
return smp;
} }
static void chan_render_loop(chan_rend_context *ct, int *buffer, int length) static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
@ -936,302 +1136,76 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
{ {
int smp = 0; /* produced sample */ int smp = 0; /* produced sample */
unsigned int eg_out, eg_out2, eg_out4; unsigned int eg_out, eg_out2, eg_out4;
FM_SLOT *SLOT;
UINT32 cnt = ct->eg_timer_add+(ct->eg_timer & ((1<<EG_SH)-1));
if (ct->pack & 2) while (cnt >= 1<<EG_SH) {
cnt -= 1<<EG_SH;
SLOT = &ct->CH->SLOT[SLOT1];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase1 = update_ssg_eg_phase(SLOT, ct->phase1);
SLOT = &ct->CH->SLOT[SLOT2];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase2 = update_ssg_eg_phase(SLOT, ct->phase2);
SLOT = &ct->CH->SLOT[SLOT3];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase3 = update_ssg_eg_phase(SLOT, ct->phase3);
SLOT = &ct->CH->SLOT[SLOT4];
if ((SLOT->ssg&0x08) && SLOT->state > EG_REL && SLOT->volume >= 0x200)
ct->phase4 = update_ssg_eg_phase(SLOT, ct->phase4);
}
if (ct->pack & 8) { /* LFO enabled ? (test Earthworm Jim in between demo 1 and 2) */
ct->pack = (ct->pack&0xffff) | (advance_lfo(ct->pack >> 16, ct->lfo_cnt, ct->lfo_cnt + ct->lfo_inc) << 16);
ct->lfo_cnt += ct->lfo_inc;
}
ct->eg_timer += ct->eg_timer_add; ct->eg_timer += ct->eg_timer_add;
if (ct->eg_timer < EG_TIMER_OVERFLOW) { while (ct->eg_timer >= 1<<EG_SH) {
SLOT = &ct->CH->SLOT[SLOT1]; ct->eg_timer -= 1<<EG_SH;
SLOT->vol_ipol = SLOT->vol_out;
if (SLOT->state > EG_REL) recalc_volout(SLOT);
SLOT = &ct->CH->SLOT[SLOT2];
SLOT->vol_ipol = SLOT->vol_out;
if (SLOT->state > EG_REL) recalc_volout(SLOT);
SLOT = &ct->CH->SLOT[SLOT3];
SLOT->vol_ipol = SLOT->vol_out;
if (SLOT->state > EG_REL) recalc_volout(SLOT);
SLOT = &ct->CH->SLOT[SLOT4];
SLOT->vol_ipol = SLOT->vol_out;
if (SLOT->state > EG_REL) recalc_volout(SLOT);
}
else while (ct->eg_timer >= EG_TIMER_OVERFLOW)
{
ct->eg_timer -= EG_TIMER_OVERFLOW;
ct->eg_cnt++;
if (ct->eg_cnt >= 4096) ct->eg_cnt = 1;
SLOT = &ct->CH->SLOT[SLOT1]; if (ct->pack & 8) { /* LFO enabled ? (test Earthworm Jim in between demo 1 and 2) */
SLOT->vol_ipol = SLOT->vol_out; ct->pack = (ct->pack&0xffff) | (advance_lfo(ct->pack >> 16, ct->lfo_cnt, ct->lfo_cnt + ct->lfo_inc) << 16);
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2); ct->lfo_cnt += ct->lfo_inc;
SLOT = &ct->CH->SLOT[SLOT2]; }
SLOT->vol_ipol = SLOT->vol_out; if (ct->pack & 2)
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2); update_ssg_eg_channel(ct);
SLOT = &ct->CH->SLOT[SLOT3];
SLOT->vol_ipol = SLOT->vol_out; if (ct->algo & 0x30)
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2); ct->algo -= 0x10;
SLOT = &ct->CH->SLOT[SLOT4]; if (!(ct->algo & 0x30)) {
SLOT->vol_ipol = SLOT->vol_out; ct->algo |= 0x30;
if (SLOT->state != EG_OFF) update_eg_phase(SLOT, ct->eg_cnt, ct->pack & 2); ct->eg_cnt++;
} if (ct->eg_cnt >= 4096) ct->eg_cnt = 1;
update_eg_phase_channel(ct);
}
#if 0
UINT32 ifrac0 = ct->eg_timer / (EG_TIMER_OVERFLOW>>EG_SH);
UINT32 ifrac1 = (1<<EG_SH) - ifrac0;
SLOT = &ct->CH->SLOT[SLOT1];
ct->vol_out1 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
SLOT = &ct->CH->SLOT[SLOT2];
ct->vol_out2 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
SLOT = &ct->CH->SLOT[SLOT3];
ct->vol_out3 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
SLOT = &ct->CH->SLOT[SLOT4];
ct->vol_out4 = (SLOT->vol_ipol*ifrac1 + SLOT->vol_out*ifrac0) >> EG_SH;
#elif 1
switch (ct->eg_timer >> EG_SH)
{
case 0:
ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_ipol;
ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_ipol;
ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_ipol;
ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_ipol;
break;
case (EG_TIMER_OVERFLOW>>EG_SH)-1:
ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_out;
ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_out;
ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_out;
ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_out;
break;
default:
ct->vol_out1 = (ct->CH->SLOT[SLOT1].vol_ipol +
ct->CH->SLOT[SLOT1].vol_out) >> 1;
ct->vol_out2 = (ct->CH->SLOT[SLOT2].vol_ipol +
ct->CH->SLOT[SLOT2].vol_out) >> 1;
ct->vol_out3 = (ct->CH->SLOT[SLOT3].vol_ipol +
ct->CH->SLOT[SLOT3].vol_out) >> 1;
ct->vol_out4 = (ct->CH->SLOT[SLOT4].vol_ipol +
ct->CH->SLOT[SLOT4].vol_out) >> 1;
break;
}
#elif 0
if (ct->eg_timer >> (EG_SH-1) < EG_TIMER_OVERFLOW >> EG_SH) {
ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_ipol;
ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_ipol;
ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_ipol;
ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_ipol;
} else {
ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_out; ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_out;
ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_out; ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_out;
ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_out; ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_out;
ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_out; ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_out;
if (ct->pack & 4) goto disabled; /* output disabled */
/* calculate channel sample */
if (ct->eg_timer < (2<<EG_SH) || (ct->pack&0xf000)) {
eg_out = ct->vol_out1;
if ( (ct->pack & 8) && (ct->pack&(1<<(SLOT1+8))) )
eg_out += ct->pack >> (((ct->pack&0xc0)>>6)+24);
if( eg_out < ENV_QUIET ) /* SLOT 1 */
{
int out = 0;
if (ct->pack&0xf000) out = ((ct->op1_out>>16) + ((ct->op1_out<<16)>>16)) << ((ct->pack&0xf000)>>12); /* op1_out0 + op1_out1 */
ct->op1_out <<= 16;
ct->op1_out |= (unsigned short)op_calc1(ct->phase1, eg_out, out);
} else {
ct->op1_out <<= 16; /* op1_out0 = op1_out1; op1_out1 = 0; */
}
}
if (ct->eg_timer < (2<<EG_SH)) {
eg_out = ct->vol_out3; // volume_calc(&CH->SLOT[SLOT3]);
eg_out2 = ct->vol_out2; // volume_calc(&CH->SLOT[SLOT2]);
eg_out4 = ct->vol_out4; // volume_calc(&CH->SLOT[SLOT4]);
if (ct->pack & 8) {
unsigned int add = ct->pack >> (((ct->pack&0xc0)>>6)+24);
if (ct->pack & (1<<(SLOT3+8))) eg_out += add;
if (ct->pack & (1<<(SLOT2+8))) eg_out2 += add;
if (ct->pack & (1<<(SLOT4+8))) eg_out4 += add;
}
smp = update_algo_channel(ct, eg_out, eg_out2, eg_out4);
}
/* done calculating channel sample */
disabled:
/* update phase counters AFTER output calculations */
ct->phase1 += ct->incr1;
ct->phase2 += ct->incr2;
ct->phase3 += ct->incr3;
ct->phase4 += ct->incr4;
} }
#else
ct->vol_out1 = ct->CH->SLOT[SLOT1].vol_out;
ct->vol_out2 = ct->CH->SLOT[SLOT2].vol_out;
ct->vol_out3 = ct->CH->SLOT[SLOT3].vol_out;
ct->vol_out4 = ct->CH->SLOT[SLOT4].vol_out;
#endif
if (ct->pack & 4) continue; /* output disabled */
/* calculate channel sample */
eg_out = ct->vol_out1;
if ( (ct->pack & 8) && (ct->pack&(1<<(SLOT1+8))) ) eg_out += ct->pack >> (((ct->pack&0xc0)>>6)+24);
if( eg_out < ENV_QUIET ) /* SLOT 1 */
{
int out = 0;
if (ct->pack&0xf000) out = ((ct->op1_out>>16) + ((ct->op1_out<<16)>>16)) << ((ct->pack&0xf000)>>12); /* op1_out0 + op1_out1 */
ct->op1_out <<= 16;
ct->op1_out |= (unsigned short)op_calc1(ct->phase1, eg_out, out);
} else {
ct->op1_out <<= 16; /* op1_out0 = op1_out1; op1_out1 = 0; */
}
eg_out = ct->vol_out3; // volume_calc(&CH->SLOT[SLOT3]);
eg_out2 = ct->vol_out2; // volume_calc(&CH->SLOT[SLOT2]);
eg_out4 = ct->vol_out4; // volume_calc(&CH->SLOT[SLOT4]);
if (ct->pack & 8) {
unsigned int add = ct->pack >> (((ct->pack&0xc0)>>6)+24);
if (ct->pack & (1<<(SLOT3+8))) eg_out += add;
if (ct->pack & (1<<(SLOT2+8))) eg_out2 += add;
if (ct->pack & (1<<(SLOT4+8))) eg_out4 += add;
}
switch( ct->algo&0x7 )
{
case 0:
{
/* M1---C1---MEM---M2---C2---OUT */
int m2,c1,c2=0; /* Phase Modulation input for operators 2,3,4 */
m2 = ct->mem;
c1 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 = op_calc(ct->phase3, eg_out, m2);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem = op_calc(ct->phase2, eg_out2, c1);
}
else ct->mem = 0;
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 1:
{
/* M1------+-MEM---M2---C2---OUT */
/* C1-+ */
int m2,c2=0;
m2 = ct->mem;
ct->mem = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 = op_calc(ct->phase3, eg_out, m2);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem+= op_calc(ct->phase2, eg_out2, 0);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 2:
{
/* M1-----------------+-C2---OUT */
/* C1---MEM---M2-+ */
int m2,c2;
m2 = ct->mem;
c2 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 += op_calc(ct->phase3, eg_out, m2);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem = op_calc(ct->phase2, eg_out2, 0);
}
else ct->mem = 0;
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 3:
{
/* M1---C1---MEM------+-C2---OUT */
/* M2-+ */
int c1,c2;
c2 = ct->mem;
c1 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 += op_calc(ct->phase3, eg_out, 0);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
ct->mem = op_calc(ct->phase2, eg_out2, c1);
}
else ct->mem = 0;
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp = op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 4:
{
/* M1---C1-+-OUT */
/* M2---C2-+ */
/* MEM: not used */
int c1,c2=0;
c1 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
c2 = op_calc(ct->phase3, eg_out, 0);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp = op_calc(ct->phase2, eg_out2, c1);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp+= op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 5:
{
/* +----C1----+ */
/* M1-+-MEM---M2-+-OUT */
/* +----C2----+ */
int m2,c1,c2;
m2 = ct->mem;
ct->mem = c1 = c2 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
smp = op_calc(ct->phase3, eg_out, m2);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp+= op_calc(ct->phase2, eg_out2, c1);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp+= op_calc(ct->phase4, eg_out4, c2);
}
break;
}
case 6:
{
/* M1---C1-+ */
/* M2-+-OUT */
/* C2-+ */
/* MEM: not used */
int c1;
c1 = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
smp = op_calc(ct->phase3, eg_out, 0);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp+= op_calc(ct->phase2, eg_out2, c1);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp+= op_calc(ct->phase4, eg_out4, 0);
}
break;
}
case 7:
{
/* M1-+ */
/* C1-+-OUT */
/* M2-+ */
/* C2-+ */
/* MEM: not used*/
smp = ct->op1_out>>16;
if( eg_out < ENV_QUIET ) { /* SLOT 3 */
smp += op_calc(ct->phase3, eg_out, 0);
}
if( eg_out2 < ENV_QUIET ) { /* SLOT 2 */
smp += op_calc(ct->phase2, eg_out2, 0);
}
if( eg_out4 < ENV_QUIET ) { /* SLOT 4 */
smp += op_calc(ct->phase4, eg_out4, 0);
}
break;
}
}
/* done calculating channel sample */
/* mix sample to output buffer */ /* mix sample to output buffer */
if (smp) { if (smp) {
@ -1250,12 +1224,6 @@ static void chan_render_loop(chan_rend_context *ct, int *buffer, int length)
} }
ct->algo |= 8; ct->algo |= 8;
} }
/* update phase counters AFTER output calculations */
ct->phase1 += ct->incr1;
ct->phase2 += ct->incr2;
ct->phase3 += ct->incr3;
ct->phase4 += ct->incr4;
} }
} }
#else #else
@ -1335,6 +1303,7 @@ static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: s
crct.op1_out = crct.CH->op1_out; crct.op1_out = crct.CH->op1_out;
crct.algo = crct.CH->ALGO & 7; crct.algo = crct.CH->ALGO & 7;
crct.algo |= crct.CH->upd_cnt << 4;
if (ym2612.OPN.ST.flags & ST_DAC) if (ym2612.OPN.ST.flags & ST_DAC)
crct.algo |= 0x80; crct.algo |= 0x80;
@ -1373,6 +1342,7 @@ static int chan_render(int *buffer, int length, int c, UINT32 flags) // flags: s
} }
else else
ym2612.slot_mask &= ~(0xf << (c*4)); ym2612.slot_mask &= ~(0xf << (c*4));
crct.CH->upd_cnt = (crct.algo >> 4) & 0x7;
return (crct.algo & 8) >> 3; // had output return (crct.algo & 8) >> 3; // had output
} }
@ -1625,9 +1595,10 @@ static void OPNSetPres(int pres)
int i; int i;
/* frequency base */ /* frequency base */
ym2612.OPN.ST.freqbase = (ym2612.OPN.ST.rate) ? ((double)ym2612.OPN.ST.clock / ym2612.OPN.ST.rate) / pres : 0; double freqbase = (ym2612.OPN.ST.rate) ? ((double)ym2612.OPN.ST.clock / ym2612.OPN.ST.rate) / pres : 0;
ym2612.OPN.eg_timer_add = (1<<EG_SH) * ym2612.OPN.ST.freqbase; ym2612.OPN.eg_timer_add = (1<<EG_SH) * freqbase;
ym2612.OPN.ST.freqbase = 1.0; // freqbase
/* make time tables */ /* make time tables */
init_timetables( dt_tab ); init_timetables( dt_tab );

View file

@ -59,7 +59,7 @@ typedef struct
UINT8 ssgn; UINT8 ssgn;
UINT16 ar_ksr; /* 0x32 ar+ksr */ UINT16 ar_ksr; /* 0x32 ar+ksr */
UINT16 vol_out; /* 0x34 current output from EG (without LFO) */ UINT16 vol_out; /* 0x34 current output from EG (without LFO) */
UINT16 vol_ipol; /* 0x36 interpolator memory */ UINT16 pad;
} FM_SLOT; } FM_SLOT;
@ -79,7 +79,7 @@ typedef struct
UINT8 kcode; /* +11 key code: */ UINT8 kcode; /* +11 key code: */
UINT8 fn_h; /* freq latch */ UINT8 fn_h; /* freq latch */
UINT8 pad2; UINT8 upd_cnt; /* eg update counter */
UINT32 fc; /* fnum,blk:adjusted to sample rate */ UINT32 fc; /* fnum,blk:adjusted to sample rate */
UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */

View file

@ -16,7 +16,6 @@
#include <pico/arm_features.h> #include <pico/arm_features.h>
@ very simple YM2612 output rate to sample rate adaption (~500k cycles @44100) @ very simple YM2612 output rate to sample rate adaption (~500k cycles @44100)
#define INTERPOL
#define SSG_EG #define SSG_EG
.equiv SLOT1, 0 .equiv SLOT1, 0
@ -34,7 +33,6 @@
.equiv EG_OFF, 0 .equiv EG_OFF, 0
.equiv EG_SH, 16 @ 16.16 fixed point (envelope generator timing) .equiv EG_SH, 16 @ 16.16 fixed point (envelope generator timing)
.equiv EG_TIMER_OVERFLOW, (3*(1<<EG_SH)) @ envelope generator timer overflows every 3 samples (on real chip)
.equiv LFO_SH, 24 /* 8.24 fixed point (LFO calculations) */ .equiv LFO_SH, 24 /* 8.24 fixed point (LFO calculations) */
.equiv ENV_QUIET, (2*13*256/8) .equiv ENV_QUIET, (2*13*256/8)
@ -46,14 +44,8 @@
@ r5=slot, r1=eg_cnt, trashes: r0,r2,r3 @ r5=slot, r1=eg_cnt, trashes: r0,r2,r3
@ writes output to routp, but only if vol_out changes @ writes output to routp, but only if vol_out changes
.macro update_eg_phase_slot .macro update_eg_phase_slot
#if defined(INTERPOL)
ldrh r0, [r5,#0x34] @ vol_out
#endif
ldrb r2, [r5,#0x17] @ state ldrb r2, [r5,#0x17] @ state
add r3, r5, #0x1c add r3, r5, #0x1c
#if defined(INTERPOL)
strh r0, [r5,#0x36] @ vol_ipol
#endif
tst r2, r2 tst r2, r2
beq 0f @ EG_OFF beq 0f @ EG_OFF
@ -211,7 +203,6 @@
cmp r2, #EG_REL+1 @ state > EG_REL && cmp r2, #EG_REL+1 @ state > EG_REL &&
cmpge r3, #0x200 @ volume >= 0x200? cmpge r3, #0x200 @ volume >= 0x200?
blt 9f blt 9f
orr r4, r4, #0x10 @ ssg_update
tst r0, #0x01 tst r0, #0x01
beq 1f beq 1f
@ -259,14 +250,8 @@
@ r5=slot, trashes: r0,r2,r3 @ r5=slot, trashes: r0,r2,r3
.macro recalc_volout .macro recalc_volout
#if defined(INTERPOL)
ldrh r0, [r5,#0x34] @ vol_out
#endif
ldrb r2, [r5,#0x30] @ ssg ldrb r2, [r5,#0x30] @ ssg
ldrb r3, [r5,#0x17] @ state ldrb r3, [r5,#0x17] @ state
#if defined(INTERPOL)
strh r0, [r5,#0x36] @ vol_ipol
#endif
ldrh r0, [r5,#0x1a] @ volume ldrh r0, [r5,#0x1a] @ volume
@ and r2, r2, #0x0c @ and r2, r2, #0x0c
@ -342,6 +327,9 @@
@ r0-r2=scratch, r3=sin_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out @ r0-r2=scratch, r3=sin_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out
.macro upd_algo0_m .macro upd_algo0_m
cmp r8, #(1<<EG_SH)
bge 1f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -383,6 +371,9 @@
.macro upd_algo1_m .macro upd_algo1_m
cmp r8, #(1<<EG_SH)
bge 1f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -423,6 +414,9 @@
.macro upd_algo2_m .macro upd_algo2_m
cmp r8, #(1<<EG_SH)
bge 1f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -464,6 +458,9 @@
.macro upd_algo3_m .macro upd_algo3_m
cmp r8, #(1<<EG_SH)
bge 1f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -505,6 +502,9 @@
.macro upd_algo4_m .macro upd_algo4_m
cmp r8, #(1<<EG_SH)
bge 2f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -542,6 +542,9 @@
.macro upd_algo5_m .macro upd_algo5_m
cmp r8, #(1<<EG_SH)
bge 2f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -582,6 +585,9 @@
.macro upd_algo6_m .macro upd_algo6_m
cmp r8, #(1<<EG_SH)
bge 2f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -617,6 +623,9 @@
.macro upd_algo7_m .macro upd_algo7_m
cmp r8, #(1<<EG_SH)
bge 2f
@ SLOT3 @ SLOT3
make_eg_out SLOT3 make_eg_out SLOT3
cmp r1, #ENV_QUIET cmp r1, #ENV_QUIET
@ -677,7 +686,7 @@
@ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16]) @ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
@ r0-r2=scratch, r3=sin_tab/scratch, r4=(length<<8)|dac,unused[2],ssg_update,was_update,algo[3], r5=tl_tab/slot, @ r0-r2=scratch, r3=sin_tab/scratch, r4=(length<<8)|dac,upd_cnt[3],was_update,algo[3], r5=tl_tab/slot,
@ r6-r7=vol_out[4], r8=eg_timer, r9=eg_timer_add[31:16], r10=op1_out, r11=buffer @ r6-r7=vol_out[4], r8=eg_timer, r9=eg_timer_add[31:16], r10=op1_out, r11=buffer
.global chan_render_loop @ chan_rend_context *ct, int *buffer, int length .global chan_render_loop @ chan_rend_context *ct, int *buffer, int length
@ -688,47 +697,48 @@ chan_render_loop:
ldr r12, [lr, #0x4c] ldr r12, [lr, #0x4c]
ldr r0, [lr, #0x50] ldr r0, [lr, #0x50]
mov r11, r1 mov r11, r1
and r0, r0, #0x87 and r0, r0, #0xf7
orr r4, r4, r0 @ (length<<8)|dac,unused[4],algo[3] orr r4, r4, r0 @ (length<<8)|dac,upd_cnt[2],unused,algo[3]
ldr r8, [lr, #0x44] @ eg_timer ldr r8, [lr, #0x44] @ eg_timer
ldr r9, [lr, #0x48] @ eg_timer_add ldr r9, [lr, #0x48] @ eg_timer_add
ldr r10, [lr, #0x54] @ op1_out ldr r10, [lr, #0x54] @ op1_out
tst r12, #8 @ lfo? crl_loop:
beq crl_loop
crl_loop_lfo:
ldr r1, [lr, #0x30] @ lfo_cnt
ldr r2, [lr, #0x34] @ lfo_inc
subs r4, r4, #0x100 subs r4, r4, #0x100
bmi crl_loop_end bmi crl_loop_end
mov r0, #0
add r8, r8, r9
subs r8, r8, #(1<<EG_SH)
blt crl_smp_loop_end
@ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt
advance_lfo_m
crl_smp_loop:
tst r12, #8 @ lfo?
beq lfo_done
ldr r2, [lr, #0x34] @ lfo_inc
ldr r1, [lr, #0x30] @ lfo_cnt
add r2, r2, r1 add r2, r2, r1
str r2, [lr, #0x30] str r2, [lr, #0x30]
@ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt @ r12=lfo_ampm[31:16], r1=lfo_cnt_old, r2=lfo_cnt
advance_lfo_m advance_lfo_m
add r4, r4, #0x100 lfo_done:
crl_loop:
subs r4, r4, #0x100
bmi crl_loop_end
ldr r5, [lr, #0x40] @ CH ldr r5, [lr, #0x40] @ CH
#if defined(SSG_EG) #if defined(SSG_EG)
tst r12, #0x02 @ ssg_enabled? tst r12, #0x02 @ ssg_enabled?
beq ssg_done beq ssg_done
@ -- SSG --
lsl r7, r8, #EG_SH
add r7, r9, r7, lsr #EG_SH
subs r7, r7, #1<<EG_SH
blt ssg_done
@ -- SSG --
ssg_loop: ssg_loop:
mov r6, #4 mov r6, #4
bic r4, r4, #0x10 @ ssg_update
ssg_upd_loop: ssg_upd_loop:
@ use lr as a pointer to the slot phases stored in the context @ use lr as a pointer to the slot phases stored in the context
update_ssg_eg update_ssg_eg
@ -748,26 +758,25 @@ ssg_upd_loop:
sub lr, lr, #4*3 sub lr, lr, #4*3
sub r5, r5, #SLOT_STRUCT_SIZE*3 sub r5, r5, #SLOT_STRUCT_SIZE*3
subs r7, r7, #1<<EG_SH
bge ssg_loop
ssg_done: ssg_done:
#endif #endif
@ -- EG -- @ -- EG --
add r8, r8, r9 tst r4, #0x30
cmp r8, #EG_TIMER_OVERFLOW subnes r4, r4, #0x10
blo volout_upd bne eg_done
ldr r1, [lr, #0x3c] @ eg_cnt orr r4, r4, #0x30
eg_loop:
sub r8, r8, #EG_TIMER_OVERFLOW ldr r1, [lr, #0x3c] @ eg_cnt
add r1, r1, #1 add r1, r1, #1
cmp r1, #4096 cmp r1, #4096
movge r1, #1 movge r1, #1
str r1, [lr, #0x3c]
mov r6, #4 mov r6, #4
eg_upd_loop: eg_upd_loop:
update_eg_phase_slot update_eg_phase_slot
#if 1 #if 0
subs r6, r6, #1 subs r6, r6, #1
addne r5, r5, #SLOT_STRUCT_SIZE addne r5, r5, #SLOT_STRUCT_SIZE
#else #else
@ -777,96 +786,38 @@ eg_upd_loop:
subne r5, r5, #SLOT_STRUCT_SIZE subne r5, r5, #SLOT_STRUCT_SIZE
#endif #endif
bne eg_upd_loop bne eg_upd_loop
cmp r8, #EG_TIMER_OVERFLOW
sub r5, r5, #SLOT_STRUCT_SIZE*3 sub r5, r5, #SLOT_STRUCT_SIZE*3
bhs eg_loop
str r1, [lr, #0x3c]
b eg_done
volout_upd:
#if defined(SSG_EG)
tst r4, #0x10 @ ssg_update?
beq eg_done
@ recalc vol_out
mov r6, #4
volout_loop:
recalc_volout
#if 0
subs r6, r6, #1
addne r5, r5, #SLOT_STRUCT_SIZE
#else
add r5, r5, #SLOT_STRUCT_SIZE*2
recalc_volout
subs r6, r6, #2
subne r5, r5, #SLOT_STRUCT_SIZE
#endif
bne volout_loop
sub r5, r5, #SLOT_STRUCT_SIZE*3
#endif
eg_done: eg_done:
@ -- disabled? -- @ -- disabled? --
and r0, r12, #0xC tst r12, #0x4
cmp r0, #0xC mov r0, #0
beq crl_loop_lfo bne crl_algo_done
cmp r0, #0x4
beq crl_loop
@ output interpolation cmp r8, #(2<<EG_SH) @ calculate only for operator memory, sample,
#if defined(INTERPOL) tstge r12, #0xf000 @ ...feedback
#if 1 // possibly too expensive for slow platforms? beq crl_algo_done
@ basic interpolator, interpolate in middle region, else use closer value
mov r3, r8, lsr #EG_SH @ eg_timer, [0..3<<EG_SH) after loop
cmp r3, #(EG_TIMER_OVERFLOW>>EG_SH)/2
bne 0f @ mix is vol_out
ldr r6, [r5, #0x34] @ vol_out, vol_ipol for all slots
ldr r2, [r5, #0x34+SLOT_STRUCT_SIZE*2]
ldr r7, [r5, #0x34+SLOT_STRUCT_SIZE]
ldr r3, [r5, #0x34+SLOT_STRUCT_SIZE*3]
add r6, r6, r6, lsl #16
lsr r6, r6, #17
add r2, r2, r2, lsl #16
lsr r2, r2, #17
add r7, r7, r7, lsl #16
lsr r7, r7, #17
add r3, r3, r3, lsl #16
lsr r3, r3, #17
b 1f
#else
@ super-basic... just take value closest to sample point
mov r3, r8, lsr #EG_SH-1 @ eg_timer, [0..3<<EG_SH) after loop
cmp r3, #(EG_TIMER_OVERFLOW>>EG_SH)
#endif
0: ldrgeh r6, [r5, #0x34] @ vol_out values for all slots
ldrlth r6, [r5, #0x36] @ vol_ipol values for all slots
ldrgeh r2, [r5, #0x34+SLOT_STRUCT_SIZE*2]
ldrlth r2, [r5, #0x36+SLOT_STRUCT_SIZE*2]
ldrgeh r7, [r5, #0x34+SLOT_STRUCT_SIZE]
ldrlth r7, [r5, #0x36+SLOT_STRUCT_SIZE]
ldrgeh r3, [r5, #0x34+SLOT_STRUCT_SIZE*3]
ldrlth r3, [r5, #0x36+SLOT_STRUCT_SIZE*3]
#else
ldrh r6, [r5, #0x34] @ vol_out values for all slots ldrh r6, [r5, #0x34] @ vol_out values for all slots
ldrh r2, [r5, #0x34+SLOT_STRUCT_SIZE*2] ldrh r2, [r5, #0x34+SLOT_STRUCT_SIZE*2]
ldrh r7, [r5, #0x34+SLOT_STRUCT_SIZE] ldrh r7, [r5, #0x34+SLOT_STRUCT_SIZE]
ldrh r3, [r5, #0x34+SLOT_STRUCT_SIZE*3] ldrh r3, [r5, #0x34+SLOT_STRUCT_SIZE*3]
#endif
1: orr r6, r6, r2, lsl #16 orr r6, r6, r2, lsl #16
orr r7, r7, r3, lsl #16 orr r7, r7, r3, lsl #16
@ -- SLOT1 --
PIC_LDR(r3, r2, ym_tl_tab) PIC_LDR(r3, r2, ym_tl_tab)
@ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16]) @ lr=context, r12=pack (stereo, ssg_enabled, disabled, lfo_enabled | pan_r, pan_l, ams[2] | AMmasks[4] | FB[4] | lfo_ampm[16])
@ r0-r2=scratch, r3=tl_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out @ r0-r2=scratch, r3=tl_tab, r5=scratch, r6-r7=vol_out[4], r10=op1_out
@ -- SLOT1 --
upd_slot1_m upd_slot1_m
@ -- SLOT2+ -- @ -- SLOT2+ --
cmp r8, #(2<<EG_SH) @ op mem or sample?
bge crl_algo_done
and r0, r4, #7 and r0, r4, #7
PIC_XB(,r0, lsl #2) PIC_XB(,r0, lsl #2)
nop nop
@ -920,6 +871,23 @@ crl_algo7:
crl_algo_done: crl_algo_done:
@ -- PHASE UPDATE --
add lr, lr, #0x10
ldmia lr, {r1-r3,r5-r7}
add r1, r1, r6
add r2, r2, r7
ldr r6, [lr, #0x18]
ldr r7, [lr, #0x1c]
add r3, r3, r6
add r5, r5, r7
stmia lr, {r1-r3,r5}
sub lr, lr, #0x10
subs r8, r8, #(1<<EG_SH)
bge crl_smp_loop
crl_smp_loop_end:
add r8, r8, #(1<<EG_SH)
@ -- WRITE SAMPLE -- @ -- WRITE SAMPLE --
tst r0, r0 tst r0, r0
beq ctl_sample_skip beq ctl_sample_skip
@ -944,36 +912,20 @@ crl_algo_done:
addeq r11, r11, #4 addeq r11, r11, #4
addne r1, r0, r1 addne r1, r0, r1
strne r1, [r11], #4 strne r1, [r11], #4
b crl_do_phase b crl_loop
ctl_sample_mono: ctl_sample_mono:
ldr r1, [r11] ldr r1, [r11]
add r1, r0, r1 add r1, r0, r1
str r1, [r11], #4 str r1, [r11], #4
b crl_do_phase b crl_loop
ctl_sample_skip: ctl_sample_skip:
and r1, r12, #1 and r1, r12, #1
add r1, r1, #1 add r1, r1, #1
add r11,r11, r1, lsl #2 add r11,r11, r1, lsl #2
crl_do_phase:
@ -- PHASE UPDATE --
add r5, lr, #0x10
ldmia r5, {r0-r3,r6-r7}
add r0, r0, r6
add r1, r1, r7
ldr r6, [r5, #0x18]
ldr r7, [r5, #0x1c]
add r2, r2, r6
add r3, r3, r7
stmia r5, {r0-r3}
tst r12, #8
bne crl_loop_lfo
b crl_loop b crl_loop
crl_loop_end: crl_loop_end:
str r8, [lr, #0x44] @ eg_timer str r8, [lr, #0x44] @ eg_timer
str r12, [lr, #0x4c] @ pack (for lfo_ampm) str r12, [lr, #0x4c] @ pack (for lfo_ampm)