ymfm: Sync with upstream:

* Match OPN LFO frequencies to hardware measurements
 * Improve OPQ detune, reverb, and KSR
This commit is contained in:
Aaron Giles 2021-05-29 02:37:19 -07:00
parent 52eee5fd27
commit 04027e4fa7
8 changed files with 50 additions and 52 deletions

View File

@ -266,7 +266,7 @@ enum envelope_state : uint32_t
EG_DECAY = 2,
EG_SUSTAIN = 3,
EG_RELEASE = 4,
EG_REVERB = 5, // OPZ only; set EG_HAS_REVERB to enable
EG_REVERB = 5, // OPQ/OPZ only; set EG_HAS_REVERB to enable
EG_STATES = 6
};

View File

@ -110,7 +110,7 @@ public:
// the following constants are uncommon:
// bool DYNAMIC_OPS: True if ops/channel can be changed at runtime (OPL3+)
// bool EG_HAS_DEPRESS: True if the chip has a DP ("depress"?) envelope stage (OPLL)
// bool EG_HAS_REVERB: True if the chip has a faux reverb envelope stage (OPZ)
// bool EG_HAS_REVERB: True if the chip has a faux reverb envelope stage (OPQ/OPZ)
// bool EG_HAS_SSG: True if the chip has SSG envelope support (OPN)
// bool MODULATOR_DELAY: True if the modulator is delayed by 1 sample (OPL pre-OPL3)
//

View File

@ -706,11 +706,8 @@ void fm_operator<RegisterType>::clock_envelope(uint32_t env_counter)
{
// glitch means that attack rates of 62/63 don't increment if
// changed after the initial key on (where they are handled
// specially)
// QUESTION: this check affects one of the operators on the gng credit sound
// is it correct?
// QUESTION: does this apply only to YM2612?
// specially); nukeykt confirms this happens on OPM, OPN, OPL/OPLL
// at least so assuming it is true for everyone
if (rate < 62)
m_env_attenuation += (~m_env_attenuation * increment) >> 4;
}

View File

@ -207,7 +207,12 @@ int32_t opn_registers_base<IsOpnA>::clock_noise_and_lfo()
// when we cross the divider count, add enough to zero it and cause an
// increment at bit 8; the 7-bit value lives from bits 8-14
if (subcount >= lfo_max_count[lfo_rate()])
m_lfo_counter += subcount ^ 0xff;
{
// note: to match the published values this should be 0x100 - subcount;
// however, tests on the hardware and nuked bear out an off-by-one
// error exists that causes the max LFO rate to be faster than published
m_lfo_counter += 0x101 - subcount;
}
// AM value is 7 bits, staring at bit 8; grab the low 6 directly
m_lfo_am = bitfield(m_lfo_counter, 8, 6);

View File

@ -116,13 +116,11 @@ public:
static constexpr uint32_t CHANNELS = IsOpnA ? 6 : 3;
static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
static constexpr uint32_t OPERATORS = CHANNELS * 4;
static constexpr bool DYNAMIC_OPS = false;
static constexpr uint32_t WAVEFORMS = 1;
static constexpr uint32_t REGISTERS = IsOpnA ? 0x200 : 0x100;
static constexpr uint32_t REG_MODE = 0x27;
static constexpr uint32_t DEFAULT_PRESCALE = 6;
static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
static constexpr bool EG_HAS_DEPRESS = false;
static constexpr bool EG_HAS_SSG = true;
static constexpr bool MODULATOR_DELAY = false;
static constexpr uint32_t CSM_TRIGGER_MASK = 1 << 2;

View File

@ -31,7 +31,7 @@
#include "ymfm_opq.h"
#include "ymfm_fm.ipp"
#define TEMPORARY_DEBUG_PRINTS (1)
#define TEMPORARY_DEBUG_PRINTS (0)
//
// OPQ (aka YM3806/YM3533)
@ -171,7 +171,7 @@ int32_t opq_registers::clock_noise_and_lfo()
// when we cross the divider count, add enough to zero it and cause an
// increment at bit 8; the 7-bit value lives from bits 8-14
if (subcount >= lfo_max_count[lfo_rate()])
m_lfo_counter += subcount ^ 0xff;
m_lfo_counter += 0x101 - subcount;
// AM value is 7 bits, staring at bit 8; grab the low 6 directly
m_lfo_am = bitfield(m_lfo_counter, 8, 6);
@ -228,7 +228,7 @@ void opq_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata
cache.waveform = &m_waveform[op_waveform(opoffs)][0];
// get frequency from the appropriate registers
uint32_t block_freq = cache.block_freq = (opoffs & 1) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs);
uint32_t block_freq = cache.block_freq = (opoffs & 8) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs);
// compute the keycode: block_freq is:
//
@ -248,11 +248,16 @@ void opq_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata
// for speed, we just look it up in a 16-bit constant
keycode |= bitfield(0xfe80, bitfield(block_freq, 8, 4));
// detune adjustment; detune isn't really understood except that it is a
// 6-bit value where the middle value (0x20) means no detune; range is +/-20 cents
// this calculation gives a bit more, but shifting by 12 gives a bit less
// also, the real calculation is probably something to do with keycodes
cache.detune = ((op_detune(opoffs) - 0x20) * block_freq) >> 11;
// detune adjustment: the detune values supported by the OPQ are
// a much larger range (6 bits vs 3 bits) compared to any other
// known FM chip; based on experiments, it seems that the extra
// bits provide a bigger detune range rather than finer control,
// so until we get true measurements just assemble a net detune
// value by summing smaller detunes
int32_t detune = int32_t(op_detune(opoffs)) - 0x20;
int32_t abs_detune = std::abs(detune);
int32_t adjust = (abs_detune / 3) * detune_adjustment(3, keycode) + detune_adjustment(abs_detune % 3, keycode);
cache.detune = (detune >= 0) ? adjust : -adjust;
// multiple value, as an x.1 value (0 means 0.5)
static const uint8_t s_multiple_map[16] = { 1,2,4,6,8,10,12,14,16,18,20,24,30,32,34,36 };
@ -273,15 +278,13 @@ void opq_registers::cache_operator_data(uint32_t choffs, uint32_t opoffs, opdata
cache.eg_sustain |= (cache.eg_sustain + 1) & 0x10;
cache.eg_sustain <<= 5;
// determine KSR adjustment for enevlope rates; KSR is supposedly 3 bits
// not 2 like all other implementations, so unsure how this would work.
// Maybe keycode is a larger range? For now, we'll just take the upper 2
// bits and use that.
uint32_t ksrval = keycode >> ((op_ksr(opoffs) >> 1) ^ 3);
// determine KSR adjustment for enevlope rates
uint32_t ksrval = keycode >> (op_ksr(opoffs) ^ 3);
cache.eg_rate[EG_ATTACK] = effective_rate(op_attack_rate(opoffs) * 2, ksrval);
cache.eg_rate[EG_DECAY] = effective_rate(op_decay_rate(opoffs) * 2, ksrval);
cache.eg_rate[EG_SUSTAIN] = effective_rate(op_sustain_rate(opoffs) * 2, ksrval);
cache.eg_rate[EG_RELEASE] = effective_rate(op_release_rate(opoffs) * 4 + 2, ksrval);
cache.eg_rate[EG_REVERB] = (ch_reverb(choffs) != 0) ? 5 : cache.eg_rate[EG_RELEASE];
cache.eg_shift = 0;
}
@ -310,15 +313,12 @@ uint32_t opq_registers::compute_phase_step(uint32_t choffs, uint32_t opoffs, opd
fnum &= 0xfff;
}
// this is likely not right, but should given the right approximate result
fnum += cache.detune;
// apply block shift to compute phase step
uint32_t block = bitfield(cache.block_freq, 12, 3);
uint32_t phase_step = (fnum << block) >> 2;
// apply detune based on the keycode -- this is probably where the real chip does it
// phase_step += cache.detune;
// apply detune based on the keycode
phase_step += cache.detune;
// clamp to 17 bits in case detune overflows
// QUESTION: is this specific to the YM2612/3438?
@ -341,10 +341,10 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
char buffer[256];
char *end = &buffer[0];
end += sprintf(end, "%d.%02d freq=%04X dt=%d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
end += sprintf(end, "%d.%02d freq=%04X dt=%+2d fb=%d alg=%X mul=%X tl=%02X ksr=%d adsr=%02X/%02X/%02X/%X sl=%X out=%c%c",
chnum, opnum,
(opoffs & 1) ? ch_block_freq_24(choffs) : ch_block_freq_13(choffs),
op_detune(opoffs),
int32_t(op_detune(opoffs)) - 0x20,
ch_feedback(choffs),
ch_algorithm(choffs),
op_multiple(opoffs),
@ -366,8 +366,8 @@ std::string opq_registers::log_keyon(uint32_t choffs, uint32_t opoffs)
end += sprintf(end, " pm=%d", ch_lfo_pm_sens(choffs));
if (am || pm)
end += sprintf(end, " lfo=%02X", lfo_rate());
if (ch_echo(choffs))
end += sprintf(end, " echo");
if (ch_reverb(choffs))
end += sprintf(end, " reverb");
return buffer;
}

View File

@ -63,7 +63,7 @@ namespace ymfm
// -x------ Pan left
// --xxx--- Feedback level for operator 1 (0-7)
// -----xxx Operator connection algorithm (0-7)
// 18-1F x------- Echo
// 18-1F x------- Reverb
// -xxx---- PM sensitivity
// ------xx AM shift
// 20-27 -xxx---- Block (0-7), Operator 2 & 4
@ -77,7 +77,7 @@ namespace ymfm
// 40-5F 0-xxxxxx Detune value (0-63)
// 1---xxxx Multiple value (0-15)
// 60-7F -xxxxxxx Total level (0-127)
// 80-9F xxx----- Key scale rate (0-7)
// 80-9F xx------ Key scale rate (0-3)
// ---xxxxx Attack rate (0-31)
// A0-BF x------- LFO AM enable, retrigger disable
// x------ Waveform select
@ -88,11 +88,10 @@ namespace ymfm
//
// Diffs from OPM:
// - 2 frequencies/channel
// - KSR is 3 bits?
// - retrigger disable
// - 2 waveforms
// - uses FNUM
// - echo behavior
// - reverb behavior
// - larger detune range
//
// Questions:
@ -108,14 +107,12 @@ public:
static constexpr uint32_t CHANNELS = 8;
static constexpr uint32_t ALL_CHANNELS = (1 << CHANNELS) - 1;
static constexpr uint32_t OPERATORS = CHANNELS * 4;
static constexpr bool DYNAMIC_OPS = false;
static constexpr uint32_t WAVEFORMS = 2;
static constexpr uint32_t REGISTERS = 0x120;
static constexpr uint32_t REG_MODE = 0x03;
static constexpr uint32_t DEFAULT_PRESCALE = 2;
static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
static constexpr bool EG_HAS_DEPRESS = false;
static constexpr bool EG_HAS_SSG = false;
static constexpr bool EG_HAS_REVERB = true;
static constexpr bool MODULATOR_DELAY = false;
static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
static constexpr uint8_t STATUS_TIMERA = 0;
@ -195,7 +192,7 @@ public:
uint32_t ch_output_3(uint32_t choffs) const { return 0; }
uint32_t ch_feedback(uint32_t choffs) const { return byte(0x10, 3, 3, choffs); }
uint32_t ch_algorithm(uint32_t choffs) const { return byte(0x10, 0, 3, choffs); }
uint32_t ch_echo(uint32_t choffs) const { return byte(0x18, 7, 1, choffs); }
uint32_t ch_reverb(uint32_t choffs) const { return byte(0x18, 7, 1, choffs); }
uint32_t ch_lfo_pm_sens(uint32_t choffs) const { return byte(0x18, 4, 3, choffs); }
uint32_t ch_lfo_am_sens(uint32_t choffs) const { return byte(0x18, 0, 2, choffs); }
uint32_t ch_block_freq_24(uint32_t choffs) const { return word(0x20, 0, 7, 0x30, 0, 8, choffs); }
@ -205,7 +202,7 @@ public:
uint32_t op_detune(uint32_t opoffs) const { return byte(0x40, 0, 6, opoffs); }
uint32_t op_multiple(uint32_t opoffs) const { return byte(0x100, 0, 4, opoffs); }
uint32_t op_total_level(uint32_t opoffs) const { return byte(0x60, 0, 7, opoffs); }
uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 5, 3, opoffs); }
uint32_t op_ksr(uint32_t opoffs) const { return byte(0x80, 6, 2, opoffs); }
uint32_t op_attack_rate(uint32_t opoffs) const { return byte(0x80, 0, 5, opoffs); }
uint32_t op_lfo_am_enable(uint32_t opoffs) const { return byte(0xa0, 7, 1, opoffs); }
uint32_t op_waveform(uint32_t opoffs) const { return byte(0xa0, 6, 1, opoffs); }

View File

@ -138,6 +138,7 @@ public:
static constexpr uint32_t REGISTERS = 0x190;
static constexpr uint32_t DEFAULT_PRESCALE = 2;
static constexpr uint32_t EG_CLOCK_DIVIDER = 3;
static constexpr bool EG_HAS_REVERB = true;
static constexpr uint32_t CSM_TRIGGER_MASK = ALL_CHANNELS;
static constexpr uint32_t REG_MODE = 0x14;
static constexpr uint8_t STATUS_TIMERA = 0x01;