ym2612: fix EG Rate calculation. Fix Attack Rate exponential formulation.

This commit is contained in:
beirich 2012-08-10 04:46:29 +00:00
parent 945c12eb13
commit f42b96ba7d
1 changed files with 72 additions and 55 deletions

View File

@ -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);