ym2612: fix EG Rate calculation. Fix Attack Rate exponential formulation.
This commit is contained in:
parent
945c12eb13
commit
f42b96ba7d
|
@ -20,6 +20,7 @@ namespace BizHawk.Emulation.Sound
|
|||
// ======================================================================
|
||||
|
||||
// TODO: Finish testing Envelope generator
|
||||
// TODO: maybe add guards when changing envelope parameters to immediately change envelope state (ie dont wait for EG cycle?)
|
||||
// TODO: Detune
|
||||
// TODO: LFO
|
||||
// TODO: Switch from Perfect Operator to Accurate Operator.
|
||||
|
@ -27,6 +28,8 @@ namespace BizHawk.Emulation.Sound
|
|||
// TODO: MEM delayed samples
|
||||
// TODO: CSM mode
|
||||
// TODO: SSG-EG
|
||||
// TODO: Seriously, I think we need better resampling code.
|
||||
// TODO: Experiment with low-pass filters, etc.
|
||||
|
||||
public sealed class YM2612 : ISoundProvider
|
||||
{
|
||||
|
@ -36,7 +39,6 @@ namespace BizHawk.Emulation.Sound
|
|||
{
|
||||
InitTimers();
|
||||
MaxVolume = short.MaxValue;
|
||||
//Channels[2].Operators[3].Debug = true;
|
||||
}
|
||||
|
||||
// ====================================================================================
|
||||
|
@ -302,10 +304,11 @@ namespace BizHawk.Emulation.Sound
|
|||
// TODO maybe its 4-frequency mode
|
||||
// TODO is this right, only reflect change when writing LSB?
|
||||
|
||||
channel.Operators[0].FrequencyNumber = channel.FrequencyNumber; channel.Operators[0].Block = channel.Block;
|
||||
channel.Operators[1].FrequencyNumber = channel.FrequencyNumber; channel.Operators[1].Block = channel.Block;
|
||||
channel.Operators[2].FrequencyNumber = channel.FrequencyNumber; channel.Operators[2].Block = channel.Block;
|
||||
channel.Operators[3].FrequencyNumber = channel.FrequencyNumber; channel.Operators[3].Block = channel.Block;
|
||||
Operator op;
|
||||
op = channel.Operators[0]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op);
|
||||
op = channel.Operators[1]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op);
|
||||
op = channel.Operators[2]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op);
|
||||
op = channel.Operators[3]; op.FrequencyNumber = channel.FrequencyNumber; op.Block = channel.Block; CalcKeyCode(op); CalcRks(op);
|
||||
}
|
||||
|
||||
static void WriteFrequencyHigh(Channel channel, byte value)
|
||||
|
@ -315,6 +318,27 @@ namespace BizHawk.Emulation.Sound
|
|||
channel.Block = (value >> 3) & 7;
|
||||
}
|
||||
|
||||
static void CalcKeyCode(Operator op)
|
||||
{
|
||||
int freq = op.FrequencyNumber;
|
||||
bool f11 = ((freq >> 10) & 1) != 0;
|
||||
bool f10 = ((freq >> 9) & 1) != 0;
|
||||
bool f09 = ((freq >> 8) & 1) != 0;
|
||||
bool f08 = ((freq >> 7) & 1) != 0;
|
||||
|
||||
bool n3a = f11 & (f10 | f09 | f08);
|
||||
bool n3b = !f11 & f10 & f09 & f08;
|
||||
bool n3 = n3a | n3b;
|
||||
|
||||
op.KeyCode = (op.Block << 2) | (f11 ? 2 : 0) | (n3 ? 1 :0);
|
||||
}
|
||||
|
||||
static void CalcRks(Operator op)
|
||||
{
|
||||
int shiftVal = 3 - op.KS_KeyScale;
|
||||
op.Rks = op.KeyCode >> shiftVal;
|
||||
}
|
||||
|
||||
static void Write_Feedback_Algorithm(Channel channel, byte value)
|
||||
{
|
||||
channel.Algorithm = value & 7;
|
||||
|
@ -350,6 +374,7 @@ namespace BizHawk.Emulation.Sound
|
|||
var oper = Channels[chan].Operators[op];
|
||||
oper.AR_AttackRate = value & 31;
|
||||
oper.KS_KeyScale = value >> 6;
|
||||
CalcRks(oper);
|
||||
}
|
||||
|
||||
public void Write_DR_AM(int chan, int op, byte value)
|
||||
|
@ -632,14 +657,11 @@ namespace BizHawk.Emulation.Sound
|
|||
op.EnvelopeState = EnvelopeState.Decay;
|
||||
if (op.SL_SustainLevel == 0) // If Sustain Level is 0, we skip Decay and go straight to Sustain phase.
|
||||
op.EnvelopeState = EnvelopeState.Sustain;
|
||||
if (op.Debug) Console.WriteLine("Switch to " + op.EnvelopeState);
|
||||
}
|
||||
|
||||
if (op.EnvelopeState == EnvelopeState.Decay && op.EgAttenuation >= op.Normalized10BitSL)
|
||||
{
|
||||
// Switch to Sustain phase
|
||||
op.EnvelopeState = EnvelopeState.Sustain;
|
||||
if (op.Debug) Console.WriteLine("Switch to Sustain");
|
||||
}
|
||||
|
||||
// At this point, we've determined what envelope phase we're in. Lets do the update.
|
||||
|
@ -654,11 +676,12 @@ namespace BizHawk.Emulation.Sound
|
|||
case EnvelopeState.Release: rate = (op.RR_ReleaseRate << 1) + 1; break;
|
||||
}
|
||||
|
||||
if (rate != 0) // rate=0 is 0 no matter the value of KeyScale.
|
||||
rate = Math.Min((rate * 2) + op.KS_KeyScale, 63);
|
||||
if (rate != 0) // rate=0 is 0 no matter the value of Rks.
|
||||
rate = Math.Min((rate * 2) + op.Rks, 63);
|
||||
|
||||
// Now we have rate. figure out shift value and cycle offset
|
||||
int shiftValue = egRateCounterShiftValues[rate];
|
||||
|
||||
if (egCycleCounter % (1 << shiftValue) == 0)
|
||||
{
|
||||
// Update attenuation value this tick
|
||||
|
@ -666,40 +689,36 @@ namespace BizHawk.Emulation.Sound
|
|||
int attenuationAdjustment = egRateIncrementValues[(rate * 8) + updateCycleOffset];
|
||||
|
||||
if (op.EnvelopeState == EnvelopeState.Attack)
|
||||
op.EgAttenuation -= attenuationAdjustment * (((op.EgAttenuation) / 16) + 1);
|
||||
op.EgAttenuation += (~op.EgAttenuation * attenuationAdjustment) >> 4;
|
||||
else // One of the decay phases
|
||||
op.EgAttenuation += attenuationAdjustment;
|
||||
|
||||
//if (op.Debug) Console.WriteLine("Attn {0} Adj {1} Cycle {2} Rate {3} Shift {4} Offset {5}", op.EgAttenuation, op.AdjustedEGOutput, egCycleCounter, rate, shiftValue, updateCycleOffset);
|
||||
}
|
||||
}
|
||||
|
||||
static void KeyOn(Operator op)
|
||||
{
|
||||
op.PhaseCounter = 0; // Reset Phase Generator
|
||||
//Console.WriteLine("key on");
|
||||
if (op.AR_AttackRate >= 30) // AR of 30 or 31 skips attack phase
|
||||
|
||||
if (op.AR_AttackRate >= 30)
|
||||
{
|
||||
|
||||
// AR of 30 or 31 skips attack phase
|
||||
op.EgAttenuation = 0; // Force minimum attenuation
|
||||
|
||||
op.EnvelopeState = EnvelopeState.Decay;
|
||||
if (op.SL_SustainLevel == 0) // If Sustain Level is 0, we skip Decay and go straight to Sustain phase.
|
||||
op.EnvelopeState = EnvelopeState.Sustain;
|
||||
|
||||
}
|
||||
else
|
||||
{ // Regular Key-On
|
||||
} else {
|
||||
|
||||
// Regular Key-On
|
||||
op.EnvelopeState = EnvelopeState.Attack;
|
||||
// It's notable, though unsurprising, that we do not reset attenuation to max attenuation during a standard KeyOn.
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void KeyOff(Operator op)
|
||||
{
|
||||
// TODO I feel like it should do more than this. But maybe it doesn't.
|
||||
op.EnvelopeState = EnvelopeState.Release;
|
||||
}
|
||||
|
||||
|
@ -722,11 +741,7 @@ namespace BizHawk.Emulation.Sound
|
|||
phase10 += phaseModulationInput10;
|
||||
phase10 &= 0x3FF;
|
||||
|
||||
|
||||
int operator_output = OperatorCalc(phase10, op.AdjustedEGOutput);
|
||||
//if (op.Debug) Log.Error("YM2612","SLOT4 eg_out {0} state _ TL {1} volume {2} SL {3} op_out {4} phase {5}", op.AdjustedEGOutput, op.Normalized10BitTL, op.EgAttenuation, op.Normalized10BitSL, operator_output, phase10);
|
||||
|
||||
return operator_output;
|
||||
return OperatorCalc(phase10, op.AdjustedEGOutput);
|
||||
}
|
||||
|
||||
static void RunPhaseGenerator(Operator op)
|
||||
|
@ -913,6 +928,9 @@ namespace BizHawk.Emulation.Sound
|
|||
public int FrequencyNumber; // 11 bits
|
||||
public int Block; // 3 bits
|
||||
|
||||
public int KeyCode; // 5 bits (described on pg 25 of YM2608 docs)
|
||||
public int Rks; // 5 bits (described on pg 29 of YM2608 docs)
|
||||
|
||||
// Internal State
|
||||
|
||||
public int PhaseCounter; // 20 bits, where the 10 most significant bits are output to the operator.
|
||||
|
@ -932,10 +950,8 @@ namespace BizHawk.Emulation.Sound
|
|||
}
|
||||
|
||||
public int Normalized10BitSL { get { return slTable[SL_SustainLevel]; } }
|
||||
public int Normalized10BitTL { get { return TL_TotalLevel << 3; } } // TODO no idea if this is correct, it's probably not.
|
||||
public int Normalized10BitTL { get { return TL_TotalLevel << 3; } }
|
||||
public int AdjustedEGOutput { get { return Math.Min(egAttenuation + Normalized10BitTL, 1023); } }
|
||||
|
||||
public bool Debug = false;
|
||||
}
|
||||
|
||||
public sealed class Channel
|
||||
|
@ -1001,6 +1017,7 @@ namespace BizHawk.Emulation.Sound
|
|||
{
|
||||
MaybeRunEnvelopeGenerator();
|
||||
|
||||
// Generate FM output
|
||||
for (int ch = 0; ch < 6; ch++)
|
||||
{
|
||||
short sample = (short)GetChannelOutput(Channels[ch], channelVolume);
|
||||
|
|
Loading…
Reference in New Issue