forked from sin365/AxibugEmuOnline
完美音效!
This commit is contained in:
parent
5f6687d8fc
commit
cad555d7ab
2
.gitignore
vendored
2
.gitignore
vendored
@ -11,3 +11,5 @@
|
|||||||
/AxibugEmuOnline.Client/ProjectSettings/Packages/
|
/AxibugEmuOnline.Client/ProjectSettings/Packages/
|
||||||
/AxibugEmuOnline.Web/config.cfg
|
/AxibugEmuOnline.Web/config.cfg
|
||||||
/AxibugEmuOnline.Client/ProjectSettings/ProjectVersion.txt
|
/AxibugEmuOnline.Client/ProjectSettings/ProjectVersion.txt
|
||||||
|
/AxibugEmuOnline.Client/ProjectSettings/AutoStreamingSettings.asset
|
||||||
|
/AxibugEmuOnline.Client/Logs
|
||||||
|
@ -5169,6 +5169,7 @@ namespace MyNes.Core
|
|||||||
{
|
{
|
||||||
Tracer.WriteLine("Running in a thread ... using custom frame limiter.");
|
Tracer.WriteLine("Running in a thread ... using custom frame limiter.");
|
||||||
FrameLimiterEnabled = true;
|
FrameLimiterEnabled = true;
|
||||||
|
currentFrame = 0;
|
||||||
mainThread = new Thread(EmuClock);
|
mainThread = new Thread(EmuClock);
|
||||||
mainThread.Start();
|
mainThread.Start();
|
||||||
}
|
}
|
||||||
@ -5263,9 +5264,6 @@ namespace MyNes.Core
|
|||||||
if (mainThread != null)
|
if (mainThread != null)
|
||||||
{
|
{
|
||||||
Tracer.WriteLine("Aborting thread ..");
|
Tracer.WriteLine("Aborting thread ..");
|
||||||
while (mainThread.IsAlive)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
mainThread.Abort();
|
mainThread.Abort();
|
||||||
mainThread = null;
|
mainThread = null;
|
||||||
}
|
}
|
||||||
@ -5274,43 +5272,29 @@ namespace MyNes.Core
|
|||||||
NesEmu.EmuShutdown?.Invoke(null, new EventArgs());
|
NesEmu.EmuShutdown?.Invoke(null, new EventArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void EMUClockFrame()
|
private static Stopwatch sw = new Stopwatch();
|
||||||
{
|
private static double fixTime;
|
||||||
emu_frame_done = false;
|
public static int currentFrame;
|
||||||
while (!emu_frame_done && ON)
|
|
||||||
{
|
|
||||||
if (!PAUSED)
|
|
||||||
{
|
|
||||||
CPUClock();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Thread.Sleep(100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ExecuteOneFrame()
|
|
||||||
{
|
|
||||||
while (!ppu_frame_finished)
|
|
||||||
{
|
|
||||||
CPUClock();
|
|
||||||
}
|
|
||||||
|
|
||||||
FrameFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EmuClock()
|
private static void EmuClock()
|
||||||
{
|
{
|
||||||
while (ON)
|
while (ON)
|
||||||
{
|
{
|
||||||
if (!PAUSED)
|
if (!PAUSED)
|
||||||
{
|
{
|
||||||
CPUClock();
|
var waitTime = GetTime() + fps_time_period + fixTime;
|
||||||
if (ppu_frame_finished)
|
|
||||||
|
while (!ppu_frame_finished)
|
||||||
|
CPUClock();
|
||||||
|
|
||||||
|
FrameFinished();
|
||||||
|
|
||||||
|
fixTime = waitTime - GetTime();
|
||||||
|
while (fixTime > 0)
|
||||||
{
|
{
|
||||||
FrameFinished();
|
fixTime = waitTime - GetTime();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
render_audio_get_is_playing(out render_audio_is_playing);
|
render_audio_get_is_playing(out render_audio_is_playing);
|
||||||
@ -5408,24 +5392,6 @@ namespace MyNes.Core
|
|||||||
audio_timer = 0.0;
|
audio_timer = 0.0;
|
||||||
}
|
}
|
||||||
fps_time_token = GetTime() - fps_time_start;
|
fps_time_token = GetTime() - fps_time_start;
|
||||||
if (FrameLimiterEnabled)
|
|
||||||
{
|
|
||||||
if (fps_time_token > 0.0)
|
|
||||||
{
|
|
||||||
fps_time_dead = fps_time_period - fps_time_token;
|
|
||||||
if (fps_time_dead > 0.0)
|
|
||||||
{
|
|
||||||
Thread.Sleep((int)Math.Floor(fps_time_dead * 1000.0));
|
|
||||||
fps_time_dead = GetTime() - fps_time_start;
|
|
||||||
while (fps_time_period - fps_time_dead > 0.0)
|
|
||||||
{
|
|
||||||
fps_time_dead = GetTime() - fps_time_start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fps_time_last = GetTime();
|
|
||||||
fps_time_frame_time = fps_time_last - fps_time_start;
|
|
||||||
}
|
|
||||||
fps_time_start = GetTime();
|
fps_time_start = GetTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5445,6 +5411,11 @@ namespace MyNes.Core
|
|||||||
fps_time_period = period;
|
fps_time_period = period;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RevertFramePeriod()
|
||||||
|
{
|
||||||
|
fps_time_period = 1 / emu_time_target_fps;
|
||||||
|
}
|
||||||
|
|
||||||
public static void ApplyRegionSetting()
|
public static void ApplyRegionSetting()
|
||||||
{
|
{
|
||||||
switch ((RegionSetting)MyNesMain.EmuSettings.RegionSetting)
|
switch ((RegionSetting)MyNesMain.EmuSettings.RegionSetting)
|
||||||
|
BIN
AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes
Normal file
BIN
AxibugEmuOnline.Client/Assets/Resources/Roms/tortoise4.nes.bytes
Normal file
Binary file not shown.
@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4933f61382c34574db545f3d9e72b51d
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -815,7 +815,7 @@ GameObject:
|
|||||||
m_Icon: {fileID: 0}
|
m_Icon: {fileID: 0}
|
||||||
m_NavMeshLayer: 0
|
m_NavMeshLayer: 0
|
||||||
m_StaticEditorFlags: 0
|
m_StaticEditorFlags: 0
|
||||||
m_IsActive: 1
|
m_IsActive: 0
|
||||||
--- !u!224 &2100984176
|
--- !u!224 &2100984176
|
||||||
RectTransform:
|
RectTransform:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
|
@ -23,7 +23,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
private AudioSource m_as;
|
private AudioSource m_as;
|
||||||
|
|
||||||
private Stopwatch sw = Stopwatch.StartNew();
|
private Stopwatch sw = Stopwatch.StartNew();
|
||||||
private Queue<short> _buffer = new Queue<short>(2048);
|
private RingBuffer<short> _buffer = new RingBuffer<short>(4096);
|
||||||
|
|
||||||
public double FPS { get; private set; }
|
public double FPS { get; private set; }
|
||||||
|
|
||||||
@ -43,9 +43,27 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
int step = channels;
|
int step = channels;
|
||||||
|
|
||||||
|
var bufferCount = _buffer.Available();
|
||||||
|
if (bufferCount < 4096)
|
||||||
|
{
|
||||||
|
double fps = 1 / 61d;
|
||||||
|
NesEmu.SetFramePeriod(ref fps);
|
||||||
|
}
|
||||||
|
else if (bufferCount > 8124)
|
||||||
|
{
|
||||||
|
double fps = 1 / 59d;
|
||||||
|
NesEmu.SetFramePeriod(ref fps);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NesEmu.RevertFramePeriod();
|
||||||
|
}
|
||||||
for (int i = 0; i < data.Length; i += step)
|
for (int i = 0; i < data.Length; i += step)
|
||||||
{
|
{
|
||||||
var rawFloat = _buffer.Count <= 0 ? lastData : _buffer.Dequeue() / 124f;
|
float rawFloat = lastData;
|
||||||
|
if (_buffer.TryRead(out short rawData))
|
||||||
|
rawFloat = rawData / 124f;
|
||||||
|
|
||||||
data[i] = rawFloat;
|
data[i] = rawFloat;
|
||||||
for (int fill = 1; fill < step; fill++)
|
for (int fill = 1; fill < step; fill++)
|
||||||
data[i + fill] = rawFloat;
|
data[i + fill] = rawFloat;
|
||||||
@ -63,14 +81,9 @@ namespace AxibugEmuOnline.Client
|
|||||||
|
|
||||||
FPS = 1d / delta.TotalSeconds;
|
FPS = 1d / delta.TotalSeconds;
|
||||||
|
|
||||||
if (_buffer.Count > 2048)
|
|
||||||
{
|
|
||||||
_buffer.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < samples_a; i++)
|
for (int i = 0; i < samples_a; i++)
|
||||||
{
|
{
|
||||||
_buffer.Enqueue(buffer[i]);
|
_buffer.Write(buffer[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,14 +44,14 @@ namespace AxibugEmuOnline.Client
|
|||||||
res.b = b / 255f;
|
res.b = b / 255f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
var colors = m_texRawBuffer;
|
var colors = m_texRawBuffer;
|
||||||
m_rawBufferWarper.SetPixels(colors);
|
m_rawBufferWarper.SetPixels(colors);
|
||||||
m_rawBufferWarper.Apply();
|
m_rawBufferWarper.Apply();
|
||||||
Graphics.Blit(m_rawBufferWarper, m_drawCanvas.texture as RenderTexture);
|
Graphics.Blit(m_rawBufferWarper, m_drawCanvas.texture as RenderTexture);
|
||||||
|
|
||||||
m_fpsText.text = $"Audio:{NesCoreProxy.Instance.AudioCom.FPS}";
|
m_fpsText.text = $"fps:{NesCoreProxy.Instance.AudioCom.FPS}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteErrorNotification(string message, bool instant)
|
public void WriteErrorNotification(string message, bool instant)
|
||||||
|
@ -10,12 +10,8 @@ namespace AxibugEmuOnline.Client.Manager
|
|||||||
public void Init(IVideoProvider videoCom, IAudioProvider audioCom)
|
public void Init(IVideoProvider videoCom, IAudioProvider audioCom)
|
||||||
{
|
{
|
||||||
MyNesMain.Initialize(this, videoCom, audioCom);
|
MyNesMain.Initialize(this, videoCom, audioCom);
|
||||||
NesEmu.LoadGame("kirby.nes", out var successed, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ExecuteFrameLogic()
|
NesEmu.LoadGame("tortoise4.nes", out var successed, true);
|
||||||
{
|
|
||||||
//NesEmu.ExecuteOneFrame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -21,11 +21,6 @@ namespace AxibugEmuOnline.Client
|
|||||||
m_appEnum.Init(VideoCom, AudioCom);
|
m_appEnum.Init(VideoCom, AudioCom);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
m_appEnum.ExecuteFrameLogic();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
Instance = null;
|
Instance = null;
|
||||||
|
73
AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs
Normal file
73
AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
public class RingBuffer<T>
|
||||||
|
{
|
||||||
|
private readonly T[] buffer;
|
||||||
|
private readonly int capacity;
|
||||||
|
private int writePos;
|
||||||
|
private int readPos;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
public RingBuffer(int capacity)
|
||||||
|
{
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.buffer = new T[capacity];
|
||||||
|
this.writePos = 0;
|
||||||
|
this.readPos = 0;
|
||||||
|
this.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(T item)
|
||||||
|
{
|
||||||
|
int localWritePos;
|
||||||
|
int localReadPos;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
localWritePos = Volatile.Read(ref writePos);
|
||||||
|
localReadPos = Volatile.Read(ref readPos);
|
||||||
|
|
||||||
|
int nextWritePos = (localWritePos + 1) % capacity;
|
||||||
|
|
||||||
|
if (nextWritePos == localReadPos)
|
||||||
|
{
|
||||||
|
// 缓冲区已满,覆盖最旧的未读数据
|
||||||
|
Interlocked.CompareExchange(ref readPos, (localReadPos + 1) % capacity, localReadPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (Interlocked.CompareExchange(ref writePos, (localWritePos + 1) % capacity, localWritePos) != localWritePos);
|
||||||
|
|
||||||
|
buffer[localWritePos] = item;
|
||||||
|
Interlocked.Increment(ref count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryRead(out T item)
|
||||||
|
{
|
||||||
|
item = default(T);
|
||||||
|
|
||||||
|
int localReadPos;
|
||||||
|
int localWritePos;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
localReadPos = Volatile.Read(ref readPos);
|
||||||
|
localWritePos = Volatile.Read(ref writePos);
|
||||||
|
|
||||||
|
if (localReadPos == localWritePos)
|
||||||
|
{
|
||||||
|
return false; // 缓冲区为空
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (Interlocked.CompareExchange(ref readPos, (localReadPos + 1) % capacity, localReadPos) != localReadPos);
|
||||||
|
|
||||||
|
item = buffer[localReadPos];
|
||||||
|
Interlocked.Decrement(ref count);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Available()
|
||||||
|
{
|
||||||
|
return Volatile.Read(ref count);
|
||||||
|
}
|
||||||
|
}
|
11
AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs.meta
Normal file
11
AxibugEmuOnline.Client/Assets/Script/RingBuffer.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6bd0f6c1647ed3f49a59e7f06406f49b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
Loading…
Reference in New Issue
Block a user