using Essgee.Utilities; using System; using System.Runtime.InteropServices; namespace Essgee.Emulation.Audio { public partial class DMGAudio { public unsafe class Square : IDMGAudioChannel { //static readonly bool[,] dutyCycleTable = new bool[,] // { // { false, false, false, false, false, false, false, true, }, // 00000001 12.5% // { true, false, false, false, false, false, false, true, }, // 10000001 25% // { true, false, false, false, false, true, true, true, }, // 10000111 50% // { false, true, true, true, true, true, true, false, } // 01111110 75% //}; // 1. 初始化 - 假设原始数组是 4行 x 8列 public const int Rows = 4; public const int Cols = 8; //private readonly bool[] _dutyCycleTable1D = new bool[Rows * Cols] //{ // // 第一行 (索引 0-7) // false, false, false, false, false, false, false, true, // // 第二行 (索引 8-15) // true, false, false, false, false, false, false, true, // // 第三行 (索引 16-23) // true, false, false, false, false, true, true, true, // // 第四行 (索引 24-31) // false, true, true, true, true, true, true, false //}; #region //指针化 _dutyCycleTable1D bool[] _dutyCycleTable1D_src; GCHandle _dutyCycleTable1D_handle; public bool* _dutyCycleTable1D; public int _dutyCycleTable1DLength; public bool _dutyCycleTable1D_IsNull => _dutyCycleTable1D == null; public bool[] _dutyCycleTable1D_set { set { _dutyCycleTable1D_handle.ReleaseGCHandle(); _dutyCycleTable1D_src = value; _dutyCycleTable1DLength = value.Length; _dutyCycleTable1D_src.GetObjectPtr(ref _dutyCycleTable1D_handle, ref _dutyCycleTable1D); } } #endregion // NR10/20 byte sweepPeriodReload, sweepShift; bool sweepNegate; // NR11/21 public byte dutyCycle, lengthLoad; // NR12/22 byte envelopeStartingVolume, envelopePeriodReload; bool envelopeAddMode; // NR13/23 public byte frequencyLSB; // NR14/24 public bool trigger, lengthEnable; public byte frequencyMSB; // readonly bool channelSupportsSweep; // Sweep bool isSweepEnabled; int sweepCounter, sweepFreqShadow; // Frequency public int frequencyCounter; // Envelope public int volume, envelopeCounter; bool isEnvelopeUpdateEnabled; // Misc public bool isChannelEnabled, isDacEnabled; public int lengthCounter, dutyCounter; //public int OutputVolume { get; private set; } public override bool IsActive { get { return lengthCounter != 0; } } public Square(bool hasSweep) { channelSupportsSweep = hasSweep; //初始化一下 _dutyCycleTable1D_set = new bool[Rows * Cols] { // 第一行 (索引 0-7) false, false, false, false, false, false, false, true, // 第二行 (索引 8-15) true, false, false, false, false, false, false, true, // 第三行 (索引 16-23) true, false, false, false, false, true, true, true, // 第四行 (索引 24-31) false, true, true, true, true, true, true, false }; } public override void Reset() { isSweepEnabled = false; sweepCounter = sweepFreqShadow = 0; frequencyCounter = 0; volume = 15; envelopeCounter = 0; isEnvelopeUpdateEnabled = false; isChannelEnabled = isDacEnabled = false; lengthCounter = dutyCounter = 0; OutputVolume = volume; } public override void LengthCounterClock() { if (lengthCounter > 0 && lengthEnable) { lengthCounter--; if (lengthCounter == 0) isChannelEnabled = false; } } public override void SweepClock() { if (!channelSupportsSweep) return; sweepCounter--; if (sweepCounter == 0) { sweepCounter = sweepPeriodReload; if (isSweepEnabled && sweepPeriodReload != 0) { var newFrequency = PerformSweepCalculations(); if (newFrequency <= 2047 && sweepShift != 0) { sweepFreqShadow = newFrequency; frequencyMSB = (byte)((newFrequency >> 8) & 0b111); frequencyLSB = (byte)(newFrequency & 0xFF); PerformSweepCalculations(); } } } } public override void VolumeEnvelopeClock() { envelopeCounter--; if (envelopeCounter == 0) { envelopeCounter = envelopePeriodReload; if (isEnvelopeUpdateEnabled) { var newVolume = volume; if (envelopeAddMode) newVolume++; else newVolume--; if (newVolume >= 0 && newVolume <= 15) volume = newVolume; else isEnvelopeUpdateEnabled = false; } } } public override void Step() { if (!isChannelEnabled) return; frequencyCounter--; if (frequencyCounter == 0) { frequencyCounter = (2048 - ((frequencyMSB << 8) | frequencyLSB)) * 4; dutyCounter++; dutyCounter %= 8; } //OutputVolume = isDacEnabled && dutyCycleTable[dutyCycle, dutyCounter] ? volume : 0; //改为一维数组访问 //OutputVolume = isDacEnabled && _dutyCycleTable1D[dutyCycle * Cols + dutyCounter] ? volume : 0; OutputVolume = isDacEnabled && *(_dutyCycleTable1D+(dutyCycle * Cols + dutyCounter)) ? volume : 0; } private void Trigger() { isChannelEnabled = true; if (lengthCounter == 0) lengthCounter = 64; frequencyCounter = (2048 - ((frequencyMSB << 8) | frequencyLSB)) * 4; volume = envelopeStartingVolume; envelopeCounter = envelopePeriodReload; isEnvelopeUpdateEnabled = true; if (channelSupportsSweep) { sweepFreqShadow = (frequencyMSB << 8) | frequencyLSB; sweepCounter = sweepPeriodReload; isSweepEnabled = sweepPeriodReload != 0 || sweepShift != 0; if (sweepShift != 0) PerformSweepCalculations(); } } private int PerformSweepCalculations() { var newFrequency = sweepFreqShadow >> sweepShift; if (sweepNegate) newFrequency = -newFrequency; newFrequency += sweepFreqShadow; if (newFrequency > 2047) isChannelEnabled = false; return newFrequency; } public override void WritePort(byte port, byte value) { switch (port) { case 0: if (channelSupportsSweep) { sweepPeriodReload = (byte)((value >> 4) & 0b111); sweepNegate = ((value >> 3) & 0b1) == 0b1; sweepShift = (byte)((value >> 0) & 0b111); } break; case 1: dutyCycle = (byte)((value >> 6) & 0b11); lengthLoad = (byte)((value >> 0) & 0b111111); lengthCounter = 64 - lengthLoad; break; case 2: envelopeStartingVolume = (byte)((value >> 4) & 0b1111); envelopeAddMode = ((value >> 3) & 0b1) == 0b1; envelopePeriodReload = (byte)((value >> 0) & 0b111); isDacEnabled = ((value >> 3) & 0b11111) != 0; break; case 3: frequencyLSB = value; break; case 4: trigger = ((value >> 7) & 0b1) == 0b1; lengthEnable = ((value >> 6) & 0b1) == 0b1; frequencyMSB = (byte)((value >> 0) & 0b111); if (trigger) Trigger(); break; } } public override byte ReadPort(byte port) { switch (port) { case 0: if (channelSupportsSweep) { return (byte)( 0x80 | (sweepPeriodReload << 4) | (sweepNegate ? (1 << 3) : 0) | (sweepShift << 0)); } else return 0xFF; case 1: return (byte)( 0x3F | (dutyCycle << 6)); case 2: return (byte)( (envelopeStartingVolume << 4) | (envelopeAddMode ? (1 << 3) : 0) | (envelopePeriodReload << 0)); case 4: return (byte)( 0xBF | (lengthEnable ? (1 << 6) : 0)); default: return 0xFF; } } public override void WriteWaveRam(byte offset, byte value) { throw new Exception("Channel type does have Wave RAM"); } public override byte ReadWaveRam(byte offset) { throw new Exception("Channel type does have Wave RAM"); } } } }