Essgee:指针化优化 DMG音频处理效率

This commit is contained in:
sin365 2025-12-09 21:25:09 +08:00
parent f53fcbfee0
commit b4261139ad
7 changed files with 504 additions and 88 deletions

View File

@ -20,6 +20,12 @@ namespace Essgee.Utilities
GetObjectPtr(srcObj, ref handle, out intptr); GetObjectPtr(srcObj, ref handle, out intptr);
ptr = (uint*)intptr; ptr = (uint*)intptr;
} }
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref bool* ptr)
{
IntPtr intptr;
GetObjectPtr(srcObj, ref handle, out intptr);
ptr = (bool*)intptr;
}
public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref short* ptr) public static void GetObjectPtr(this object srcObj, ref GCHandle handle, ref short* ptr)
{ {

View File

@ -5,14 +5,19 @@
//本身wave就继承了IDMGAudioChannel //本身wave就继承了IDMGAudioChannel
public class CGBWave : Wave//, IDMGAudioChannel public class CGBWave : Wave//, IDMGAudioChannel
{ {
public override void Reset() public unsafe override void Reset()
{ {
base.Reset(); base.Reset();
for (var i = 0; i < sampleBuffer.Length; i += 2) //for (var i = 0; i < sampleBuffer.Length; i += 2)
//{
// sampleBuffer[i + 0] = 0x00;
// sampleBuffer[i + 1] = 0xFF;
//}
for (var i = 0; i < sampleBufferLength; i += 2)
{ {
sampleBuffer[i + 0] = 0x00; *(sampleBuffer + i) = 0x00;
sampleBuffer[i + 1] = 0xFF; *(sampleBuffer + i + 1) = 0xFF;
} }
} }
} }

View File

@ -1,15 +1,36 @@
using System; using Essgee.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Essgee.Emulation.Audio namespace Essgee.Emulation.Audio
{ {
public partial class DMGAudio public partial class DMGAudio
{ {
public class Noise : IDMGAudioChannel public unsafe class Noise : IDMGAudioChannel
{ {
static readonly int[] divisors = new int[] //static readonly int[] divisors = new int[]
//{
// 8, 16, 32, 48, 64, 80, 96, 112
//};
#region //指针化 divisors
int[] divisors_src;
static GCHandle divisors_handle;
public int* divisors;
public int divisorsLength;
public bool divisors_IsNull => divisors == null;
public int[] divisors_set
{ {
8, 16, 32, 48, 64, 80, 96, 112 set
}; {
divisors_handle.ReleaseGCHandle();
divisors_src = value;
divisorsLength = value.Length;
divisors_src.GetObjectPtr(ref divisors_handle, ref divisors);
}
}
#endregion
// NR41 // NR41
byte lengthLoad; byte lengthLoad;
@ -23,7 +44,7 @@ namespace Essgee.Emulation.Audio
bool lfsrWidthMode; bool lfsrWidthMode;
// NR44 // NR44
bool trigger, lengthEnable; public bool trigger, lengthEnable;
// //
@ -36,8 +57,8 @@ namespace Essgee.Emulation.Audio
bool isEnvelopeUpdateEnabled; bool isEnvelopeUpdateEnabled;
// Misc // Misc
bool isChannelEnabled, isDacEnabled; public bool isChannelEnabled, isDacEnabled;
int lengthCounter; public int lengthCounter;
//public int OutputVolume { get; private set; } //public int OutputVolume { get; private set; }
@ -46,6 +67,12 @@ namespace Essgee.Emulation.Audio
public Noise() public Noise()
{ {
// //
//初始化一下
divisors_set = new int[]
{
8, 16, 32, 48, 64, 80, 96, 112
};
} }
public override void Reset() public override void Reset()
@ -106,7 +133,8 @@ namespace Essgee.Emulation.Audio
noiseCounter--; noiseCounter--;
if (noiseCounter == 0) if (noiseCounter == 0)
{ {
noiseCounter = divisors[divisorCode] << clockShift; //noiseCounter = divisors[divisorCode] << clockShift;
noiseCounter = *(divisors + divisorCode) << clockShift;
var result = (lfsr & 0b1) ^ ((lfsr >> 1) & 0b1); var result = (lfsr & 0b1) ^ ((lfsr >> 1) & 0b1);
lfsr = (ushort)((lfsr >> 1) | (result << 14)); lfsr = (ushort)((lfsr >> 1) | (result << 14));
@ -124,7 +152,8 @@ namespace Essgee.Emulation.Audio
if (lengthCounter == 0) lengthCounter = 64; if (lengthCounter == 0) lengthCounter = 64;
noiseCounter = divisors[divisorCode] << clockShift; //noiseCounter = divisors[divisorCode] << clockShift;
noiseCounter = *(divisors + divisorCode) << clockShift;
volume = envelopeStartingVolume; volume = envelopeStartingVolume;
envelopeCounter = envelopePeriodReload; envelopeCounter = envelopePeriodReload;
isEnvelopeUpdateEnabled = true; isEnvelopeUpdateEnabled = true;

View File

@ -1,10 +1,12 @@
using System; using Essgee.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Essgee.Emulation.Audio namespace Essgee.Emulation.Audio
{ {
public partial class DMGAudio public partial class DMGAudio
{ {
public class Square : IDMGAudioChannel public unsafe class Square : IDMGAudioChannel
{ {
//static readonly bool[,] dutyCycleTable = new bool[,] //static readonly bool[,] dutyCycleTable = new bool[,]
// { // {
@ -15,45 +17,56 @@ namespace Essgee.Emulation.Audio
//}; //};
// 1. 初始化 - 假设原始数组是 4行 x 8列 // 1. 初始化 - 假设原始数组是 4行 x 8列
private const int Rows = 4; public const int Rows = 4;
private const int Cols = 8; public const int Cols = 8;
private readonly bool[] _dutyCycleTable1D = new bool[Rows * Cols] //private readonly bool[] _dutyCycleTable1D = new bool[Rows * Cols]
{ //{
// 第一行 (索引 0-7) // // 第一行 (索引 0-7)
false, false, false, false, false, false, false, true, // false, false, false, false, false, false, false, true,
// 第二行 (索引 8-15) // // 第二行 (索引 8-15)
true, false, false, false, false, false, false, true, // true, false, false, false, false, false, false, true,
// 第三行 (索引 16-23) // // 第三行 (索引 16-23)
true, false, false, false, false, true, true, true, // true, false, false, false, false, true, true, true,
// 第四行 (索引 24-31) // // 第四行 (索引 24-31)
false, true, true, true, true, true, true, false // false, true, true, true, true, true, true, false
}; //};
// 2. 访问方法 - 替代原来的 dutyCycleTable[row, col]
public bool GetValue(int row, int col) #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
// if (row < 0 || row >= Rows || col < 0 || col >= Cols) return false; {
return _dutyCycleTable1D[row * Cols + col]; _dutyCycleTable1D_handle.ReleaseGCHandle();
_dutyCycleTable1D_src = value;
_dutyCycleTable1DLength = value.Length;
_dutyCycleTable1D_src.GetObjectPtr(ref _dutyCycleTable1D_handle, ref _dutyCycleTable1D);
} }
}
#endregion
// NR10/20 // NR10/20
byte sweepPeriodReload, sweepShift; byte sweepPeriodReload, sweepShift;
bool sweepNegate; bool sweepNegate;
// NR11/21 // NR11/21
byte dutyCycle, lengthLoad; public byte dutyCycle, lengthLoad;
// NR12/22 // NR12/22
byte envelopeStartingVolume, envelopePeriodReload; byte envelopeStartingVolume, envelopePeriodReload;
bool envelopeAddMode; bool envelopeAddMode;
// NR13/23 // NR13/23
byte frequencyLSB; public byte frequencyLSB;
// NR14/24 // NR14/24
bool trigger, lengthEnable; public bool trigger, lengthEnable;
byte frequencyMSB; public byte frequencyMSB;
// //
@ -64,15 +77,15 @@ namespace Essgee.Emulation.Audio
int sweepCounter, sweepFreqShadow; int sweepCounter, sweepFreqShadow;
// Frequency // Frequency
int frequencyCounter; public int frequencyCounter;
// Envelope // Envelope
int volume, envelopeCounter; public int volume, envelopeCounter;
bool isEnvelopeUpdateEnabled; bool isEnvelopeUpdateEnabled;
// Misc // Misc
bool isChannelEnabled, isDacEnabled; public bool isChannelEnabled, isDacEnabled;
int lengthCounter, dutyCounter; public int lengthCounter, dutyCounter;
//public int OutputVolume { get; private set; } //public int OutputVolume { get; private set; }
@ -81,6 +94,19 @@ namespace Essgee.Emulation.Audio
public Square(bool hasSweep) public Square(bool hasSweep)
{ {
channelSupportsSweep = 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() public override void Reset()
@ -169,7 +195,8 @@ namespace Essgee.Emulation.Audio
//OutputVolume = isDacEnabled && dutyCycleTable[dutyCycle, dutyCounter] ? volume : 0; //OutputVolume = isDacEnabled && dutyCycleTable[dutyCycle, dutyCounter] ? volume : 0;
//改为一维数组访问 //改为一维数组访问
OutputVolume = isDacEnabled && _dutyCycleTable1D[dutyCycle * Cols + dutyCounter] ? volume : 0; //OutputVolume = isDacEnabled && _dutyCycleTable1D[dutyCycle * Cols + dutyCounter] ? volume : 0;
OutputVolume = isDacEnabled && *(_dutyCycleTable1D+(dutyCycle * Cols + dutyCounter)) ? volume : 0;
} }
private void Trigger() private void Trigger()

View File

@ -1,8 +1,10 @@
using System; using Essgee.Utilities;
using System;
using System.Runtime.InteropServices;
namespace Essgee.Emulation.Audio namespace Essgee.Emulation.Audio
{ {
public partial class DMGAudio public unsafe partial class DMGAudio
{ {
public class Wave : IDMGAudioChannel public class Wave : IDMGAudioChannel
{ {
@ -19,16 +21,35 @@ namespace Essgee.Emulation.Audio
byte frequencyLSB; byte frequencyLSB;
// NR34 // NR34
bool trigger, lengthEnable; public bool trigger, lengthEnable;
byte frequencyMSB; byte frequencyMSB;
// Wave // Wave
protected byte[] sampleBuffer; //protected byte[] sampleBuffer;
#region //指针化 sampleBuffer
byte[] sampleBuffer_src;
GCHandle sampleBuffer_handle;
public byte* sampleBuffer;
public int sampleBufferLength;
public bool sampleBuffer_IsNull => sampleBuffer == null;
public byte[] sampleBuffer_set
{
set
{
sampleBuffer_handle.ReleaseGCHandle();
sampleBuffer_src = value;
sampleBufferLength = value.Length;
sampleBuffer_src.GetObjectPtr(ref sampleBuffer_handle, ref sampleBuffer);
}
}
#endregion
int frequencyCounter, positionCounter, volume; int frequencyCounter, positionCounter, volume;
// Misc // Misc
bool isChannelEnabled; public bool isChannelEnabled;
int lengthCounter; public int lengthCounter;
//public int OutputVolume { get; private set; } //public int OutputVolume { get; private set; }
@ -36,12 +57,15 @@ namespace Essgee.Emulation.Audio
public Wave() public Wave()
{ {
sampleBuffer = new byte[16]; //sampleBuffer = new byte[16];
sampleBuffer_set = new byte[16];
} }
public override void Reset() public override void Reset()
{ {
for (var i = 0; i < sampleBuffer.Length; i++) sampleBuffer[i] = (byte)EmuStandInfo.Random.Next(255); //for (var i = 0; i < sampleBuffer.Length; i++) sampleBuffer[i] = (byte)EmuStandInfo.Random.Next(255);
byte* ptr = sampleBuffer;
for (var i = 0; i < sampleBufferLength; i++, ptr++) *ptr = 0;// (byte)EmuStandInfo.Random.Next(255);
frequencyCounter = positionCounter = 0; frequencyCounter = positionCounter = 0;
volume = 15; volume = 15;
@ -82,7 +106,8 @@ namespace Essgee.Emulation.Audio
positionCounter++; positionCounter++;
positionCounter %= 32; positionCounter %= 32;
var value = sampleBuffer[positionCounter / 2]; //var value = sampleBuffer[positionCounter / 2];
var value = *(sampleBuffer + (positionCounter / 2));
if ((positionCounter & 0b1) == 0) value >>= 4; if ((positionCounter & 0b1) == 0) value >>= 4;
value &= 0b1111; value &= 0b1111;
@ -168,16 +193,23 @@ namespace Essgee.Emulation.Audio
public override void WriteWaveRam(byte offset, byte value) public override void WriteWaveRam(byte offset, byte value)
{ {
//if (!isDacEnabled)
// sampleBuffer[offset & (sampleBuffer.Length - 1)] = value;
//else
// sampleBuffer[positionCounter & (sampleBuffer.Length - 1)] = value;
if (!isDacEnabled) if (!isDacEnabled)
sampleBuffer[offset & (sampleBuffer.Length - 1)] = value; *(sampleBuffer + (offset & (sampleBufferLength - 1))) = value;
else else
sampleBuffer[positionCounter & (sampleBuffer.Length - 1)] = value; *(sampleBuffer+(positionCounter & (sampleBufferLength - 1))) = value;
} }
public override byte ReadWaveRam(byte offset) public override byte ReadWaveRam(byte offset)
{ {
if (!isDacEnabled) if (!isDacEnabled)
return sampleBuffer[offset & (sampleBuffer.Length - 1)]; {
//return sampleBuffer[offset & (sampleBufferLength - 1)];
return *(sampleBuffer + (offset & (sampleBufferLength - 1)));
}
else else
return 0xFF; return 0xFF;
} }

View File

@ -19,7 +19,10 @@ namespace Essgee.Emulation.Audio
protected const string channel3OptionName = "AudioEnableCh3Wave"; protected const string channel3OptionName = "AudioEnableCh3Wave";
protected const string channel4OptionName = "AudioEnableCh4Noise"; protected const string channel4OptionName = "AudioEnableCh4Noise";
protected IDMGAudioChannel channel1, channel2, channel3, channel4; //protected IDMGAudioChannel channel1, channel2, channel3, channel4;
protected Square channel1, channel2;
protected Wave channel3;
protected Noise channel4;
// FF24 - NR50 // FF24 - NR50
byte[] volumeRightLeft; byte[] volumeRightLeft;
@ -298,6 +301,161 @@ namespace Essgee.Emulation.Audio
//独立声明,不在函数内部 //独立声明,不在函数内部
private bool[] channelEnableFlags = new bool[4]; private bool[] channelEnableFlags = new bool[4];
//public void Step(int clockCyclesInStep)
//{
// if (!isSoundHwEnabled) return;
// sampleCycleCount += clockCyclesInStep;
// frameCycleCount += clockCyclesInStep;
// for (int i = 0; i < clockCyclesInStep; i++)
// {
// frameSequencerCounter--;
// if (frameSequencerCounter == 0)
// {
// frameSequencerCounter = frameSequencerReload;
// switch (frameSequencer)
// {
// case 0:
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 1:
// break;
// case 2:
// channel1.SweepClock();
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 3:
// break;
// case 4:
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 5:
// break;
// case 6:
// channel1.SweepClock();
// channel1.LengthCounterClock();
// channel2.LengthCounterClock();
// channel3.LengthCounterClock();
// channel4.LengthCounterClock();
// break;
// case 7:
// channel1.VolumeEnvelopeClock();
// channel2.VolumeEnvelopeClock();
// channel4.VolumeEnvelopeClock();
// break;
// }
// frameSequencer++;
// if (frameSequencer >= 8)
// frameSequencer = 0;
// }
// //channel1.Step();
// //channel2.Step();
// //channel3.Step();
// //channel4.Step();
// //手动内联
// //channel1.Step();
// if (channel1.isChannelEnabled)
// {
// channel1.frequencyCounter--;
// if (channel1.frequencyCounter == 0)
// {
// channel1.frequencyCounter = (2048 - ((channel1.frequencyMSB << 8) | channel1.frequencyLSB)) * 4;
// channel1.dutyCounter++;
// channel1.dutyCounter %= 8;
// }
// channel1.OutputVolume = channel1.isDacEnabled && *(channel1._dutyCycleTable1D + (channel1.dutyCycle * Square.Cols + channel1.dutyCounter)) ? channel1.volume : 0;
// }
// //channel2.Step();
// if (channel2.isChannelEnabled)
// {
// channel2.frequencyCounter--;
// if (channel2.frequencyCounter == 0)
// {
// channel2.frequencyCounter = (2048 - ((channel2.frequencyMSB << 8) | channel2.frequencyLSB)) * 4;
// channel2.dutyCounter++;
// channel2.dutyCounter %= 8;
// }
// channel2.OutputVolume = channel2.isDacEnabled && *(channel2._dutyCycleTable1D + (channel2.dutyCycle * Square.Cols + channel2.dutyCounter)) ? channel2.volume : 0;
// }
// channel3.Step();
// channel4.Step();
// }
// if (sampleCycleCount >= cyclesPerSample)
// {
// GenerateSample();
// sampleCycleCount -= cyclesPerSample;
// }
// //if (mixedSampleBuffer.Count >= (samplesPerFrame * numOutputChannels))
// if (mixedSampleBuffer_writePos >= (samplesPerFrame * numOutputChannels))
// {
// //EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
// // numChannels,
// // channelSampleBuffer.Select(x => x.ToArray()).ToArray(),
// // new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
// // mixedSampleBuffer.ToArray());
// //有GC
// //EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
// // numChannels,
// // channelSampleBuffer,
// // new bool[] { !channel1ForceEnable, !channel2ForceEnable, !channel3ForceEnable, !channel4ForceEnable },
// // mixedSampleBuffer,
// // mixedSampleBuffer_writePos);
// // 在函数中使用
// channelEnableFlags[0] = !channel1ForceEnable;
// channelEnableFlags[1] = !channel2ForceEnable;
// channelEnableFlags[2] = !channel3ForceEnable;
// channelEnableFlags[3] = !channel4ForceEnable;
// EnqueueSamplesEventArgs eventArgs = EnqueueSamplesEventArgs.Create(
// numChannels,
// channelSampleBuffer,
// channelEnableFlags,
// mixedSampleBuffer,
// mixedSampleBuffer_writePos);
// OnEnqueueSamples(eventArgs);
// FlushSamples();
// eventArgs.Release();
// }
// if (frameCycleCount >= cyclesPerFrame)
// {
// frameCycleCount -= cyclesPerFrame;
// sampleCycleCount = frameCycleCount;
// }
//}
//手动内联
public void Step(int clockCyclesInStep) public void Step(int clockCyclesInStep)
{ {
if (!isSoundHwEnabled) return; if (!isSoundHwEnabled) return;
@ -315,10 +473,34 @@ namespace Essgee.Emulation.Audio
switch (frameSequencer) switch (frameSequencer)
{ {
case 0: case 0:
channel1.LengthCounterClock(); //channel1.LengthCounterClock();
channel2.LengthCounterClock(); if (channel1.lengthCounter > 0 && channel1.lengthEnable)
channel3.LengthCounterClock(); {
channel4.LengthCounterClock(); channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break; break;
case 1: case 1:
@ -326,20 +508,68 @@ namespace Essgee.Emulation.Audio
case 2: case 2:
channel1.SweepClock(); channel1.SweepClock();
channel1.LengthCounterClock(); //channel1.LengthCounterClock();
channel2.LengthCounterClock(); if (channel1.lengthCounter > 0 && channel1.lengthEnable)
channel3.LengthCounterClock(); {
channel4.LengthCounterClock(); channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break; break;
case 3: case 3:
break; break;
case 4: case 4:
channel1.LengthCounterClock(); //channel1.LengthCounterClock();
channel2.LengthCounterClock(); if (channel1.lengthCounter > 0 && channel1.lengthEnable)
channel3.LengthCounterClock(); {
channel4.LengthCounterClock(); channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break; break;
case 5: case 5:
@ -347,10 +577,34 @@ namespace Essgee.Emulation.Audio
case 6: case 6:
channel1.SweepClock(); channel1.SweepClock();
channel1.LengthCounterClock(); //channel1.LengthCounterClock();
channel2.LengthCounterClock(); if (channel1.lengthCounter > 0 && channel1.lengthEnable)
channel3.LengthCounterClock(); {
channel4.LengthCounterClock(); channel1.lengthCounter--;
if (channel1.lengthCounter == 0)
channel1.isChannelEnabled = false;
}
//channel2.LengthCounterClock();
if (channel2.lengthCounter > 0 && channel2.lengthEnable)
{
channel2.lengthCounter--;
if (channel2.lengthCounter == 0)
channel2.isChannelEnabled = false;
}
//channel3.LengthCounterClock();
if (channel3.lengthCounter > 0 && channel3.lengthEnable)
{
channel3.lengthCounter--;
if (channel3.lengthCounter == 0)
channel3.isChannelEnabled = false;
}
//channel4.LengthCounterClock();
if (channel4.lengthCounter > 0 && channel4.lengthEnable)
{
channel4.lengthCounter--;
if (channel4.lengthCounter == 0)
channel4.isChannelEnabled = false;
}
break; break;
case 7: case 7:
@ -365,8 +619,36 @@ namespace Essgee.Emulation.Audio
frameSequencer = 0; frameSequencer = 0;
} }
channel1.Step(); //channel1.Step();
channel2.Step(); //channel2.Step();
//channel3.Step();
//channel4.Step();
//手动内联
//channel1.Step();
if (channel1.isChannelEnabled)
{
channel1.frequencyCounter--;
if (channel1.frequencyCounter == 0)
{
channel1.frequencyCounter = (2048 - ((channel1.frequencyMSB << 8) | channel1.frequencyLSB)) * 4;
channel1.dutyCounter++;
channel1.dutyCounter %= 8;
}
channel1.OutputVolume = channel1.isDacEnabled && *(channel1._dutyCycleTable1D + (channel1.dutyCycle * Square.Cols + channel1.dutyCounter)) ? channel1.volume : 0;
}
//channel2.Step();
if (channel2.isChannelEnabled)
{
channel2.frequencyCounter--;
if (channel2.frequencyCounter == 0)
{
channel2.frequencyCounter = (2048 - ((channel2.frequencyMSB << 8) | channel2.frequencyLSB)) * 4;
channel2.dutyCounter++;
channel2.dutyCounter %= 8;
}
channel2.OutputVolume = channel2.isDacEnabled && *(channel2._dutyCycleTable1D + (channel2.dutyCycle * Square.Cols + channel2.dutyCounter)) ? channel2.volume : 0;
}
channel3.Step(); channel3.Step();
channel4.Step(); channel4.Step();
} }

View File

@ -106,13 +106,33 @@ namespace Essgee.Emulation.Video.Nintendo
//取值范例 colorValuesBgr[colorIndex * 3 + channelIndex]; //取值范例 colorValuesBgr[colorIndex * 3 + channelIndex];
const byte colorValuesBgr_singleLen = 3; const byte colorValuesBgr_singleLen = 3;
// 转换后的一维数组 // 转换后的一维数组
readonly byte[] colorValuesBgr = new byte[] //readonly byte[] colorValuesBgr = new byte[]
//{
///* White */ 0xF8, 0xF8, 0xF8,
///* Light gray */0x9B, 0x9B, 0x9B,
///* Dark gray */ 0x3E, 0x3E, 0x3E,
///* Black */ 0x1F, 0x1F, 0x1F
//};
#region //指针化 colorValuesBgr
byte[] colorValuesBgr_src;
GCHandle colorValuesBgr_handle;
public byte* colorValuesBgr;
public int colorValuesBgrLength;
public bool colorValuesBgr_IsNull => colorValuesBgr == null;
public byte[] colorValuesBgr_set
{ {
/* White */ 0xF8, 0xF8, 0xF8, set
/* Light gray */0x9B, 0x9B, 0x9B, {
/* Dark gray */ 0x3E, 0x3E, 0x3E, colorValuesBgr_handle.ReleaseGCHandle();
/* Black */ 0x1F, 0x1F, 0x1F colorValuesBgr_src = value;
}; colorValuesBgrLength = value.Length;
colorValuesBgr_src.GetObjectPtr(ref colorValuesBgr_handle, ref colorValuesBgr);
}
}
#endregion
protected const byte screenUsageEmpty = 0; protected const byte screenUsageEmpty = 0;
protected const byte screenUsageBackground = 1 << 0; protected const byte screenUsageBackground = 1 << 0;
@ -305,6 +325,15 @@ namespace Essgee.Emulation.Video.Nintendo
outputFramebufferCopy_set = new byte[numDisplayPixels * 4]; outputFramebufferCopy_set = new byte[numDisplayPixels * 4];
//初始化一下
colorValuesBgr_set = new byte[]
{
/* White */ 0xF8, 0xF8, 0xF8,
/* Light gray */0x9B, 0x9B, 0x9B,
/* Dark gray */ 0x3E, 0x3E, 0x3E,
/* Black */ 0x1F, 0x1F, 0x1F
};
for (var y = 0; y < displayActiveHeight; y++) for (var y = 0; y < displayActiveHeight; y++)
SetLine(y, 0xFF, 0xFF, 0xFF); SetLine(y, 0xFF, 0xFF, 0xFF);
} }
@ -734,18 +763,24 @@ namespace Essgee.Emulation.Video.Nintendo
//outputFramebuffer[address + 0] = colorValuesBgr[c & 0x03][0]; //outputFramebuffer[address + 0] = colorValuesBgr[c & 0x03][0];
//outputFramebuffer[address + 1] = colorValuesBgr[c & 0x03][1]; //outputFramebuffer[address + 1] = colorValuesBgr[c & 0x03][1];
//outputFramebuffer[address + 2] = colorValuesBgr[c & 0x03][2]; //outputFramebuffer[address + 2] = colorValuesBgr[c & 0x03][2];
outputFramebuffer[address + 0] = colorValuesBgr[(c & 0x03) * 3 + 0];
outputFramebuffer[address + 1] = colorValuesBgr[(c & 0x03) * 3 + 1]; //outputFramebuffer[address + 0] = colorValuesBgr[(c & 0x03) * 3 + 0];
outputFramebuffer[address + 2] = colorValuesBgr[(c & 0x03) * 3 + 2]; //outputFramebuffer[address + 1] = colorValuesBgr[(c & 0x03) * 3 + 1];
outputFramebuffer[address + 3] = 0xFF; //outputFramebuffer[address + 2] = colorValuesBgr[(c & 0x03) * 3 + 2];
//outputFramebuffer[address + 3] = 0xFF;
*(outputFramebuffer + address) = colorValuesBgr[(c & 0x03) * 3 + 0];
*(outputFramebuffer + address + 1) = colorValuesBgr[(c & 0x03) * 3 + 1];
*(outputFramebuffer + address + 2) = colorValuesBgr[(c & 0x03) * 3 + 2];
*(outputFramebuffer + address + 3) = 0xFF;
} }
protected virtual void WriteColorToFramebuffer(byte b, byte g, byte r, int address) protected virtual void WriteColorToFramebuffer(byte b, byte g, byte r, int address)
{ {
outputFramebuffer[address + 0] = b; *(outputFramebuffer + address) = b;
outputFramebuffer[address + 1] = g; *(outputFramebuffer + address + 1) = g;
outputFramebuffer[address + 2] = r; *(outputFramebuffer + address + 2) = r;
outputFramebuffer[address + 3] = 0xFF; *(outputFramebuffer + address + 3) = 0xFF;
} }
protected virtual void ClearScreenUsage() protected virtual void ClearScreenUsage()