sound: rework ym2612 phase increment

Phase increment is now calculated before scaling to target sample rate
using the right bit count to correctly handle underflow from detune.
Few more benefits:
* several table lookups replaced with single 32bit mul
* fixed incr not being recalculated on 3slot mode toggle
* fixed wrong keycode being used in 3slot mode + lfo
This commit is contained in:
notaz authored and irixxxx committed 2025-08-06 00:05:52 +02:00
1 parent 9e304a66eb
commit 3cd193ed1c
3 files changed
+103 -139

No files matched your search

+3 -8
View File
@@ -1431,7 +1431,8 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
elprintf(EL_YMTIMER, "st mode %02x", d);
ym2612_sync_timers(cycles, old_mode, d);
ym2612.OPN.ST.mode = d;
if ((d ^ old_mode) & 0xc0)
break;
/* reset Timer a flag */
if (d & 0x10)
@@ -1441,13 +1442,7 @@ static int ym2612_write_local(u32 a, u32 d, int is_from_z80)
if (d & 0x20)
ym2612.OPN.ST.status &= ~2;
if ((d ^ old_mode) & 0xc0) {
#ifdef __GP2X__
if (PicoIn.opt & POPT_EXT_FM) return YM2612Write_940(a, d, get_scanline(is_from_z80));
#endif
PsndDoFM(cycles);
return 1;
}
ym2612.OPN.ST.mode = d;
return 0;
}
case 0x2a: { /* DAC data */
+91 -122
View File
@@ -152,6 +152,7 @@ void memset32(void *dest, int c, int count);
/* globals */
#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
#define FREQBASE_SH 9 /* Q9 fixed point */
#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
@@ -364,20 +365,25 @@ O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
};
#undef O
static const UINT8 dt_tab[4 * 32]={
static const INT8 dt_tab[4][32] = {
/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/
{
/* 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,
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,
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,
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
}
};
@@ -509,10 +515,6 @@ static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8
/* all 128 LFO PM waveforms */
static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers
but LFO works with one more bit of a precision so we really need 4096 elements */
static UINT32 fn_table[4096]; /* fnumber->increment counter */
/* register number to channel number , slot offset */
#define OPN_CHAN(N) (N&3)
#define OPN_SLOT(N) ((N>>2)&3)
@@ -527,6 +529,9 @@ static UINT32 fn_table[4096]; /* fnumber->increment counter */
/* OPN Mode Register Write */
static INLINE void set_timers( int v )
{
if ((ym2612.OPN.ST.mode ^ v) & 0xc0)
ym2612.CH[2].SLOT[SLOT1].Incr = -1;
/* b7 = CSM MODE */
/* b6 = 3 slot mode */
/* b5 = reset b */
@@ -600,7 +605,7 @@ static INLINE void FM_KEYOFF(int c , int s )
static INLINE void set_det_mul(FM_CH *CH, FM_SLOT *SLOT, int v)
{
SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1;
SLOT->DT = ym2612.OPN.ST.dt_tab[(v>>4)&7];
SLOT->detune = (v>>4)&7;
CH->SLOT[SLOT1].Incr=-1;
}
@@ -690,7 +695,7 @@ static INLINE void set_sl_rr(FM_SLOT *SLOT, int v)
static INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
{
int ret, sin = (phase>>16) + (pm>>1);
int ret, sin = (phase >> FREQ_SH) + (pm >> 1);
int neg = sin & 0x200;
if (sin & 0x100) sin ^= 0xff;
sin&=0xff;
@@ -707,7 +712,7 @@ static INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
static INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm)
{
int ret, sin = (phase+pm)>>16;
int ret, sin = (phase + pm) >> FREQ_SH;
int neg = sin & 0x200;
if (sin & 0x100) sin ^= 0xff;
sin&=0xff;
@@ -1237,13 +1242,28 @@ static void chan_render_finish(s32 *buffer, int length, int active_chans)
ym2612.OPN.lfo_cnt += ym2612.OPN.lfo_inc * length;
}
static UINT32 update_lfo_phase(const FM_SLOT *SLOT, UINT32 block_fnum)
/* calculate the 20bit (Q10) phase increment as in the real chip */
static INLINE UINT32 get_phase_increment(FM_SLOT *SLOT, UINT32 fnum12, UINT32 blk, UINT32 kcode)
{
UINT32 fnum_lfo;
UINT32 inc;
int detune = dt_tab[SLOT->detune & 3][kcode];
if (SLOT->detune & 4)
detune = -detune;
inc = (fnum12 << blk) >> 2;
inc += detune;
SLOT->detune_overflow = inc >> 31;
inc &= (1u << 17) - 1;
inc = inc * SLOT->mul >> 1;
inc &= (1u << 20) - 1;
return inc;
}
static UINT32 update_lfo_phase(FM_SLOT *SLOT, UINT32 freqbase, UINT32 block_fnum, UINT32 kcode)
{
UINT32 fnum_lfo, incr;
INT32 lfo_fn_table_index_offset;
UINT8 blk;
UINT32 fn;
int fc,fdt;
fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + crct.CH->pms + ((crct.pack>>16)&0xff) ];
@@ -1253,19 +1273,15 @@ static UINT32 update_lfo_phase(const FM_SLOT *SLOT, UINT32 block_fnum)
blk = (block_fnum&0x7000) >> 12;
fn = block_fnum & 0xfff;
/* phase increment counter */
fc = (fn_table[fn]>>(7-blk));
fdt = fc + SLOT->DT[crct.CH->kcode];
if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
return (fdt * SLOT->mul) >> 1;
incr = get_phase_increment(SLOT, fn, blk, kcode);
return incr * freqbase >> (FREQBASE_SH - (FREQ_SH-10));
} else
return SLOT->Incr;
}
static int chan_render(s32 *buffer, int length, int c, UINT32 flags) // flags: stereo, ?, disabled, ?, pan_r, pan_l
{
UINT32 freqbase = ym2612.OPN.ST.freqbase_ui;
crct.CH = &ym2612.CH[c];
crct.mem = crct.CH->mem_value; /* one sample delay memory */
crct.lfo_cnt = ym2612.OPN.lfo_cnt;
@@ -1298,19 +1314,21 @@ static int chan_render(s32 *buffer, int length, int c, UINT32 flags) // flags: s
if (ym2612.OPN.ST.flags & ST_DAC)
crct.algo |= 0x80;
if(crct.CH->pms && (ym2612.OPN.ST.mode & 0xC0) && c == 2) {
/* 3 slot mode */
crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], ym2612.OPN.SL3.block_fnum[1]);
crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], ym2612.OPN.SL3.block_fnum[2]);
crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], ym2612.OPN.SL3.block_fnum[0]);
crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);
}
else if(crct.CH->pms)
{
crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], crct.CH->block_fnum);
crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], crct.CH->block_fnum);
crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], crct.CH->block_fnum);
crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], crct.CH->block_fnum);
if (crct.CH->pms) {
UINT32 block_fnum = crct.CH->block_fnum, kcode = crct.CH->kcode;
if ((ym2612.OPN.ST.mode & 0xC0) && c == 2) {
/* 3 slot mode */
const FM_3SLOT *SL3 = &ym2612.OPN.SL3;
crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], freqbase, SL3->block_fnum[1], SL3->kcode[1]);
crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], freqbase, SL3->block_fnum[2], SL3->kcode[2]);
crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], freqbase, SL3->block_fnum[0], SL3->kcode[0]);
}
else {
crct.incr1 = update_lfo_phase(&crct.CH->SLOT[SLOT1], freqbase, block_fnum, kcode);
crct.incr2 = update_lfo_phase(&crct.CH->SLOT[SLOT2], freqbase, block_fnum, kcode);
crct.incr3 = update_lfo_phase(&crct.CH->SLOT[SLOT3], freqbase, block_fnum, kcode);
}
crct.incr4 = update_lfo_phase(&crct.CH->SLOT[SLOT4], freqbase, block_fnum, kcode);
}
else /* no LFO phase modulation */
{
@@ -1339,16 +1357,16 @@ static int chan_render(s32 *buffer, int length, int c, UINT32 flags) // flags: s
}
/* update phase increment and envelope generator */
static INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT, int fc, int kc)
static void refresh_incr_eg_slot(FM_SLOT *SLOT, UINT32 freqbase, UINT32 fnum, UINT32 kc)
{
int ksr, fdt;
int ksr;
/* (frequency) phase increment counter */
fdt = fc+SLOT->DT[kc];
/* detect overflow */
// if (fdt < 0) fdt += fn_table[0x7ff*2] >> (7-blk-1);
if (fdt < 0) fdt += fn_table[0x7ff*2] >> 2;
SLOT->Incr = fdt*SLOT->mul >> 1;
SLOT->Incr = get_phase_increment(SLOT, fnum << 1, kc >> 2, kc);
if (SLOT->detune_overflow)
// always force the native rate phase increment, seems to sound a bit better when w/o filter
SLOT->Incr <<= FREQ_SH-10;
else
SLOT->Incr = SLOT->Incr * freqbase >> (FREQBASE_SH - (FREQ_SH-10));
ksr = kc >> SLOT->KSR;
if( SLOT->ksr != ksr )
@@ -1389,46 +1407,30 @@ static INLINE void refresh_fc_eg_slot(FM_SLOT *SLOT, int fc, int kc)
}
/* update phase increment counters */
static INLINE void refresh_fc_eg_chan(FM_CH *CH)
static INLINE void refresh_fc_eg_chan(FM_CH *CH, UINT32 freqbase)
{
if( CH->SLOT[SLOT1].Incr==-1){
int fc = CH->fc;
int kc = CH->kcode;
refresh_fc_eg_slot(&CH->SLOT[SLOT1] , fc , kc );
refresh_fc_eg_slot(&CH->SLOT[SLOT2] , fc , kc );
refresh_fc_eg_slot(&CH->SLOT[SLOT3] , fc , kc );
refresh_fc_eg_slot(&CH->SLOT[SLOT4] , fc , kc );
}
}
static INLINE void refresh_fc_eg_chan_sl3(void)
{
if( ym2612.CH[2].SLOT[SLOT1].Incr==-1)
if (CH->SLOT[SLOT1].Incr == -1)
{
refresh_fc_eg_slot(&ym2612.CH[2].SLOT[SLOT1], ym2612.OPN.SL3.fc[1], ym2612.OPN.SL3.kcode[1] );
refresh_fc_eg_slot(&ym2612.CH[2].SLOT[SLOT2], ym2612.OPN.SL3.fc[2], ym2612.OPN.SL3.kcode[2] );
refresh_fc_eg_slot(&ym2612.CH[2].SLOT[SLOT3], ym2612.OPN.SL3.fc[0], ym2612.OPN.SL3.kcode[0] );
refresh_fc_eg_slot(&ym2612.CH[2].SLOT[SLOT4], ym2612.CH[2].fc , ym2612.CH[2].kcode );
int kc = CH->kcode;
int fnum = CH->block_fnum & 0x7ff;
refresh_incr_eg_slot(&CH->SLOT[SLOT1], freqbase, fnum, kc);
refresh_incr_eg_slot(&CH->SLOT[SLOT2], freqbase, fnum, kc);
refresh_incr_eg_slot(&CH->SLOT[SLOT3], freqbase, fnum, kc);
refresh_incr_eg_slot(&CH->SLOT[SLOT4], freqbase, fnum, kc);
}
}
/* initialize time tables */
static void init_timetables(const UINT8 *dttable)
static INLINE void refresh_fc_eg_chan_sl3(FM_CH *CH2, const FM_3SLOT *SL3, UINT32 freqbase)
{
int i,d;
double rate;
/* DeTune table */
for (d = 0;d <= 3;d++){
for (i = 0;i <= 31;i++){
rate = ((double)dttable[d*32 + i]) * ym2612.OPN.ST.freqbase * (1<<(FREQ_SH-10));
ym2612.OPN.ST.dt_tab[d][i] = (INT32) rate;
ym2612.OPN.ST.dt_tab[d+4][i] = -ym2612.OPN.ST.dt_tab[d][i];
}
if (CH2->SLOT[SLOT1].Incr == -1)
{
refresh_incr_eg_slot(&CH2->SLOT[SLOT1], freqbase, SL3->block_fnum[1] & 0x7ff, SL3->kcode[1]);
refresh_incr_eg_slot(&CH2->SLOT[SLOT2], freqbase, SL3->block_fnum[2] & 0x7ff, SL3->kcode[2]);
refresh_incr_eg_slot(&CH2->SLOT[SLOT3], freqbase, SL3->block_fnum[0] & 0x7ff, SL3->kcode[0]);
refresh_incr_eg_slot(&CH2->SLOT[SLOT4], freqbase, CH2->block_fnum & 0x7ff, CH2->kcode);
}
}
static void reset_channels(FM_CH *CH)
{
int c,s;
@@ -1441,21 +1443,16 @@ static void reset_channels(FM_CH *CH)
for( c = 0 ; c < 6 ; c++ )
{
CH[c].fc = 0;
memset(&CH[c], 0, sizeof(CH[c]));
for(s = 0 ; s < 4 ; s++ )
{
CH[c].SLOT[s].Incr = -1;
CH[c].SLOT[s].key = 0;
CH[c].SLOT[s].phase = 0;
CH[c].SLOT[s].ar = CH[c].SLOT[s].ksr = 0;
CH[c].SLOT[s].ar_ksr = 0;
CH[c].SLOT[s].ssg = CH[c].SLOT[s].ssgn = 0;
CH[c].SLOT[s].state= EG_OFF;
CH[c].SLOT[s].state = EG_OFF;
CH[c].SLOT[s].volume = MAX_ATT_INDEX;
CH[c].SLOT[s].vol_out = MAX_ATT_INDEX;
}
CH[c].mem_value = CH[c].op1_out = 0;
}
memset(&ym2612.OPN.SL3, 0, sizeof(ym2612.OPN.SL3));
ym2612.slot_mask = 0;
ym2612.ssg_mask = 0;
}
@@ -1593,19 +1590,8 @@ static void OPNSetPres(int pres)
ym2612.OPN.eg_timer_add = (1<<EG_SH) * freqbase;
ym2612.OPN.ST.freqbase = freqbase;
/* make time tables */
init_timetables( dt_tab );
/* there are 2048 FNUMs that can be generated using FNUM/BLK registers
but LFO works with one more bit of a precision so we really need 4096 elements */
/* calculate fnumber -> increment counter table */
for(i = 0; i < 4096; i++)
{
/* freq table for octave 7 */
/* OPN phase increment counter = 20bit */
fn_table[i] = (UINT32)( (double)i * 32 * ym2612.OPN.ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
}
ym2612.OPN.ST.freqbase_ui = round((1 << FREQBASE_SH) * freqbase);
//printf("freqbase %f %x\n", freqbase, ym2612.OPN.ST.freqbase_ui);
/* LFO freq. table */
for(i = 0; i < 8; i++)
@@ -1678,8 +1664,6 @@ static int OPNWriteReg(int r, int v)
UINT8 blk = ym2612.OPN.ST.fn_h >> 3;
/* keyscale code */
CH->kcode = (blk<<2) | opn_fktable[fn >> 7];
/* phase increment counter */
CH->fc = fn_table[fn*2]>>(7-blk);
/* store fnum in clear form for LFO PM calculations */
CH->block_fnum = (blk<<11) | fn;
@@ -1698,8 +1682,6 @@ static int OPNWriteReg(int r, int v)
UINT8 blk = ym2612.OPN.SL3.fn_h>>3;
/* keyscale code */
ym2612.OPN.SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7];
/* phase increment counter */
ym2612.OPN.SL3.fc[c] = fn_table[fn*2]>>(7-blk);
ym2612.OPN.SL3.block_fnum[c] = (blk<<11) | fn;
ym2612.CH[2].SLOT[SLOT1].Incr=-1;
}
@@ -1760,6 +1742,7 @@ static int OPNWriteReg(int r, int v)
/* Generate samples for YM2612 */
int YM2612UpdateOne_(s32 *buffer, int length, int stereo, int is_buf_empty)
{
UINT32 freqbase = ym2612.OPN.ST.freqbase_ui;
int pan;
int active_chs = 0;
int flags = stereo ? 1:0;
@@ -1786,16 +1769,16 @@ int YM2612UpdateOne_(s32 *buffer, int length, int stereo, int is_buf_empty)
}
*/
/* refresh PG and EG */
refresh_fc_eg_chan( &ym2612.CH[0] );
refresh_fc_eg_chan( &ym2612.CH[1] );
refresh_fc_eg_chan(&ym2612.CH[0], freqbase);
refresh_fc_eg_chan(&ym2612.CH[1], freqbase);
if( (ym2612.OPN.ST.mode & 0xc0) )
/* 3SLOT MODE */
refresh_fc_eg_chan_sl3();
refresh_fc_eg_chan_sl3(&ym2612.CH[2], &ym2612.OPN.SL3, freqbase);
else
refresh_fc_eg_chan( &ym2612.CH[2] );
refresh_fc_eg_chan( &ym2612.CH[3] );
refresh_fc_eg_chan( &ym2612.CH[4] );
refresh_fc_eg_chan( &ym2612.CH[5] );
refresh_fc_eg_chan(&ym2612.CH[2], freqbase);
refresh_fc_eg_chan(&ym2612.CH[3], freqbase);
refresh_fc_eg_chan(&ym2612.CH[4], freqbase);
refresh_fc_eg_chan(&ym2612.CH[5], freqbase);
pan = ym2612.OPN.pan;
@@ -2253,11 +2236,10 @@ int YM2612PicoStateLoad2(int *tat, int *tbt, int *busy)
static size_t save_slot(u8 *buf, const FM_SLOT *slot)
{
size_t tmp, b = 0;
size_t b = 0;
b++; // length, assumes slot state won't grow beyond 255
tmp = (slot->DT - ym2612.OPN.ST.dt_tab[0]) / sizeof(ym2612.OPN.ST.dt_tab[0]);
save_u8_(buf, &b, tmp);
save_u8_(buf, &b, slot->detune);
save_u8_(buf, &b, slot->ar);
save_u8_(buf, &b, slot->d1r);
save_u8_(buf, &b, slot->d2r);
@@ -2283,9 +2265,8 @@ static size_t save_slot(u8 *buf, const FM_SLOT *slot)
static void load_slot(const u8 *buf, FM_SLOT *slot)
{
size_t b = 0;
u8 dt_reg;
dt_reg = load_u8_(buf, &b);
slot->detune = load_u8_(buf, &b);
slot->ar = load_u8_(buf, &b);
slot->d1r = load_u8_(buf, &b);
slot->d2r = load_u8_(buf, &b);
@@ -2301,8 +2282,6 @@ static void load_slot(const u8 *buf, FM_SLOT *slot)
slot->ssg = load_u8_(buf, &b);
slot->ssgn = load_u8_(buf, &b);
assert(dt_reg < 8);
slot->DT = ym2612.OPN.ST.dt_tab[dt_reg & 7];
recalc_volout( slot );
// trigger a refresh
@@ -2338,7 +2317,6 @@ static size_t save_channel(u8 *buf, const FM_CH *ch)
static size_t load_channel(const u8 *buf, size_t size, FM_CH *ch)
{
size_t i, b = 0, slot_size = 0, ch_size;
u32 fn, blk;
for (i = 0; i < 4; i++) {
u8 size_next = load_u8_(buf, &slot_size);
@@ -2364,10 +2342,6 @@ static size_t load_channel(const u8 *buf, size_t size, FM_CH *ch)
ch->block_fnum = load_u16(buf, &b) & 0x3fff;
ch->AMmasks = load_u8_(buf, &b);
fn = ch->block_fnum & 0x7ff;
blk = ch->block_fnum >> 11;
ch->fc = fn_table[fn*2] >> (7 - blk);
assert(ch_size >= b - slot_size - 1);
return slot_size + 1 + ch_size;
}
@@ -2437,13 +2411,8 @@ void YM2612PicoStateLoad3(const void *buf_, size_t size)
ym2612.OPN.ST.fn_h = load_u8_(buf, &b);
ym2612.OPN.SL3.fn_h = load_u8_(buf, &b);
for (i = 0; i < 3; i++) {
u32 fn, blk;
ym2612.OPN.SL3.kcode[i] = load_u8_(buf, &b);
ym2612.OPN.SL3.block_fnum[i] = load_u16(buf, &b) & 0x3fff;
fn = ym2612.OPN.SL3.block_fnum[i] & 0x7ff;
blk = ym2612.OPN.SL3.block_fnum[i] >> 11;
ym2612.OPN.SL3.fc[i] = fn_table[fn*2] >> (7 - blk);
}
ym2612.OPN.pan = load_u16(buf, &b);
ym2612.OPN.eg_cnt = load_u16(buf, &b);
+9 -9
View File
@@ -26,7 +26,9 @@ typedef s32 INT32; /* signed 32bit */
/* struct describing a single operator (SLOT) */
typedef struct
{
INT32 *DT; /* #0x00 detune :dt_tab[DT] */
UINT8 detune; /* #0x00 detune */
UINT8 detune_overflow;
UINT8 pad0[2];
UINT8 ar; /* #0x04 attack rate */
UINT8 d1r; /* #0x05 decay rate */
UINT8 d2r; /* #0x06 sustain rate */
@@ -84,21 +86,22 @@ typedef struct
UINT8 kcode; /* +11 key code: */
UINT8 pad2;
UINT8 upd_cnt; /* eg update counter */
UINT32 fc; /* fnum,blk:adjusted to sample rate */
UINT32 pad3;
UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
/* LFO */
UINT8 AMmasks; /* AM enable flag */
UINT8 pad3[3];
UINT8 pad4[3];
} FM_CH;
typedef struct
{
int clock; /* master clock (Hz) */
int rate; /* sampling rate (Hz) */
double freqbase; /* 08 frequency base */
UINT8 address; /* 10 address register | need_save */
UINT8 status; /* 11 status flag | need_save */
double freqbase; /* frequency base */
UINT32 freqbase_ui;
UINT8 address; /* address register | need_save */
UINT8 status; /* status flag | need_save */
UINT8 mode; /* mode CSM / 3SLOT */
UINT8 flags; /* operational flags */
int TA; /* timer a */
@@ -110,8 +113,6 @@ typedef struct
UINT8 pad2[1];
//int TBC; /* timer b maxval */
//int TBT; /* timer b ticker | need_save */
/* local time tables */
INT32 dt_tab[8][32];/* DeTune table */
} FM_ST;
#define ST_SSG 1
@@ -124,7 +125,6 @@ typedef struct
/* OPN 3slot struct */
typedef struct
{
UINT32 fc[3]; /* fnum3,blk3: calculated */
UINT8 fn_h; /* freq3 latch */
UINT8 kcode[3]; /* key code */
UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */