summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2021-04-21 02:40:58 -0700
committer Aaron Giles <aaron@aarongiles.com>2021-04-21 02:40:58 -0700
commitb13e15fbfda231df67796c1597a36433fefac042 (patch)
tree4e2e0d26c6c244bab9a34879706b0c31fc59658f
parent782d8276708eedae93374967dbef05f20d4a9bb3 (diff)
ymfm: Implement delay in OPM/OPN operators. Not sure this really affects much audibly, but this does seem to be documented, so let's make it right.
-rw-r--r--src/devices/sound/ymfm.cpp69
-rw-r--r--src/devices/sound/ymfm.h1
2 files changed, 39 insertions, 31 deletions
diff --git a/src/devices/sound/ymfm.cpp b/src/devices/sound/ymfm.cpp
index abea6fa365a..2ab497776d6 100644
--- a/src/devices/sound/ymfm.cpp
+++ b/src/devices/sound/ymfm.cpp
@@ -2529,6 +2529,7 @@ ymfm_channel<RegisterType>::ymfm_channel(ymfm_engine_base<RegisterType> &owner,
m_choffs(choffs),
m_feedback{ 0, 0 },
m_feedback_in(0),
+ m_delay_in(0),
m_op{ nullptr, nullptr, nullptr, nullptr },
m_regs(owner.regs()),
m_owner(owner)
@@ -2546,6 +2547,7 @@ void ymfm_channel<RegisterType>::save(device_t &device, u32 index)
// save our data
device.save_item(YMFM_NAME(m_feedback), index);
device.save_item(YMFM_NAME(m_feedback_in), index);
+ device.save_item(YMFM_NAME(m_delay_in), index);
}
@@ -2559,6 +2561,7 @@ void ymfm_channel<RegisterType>::reset()
// reset our data
m_feedback[0] = m_feedback[1] = 0;
m_feedback_in = 0;
+ m_delay_in = 0;
}
@@ -2693,11 +2696,6 @@ void ymfm_channel<RegisterType>::output_4op(s32 outputs[RegisterType::OUTPUTS],
// compute the 14-bit volume/value of operator 1 and update the feedback
s32 op1value = m_feedback_in = m_op[0]->compute_volume(m_op[0]->phase() + opmod, am_offset);
- // now that the feedback has been computed, skip the rest if all volumes
- // are clear; no need to do all this work for nothing
- if (m_regs.ch_output_any(m_choffs) == 0)
- return;
-
// OPM/OPN offer 8 different connection algorithms for 4 operators,
// and OPL3 offers 4 more, which we designate here as 8-11.
//
@@ -2707,36 +2705,41 @@ void ymfm_channel<RegisterType>::output_4op(s32 outputs[RegisterType::OUTPUTS],
// 1 = O1
// 2 = O2
// 3 = O3
- // 4 = (O4)
+ // 4 = delay
// 5 = O1+O2
// 6 = O1+O3
- // 7 = O2+O3
+ // 7 = delay+O3
+ //
+ // Several algorithms consume data from the previous sample; this is
+ // designated as 'delay' here. Inputs to 'delay' are stored for later,
+ // and consumers of 'delay' pull from the previous value.
//
// The s_algorithm_ops table describes the inputs and outputs of each
// algorithm as follows:
//
- // ---------x use opout[x] as operator 2 input
- // ------xxx- use opout[x] as operator 3 input
- // ---xxx---- use opout[x] as operator 4 input
- // --x------- include opout[1] in final sum
- // -x-------- include opout[2] in final sum
- // x--------- include opout[3] in final sum
- #define ALGORITHM(op2in, op3in, op4in, op1out, op2out, op3out) \
- (op2in | (op3in << 1) | (op4in << 4) | (op1out << 7) | (op2out << 8) | (op3out << 9))
+ // ------------x use opout[x] as operator 2 input
+ // ---------xxx- use opout[x] as operator 3 input
+ // ------xxx---- use opout[x] as operator 4 input
+ // ---xxx------- use opout[x] as delay value for next sample
+ // --x---------- include opout[1] in final sum
+ // -x----------- include opout[2] in final sum
+ // x------------ include opout[3] in final sum
+ #define ALGORITHM(op2in, op3in, op4in, delay, op1out, op2out, op3out) \
+ (op2in | (op3in << 1) | (op4in << 4) | (delay << 7) | (op1out << 10) | (op2out << 11) | (op3out << 12))
static u16 const s_algorithm_ops[8+4] =
{
- ALGORITHM(1,2,3, 0,0,0), // 0: O1 -> O2 -> O3 -> O4 -> out (O4)
- ALGORITHM(0,5,3, 0,0,0), // 1: (O1 + O2) -> O3 -> O4 -> out (O4)
- ALGORITHM(0,2,6, 0,0,0), // 2: (O1 + (O2 -> O3)) -> O4 -> out (O4)
- ALGORITHM(1,0,7, 0,0,0), // 3: ((O1 -> O2) + O3) -> O4 -> out (O4)
- ALGORITHM(1,0,3, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4)
- ALGORITHM(1,1,1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> O3) + (O1 -> O4)) -> out (O2+O3+O4)
- ALGORITHM(1,0,0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4)
- ALGORITHM(0,0,0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4)
- ALGORITHM(1,2,3, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0]
- ALGORITHM(0,2,3, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique]
- ALGORITHM(1,0,3, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4]
- ALGORITHM(0,2,0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique]
+ ALGORITHM(1,4,3, 2, 0,0,0), // 0: O1 -> O2 -> (delay) -> O3 -> O4 -> out (O4)
+ ALGORITHM(0,4,3, 5, 0,0,0), // 1: (O1 + O2) -> (delay) -> O3 -> O4 -> out (O4)
+ ALGORITHM(0,4,6, 2, 0,0,0), // 2: (O1 + (O2 -> (delay) -> O3)) -> O4 -> out (O4)
+ ALGORITHM(1,0,7, 2, 0,0,0), // 3: ((O1 -> O2 -> (delay)) + O3) -> O4 -> out (O4)
+ ALGORITHM(1,0,3, 0, 0,1,0), // 4: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4)
+ ALGORITHM(1,4,1, 1, 0,1,1), // 5: ((O1 -> O2) + (O1 -> (delay) -> O3) + (O1 -> O4)) -> out (O2+O3+O4)
+ ALGORITHM(1,0,0, 0, 0,1,1), // 6: ((O1 -> O2) + O3 + O4) -> out (O2+O3+O4)
+ ALGORITHM(0,0,0, 0, 1,1,1), // 7: (O1 + O2 + O3 + O4) -> out (O1+O2+O3+O4)
+ ALGORITHM(1,2,3, 0, 0,0,0), // 8: O1 -> O2 -> O3 -> O4 -> out (O4) [same as 0]
+ ALGORITHM(0,2,3, 0, 1,0,0), // 9: (O1 + (O2 -> O3 -> O4)) -> out (O1+O4) [unique]
+ ALGORITHM(1,0,3, 0, 0,1,0), // 10: ((O1 -> O2) + (O3 -> O4)) -> out (O2+O4) [same as 4]
+ ALGORITHM(0,2,0, 0, 1,0,1) // 11: (O1 + (O2 -> O3) + O4) -> out (O1+O3+O4) [unique]
};
u32 algorithm_ops = s_algorithm_ops[m_regs.ch_algorithm(m_choffs)];
@@ -2744,6 +2747,7 @@ void ymfm_channel<RegisterType>::output_4op(s32 outputs[RegisterType::OUTPUTS],
s16 opout[8];
opout[0] = 0;
opout[1] = op1value;
+ opout[4] = m_delay_in;
// compute the 14-bit volume/value of operator 2
opmod = opout[BIT(algorithm_ops, 0, 1)] >> 1;
@@ -2754,7 +2758,7 @@ void ymfm_channel<RegisterType>::output_4op(s32 outputs[RegisterType::OUTPUTS],
opmod = opout[BIT(algorithm_ops, 1, 3)] >> 1;
opout[3] = m_op[2]->compute_volume(m_op[2]->phase() + opmod, am_offset);
opout[6] = opout[1] + opout[3];
- opout[7] = opout[2] + opout[3];
+ opout[7] = opout[4] + opout[3];
// compute the 14-bit volume/value of operator 4; this could be a noise
// value on the OPM; all algorithms consume OP4 output at a minimum
@@ -2768,13 +2772,16 @@ void ymfm_channel<RegisterType>::output_4op(s32 outputs[RegisterType::OUTPUTS],
}
result >>= rshift;
+ // set the delay value for the next sample
+ m_delay_in = opout[BIT(algorithm_ops, 7, 3)];
+
// optionally add OP1, OP2, OP3
s32 clipmin = -clipmax - 1;
- if (BIT(algorithm_ops, 7) != 0)
+ if (BIT(algorithm_ops, 10) != 0)
result = std::clamp(result + (opout[1] >> rshift), clipmin, clipmax);
- if (BIT(algorithm_ops, 8) != 0)
+ if (BIT(algorithm_ops, 11) != 0)
result = std::clamp(result + (opout[2] >> rshift), clipmin, clipmax);
- if (BIT(algorithm_ops, 9) != 0)
+ if (BIT(algorithm_ops, 12) != 0)
result = std::clamp(result + (opout[3] >> rshift), clipmin, clipmax);
// add to the output
diff --git a/src/devices/sound/ymfm.h b/src/devices/sound/ymfm.h
index 8ab0ddc9d03..9d898775d55 100644
--- a/src/devices/sound/ymfm.h
+++ b/src/devices/sound/ymfm.h
@@ -1246,6 +1246,7 @@ private:
u32 m_choffs; // channel offset in registers
s16 m_feedback[2]; // feedback memory for operator 1
mutable s16 m_feedback_in; // next input value for op 1 feedback (set in output)
+ mutable s16 m_delay_in; // next delay value (set in output)
ymfm_operator<RegisterType> *m_op[4]; // up to 4 operators
RegisterType &m_regs; // direct reference to registers
ymfm_engine_base<RegisterType> &m_owner; // reference to the owning engine