Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.Envelope.cs
2 views
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using BizHawk.Common;

namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
	public sealed partial class Sid
	{
	    private sealed class Envelope
		{
		    [SaveState.DoNotSave] private const int StateAttack = 0;
		    [SaveState.DoNotSave] private const int StateDecay = 1;
		    [SaveState.DoNotSave] private const int StateRelease = 2;

		    private int _attack;
		    private int _decay;
		    private bool _delay;
		    private int _envCounter;
		    private int _expCounter;
		    private int _expPeriod;
		    private bool _freeze;
		    private int _lfsr;
		    private bool _gate;
		    private int _rate;
		    private int _release;
		    private int _state;
		    private int _sustain;

		    private static readonly int[] AdsrTable = {
				0x7F00, 0x0006, 0x003C, 0x0330,
				0x20C0, 0x6755, 0x3800, 0x500E,
				0x1212, 0x0222, 0x1848, 0x59B8,
				0x3840, 0x77E2, 0x7625, 0x0A93
			};

		    private static readonly int[] ExpCounterTable = {
				0xFF, 0x5D, 0x36, 0x1A, 0x0E, 0x06, 0x00
			};

		    private static readonly int[] ExpPeriodTable = {
				0x01, 0x02, 0x04, 0x08, 0x10, 0x1E, 0x01
			};

		    private static readonly int[] SustainTable = {
				0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
				0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
			};

			public Envelope()
			{
				HardReset();
			}

			public void ExecutePhase2()
			{
				if (!_delay)
				{
					_envCounter--;
					_delay = true;
					UpdateExpCounter();
				}

				if (_lfsr != _rate)
				{
					var feedback = ((_lfsr >> 14) ^ (_lfsr >> 13)) & 0x1;
					_lfsr = ((_lfsr << 1) & 0x7FFF) | feedback;
					return;
				}
				_lfsr = 0x7FFF;

				if (_state != StateAttack && ++_expCounter != _expPeriod)
				{
				    return;
				}

				_expCounter = 0;
				if (_freeze)
				    return;

				switch (_state)
				{
				    case StateAttack:
				        _envCounter++;
				        if (_envCounter == 0xFF)
				        {
				            _state = StateDecay;
				            _rate = AdsrTable[_decay];
				        }
				        break;
				    case StateDecay:
				        if (_envCounter == SustainTable[_sustain])
				        {
				            return;
				        }
				        if (_expPeriod != 1)
				        {
				            _delay = false;
				            return;
				        }
				        _envCounter--;
				        break;
				    case StateRelease:
				        if (_expPeriod != 1)
				        {
				            _delay = false;
				            return;
				        }
				        _envCounter--;
				        break;
				}
				_envCounter &= 0xFF;
				UpdateExpCounter();
			}

			public void HardReset()
			{
				_attack = 0;
				_decay = 0;
				_delay = true;
				_envCounter = 0;
				_expCounter = 0;
				_expPeriod = ExpPeriodTable[0];
				_freeze = false;
				_gate = false;
				_lfsr = 0x7FFF;
				_rate = AdsrTable[_release];
				_release = 0;
				_state = StateRelease;
				_sustain = 0;
			}

			private void UpdateExpCounter()
			{

				{
					for (var i = 0; i < 7; i++)
					{
						if (_envCounter == ExpCounterTable[i])
							_expPeriod = ExpPeriodTable[i];
					}
					if (_envCounter == 0)
						_freeze = true;
				}
			}

			// ------------------------------------

			public int Attack
			{
				get
				{
					return _attack;
				}
				set
				{
					_attack = value & 0xF;
					if (_state == StateAttack)
						_rate = AdsrTable[_attack];
				}
			}

			public int Decay
			{
				get
				{
					return _decay;
				}
				set
				{
					_decay = value & 0xF;
					if (_state == StateDecay)
						_rate = AdsrTable[_decay];
				}
			}

			public bool Gate
			{
				get
				{
					return _gate;
				}
				set
				{
					var nextGate = value;
					if (nextGate && !_gate)
					{
						_state = StateAttack;
						_rate = AdsrTable[_attack];
						_delay = true;
						_freeze = false;
					}
					else if (!nextGate && _gate)
					{
						_state = StateRelease;
						_rate = AdsrTable[_release];
					}
					_gate = nextGate;
				}
			}

			public int Level
			{
				get
				{
					return _envCounter;
				}
			}

			public int Release
			{
				get
				{
					return _release;
				}
				set
				{
					_release = value & 0xF;
					if (_state == StateRelease)
						_rate = AdsrTable[_release];
				}
			}

			public int Sustain
			{
				get
				{
					return _sustain;
				}
				set
				{
					_sustain = value & 0xF;
				}
			}

			// ------------------------------------

			public void SyncState(Serializer ser)
			{
				SaveState.SyncObject(ser, this);
			}

			// ------------------------------------
		}
	}
}