2011-01-11 02:55:51 +00:00
|
|
|
|
using System;
|
2011-01-21 03:59:50 +00:00
|
|
|
|
using System.Collections.Generic;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
using System.IO;
|
2013-11-04 00:36:15 +00:00
|
|
|
|
using BizHawk.Common;
|
2011-01-11 02:55:51 +00:00
|
|
|
|
|
2013-11-04 01:39:19 +00:00
|
|
|
|
namespace BizHawk.Emulation.Common
|
2011-01-11 02:55:51 +00:00
|
|
|
|
{
|
2014-04-25 01:19:57 +00:00
|
|
|
|
[CoreAttributes("NullHawk", "")]
|
2013-12-18 19:36:17 +00:00
|
|
|
|
public class NullEmulator : IEmulator, IVideoProvider, ISyncSoundProvider, ISoundProvider
|
2011-07-11 23:26:20 +00:00
|
|
|
|
{
|
|
|
|
|
public string SystemId { get { return "NULL"; } }
|
|
|
|
|
public static readonly ControllerDefinition NullController = new ControllerDefinition { Name = "Null Controller" };
|
2011-01-12 02:05:48 +00:00
|
|
|
|
|
2013-08-24 16:54:22 +00:00
|
|
|
|
public string BoardName { get { return null; } }
|
|
|
|
|
|
2014-07-12 20:42:44 +00:00
|
|
|
|
bool frameBufferClear = true;
|
2013-11-04 03:12:50 +00:00
|
|
|
|
private readonly int[] frameBuffer = new int[256 * 192];
|
|
|
|
|
private readonly Random rand = new Random();
|
2012-12-10 00:43:43 +00:00
|
|
|
|
public CoreComm CoreComm { get; private set; }
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public IVideoProvider VideoProvider { get { return this; } }
|
|
|
|
|
public ISoundProvider SoundProvider { get { return this; } }
|
2013-12-18 19:36:17 +00:00
|
|
|
|
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
|
sound api changes. added a new ISyncSoundProvider, which works similarly to ISoundProvider except the source (not the sink) determines the number of samples to process. Added facilities to metaspu, dcfilter, speexresampler to work with ISyncSoundProvider. Add ISyncSoundProvider to IEmulator. All IEmulators must provide sync sound, but they need not provide async sound. When async is needed and an IEmulator doesn't provide it, the frontend will wrap it in a vecna metaspu. SNES, GB changed to provide sync sound only. All other emulator cores mostly unchanged; they just provide stub fakesync alongside async, for now. For the moment, the only use of the sync sound is for realtime audio throttling, where it works and sounds quite nice. In the future, sync sound will be supported for AV dumping as well.
2012-10-11 00:44:59 +00:00
|
|
|
|
public bool StartAsyncSound() { return true; }
|
|
|
|
|
public void EndAsyncSound() { }
|
2013-12-18 19:36:17 +00:00
|
|
|
|
|
2012-12-10 00:43:43 +00:00
|
|
|
|
public NullEmulator(CoreComm comm)
|
2011-07-11 23:26:20 +00:00
|
|
|
|
{
|
2012-12-10 00:43:43 +00:00
|
|
|
|
CoreComm = comm;
|
2013-12-18 20:37:32 +00:00
|
|
|
|
|
|
|
|
|
var d = DateTime.Now;
|
|
|
|
|
xmas = d.Month == 12 && d.Day >= 17 && d.Day <= 27;
|
|
|
|
|
if (xmas)
|
|
|
|
|
pleg = new Pleg();
|
2011-07-11 23:26:20 +00:00
|
|
|
|
}
|
2013-11-03 16:29:51 +00:00
|
|
|
|
public void ResetCounters()
|
2011-07-30 20:49:36 +00:00
|
|
|
|
{
|
|
|
|
|
Frame = 0;
|
2012-11-25 15:41:40 +00:00
|
|
|
|
// no lag frames on this stub core
|
2011-07-30 20:49:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 19:52:47 +00:00
|
|
|
|
public void FrameAdvance(bool render, bool rendersound)
|
2011-07-11 23:26:20 +00:00
|
|
|
|
{
|
|
|
|
|
if (render == false) return;
|
2014-07-12 20:42:44 +00:00
|
|
|
|
if (!CoreComm.DispSnowyNullEmulator())
|
2013-11-04 03:12:50 +00:00
|
|
|
|
{
|
2014-07-12 20:42:44 +00:00
|
|
|
|
if (frameBufferClear) return;
|
|
|
|
|
frameBufferClear = true;
|
|
|
|
|
Array.Clear(frameBuffer, 0, 256 * 192);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
frameBufferClear = false;
|
|
|
|
|
if (xmas)
|
|
|
|
|
for (int i = 0; i < 256 * 192; i++)
|
|
|
|
|
{
|
|
|
|
|
byte b = (byte)rand.Next();
|
2013-12-18 20:37:32 +00:00
|
|
|
|
frameBuffer[i] = Colors.ARGB(b, (byte)(255 - b), 0, 255);
|
2014-07-12 20:42:44 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
for (int i = 0; i < 256 * 192; i++)
|
2013-12-18 20:37:32 +00:00
|
|
|
|
frameBuffer[i] = Colors.Luminosity((byte) rand.Next());
|
2011-07-11 23:26:20 +00:00
|
|
|
|
}
|
|
|
|
|
public ControllerDefinition ControllerDefinition { get { return NullController; } }
|
|
|
|
|
public IController Controller { get; set; }
|
2011-02-26 21:36:46 +00:00
|
|
|
|
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public int Frame { get; set; }
|
|
|
|
|
public int LagCount { get { return 0; } set { return; } }
|
|
|
|
|
public bool IsLagFrame { get { return false; } }
|
2011-05-01 12:59:26 +00:00
|
|
|
|
|
2014-08-13 17:52:13 +00:00
|
|
|
|
public byte[] CloneSaveRam() { return null; }
|
2012-09-14 22:28:38 +00:00
|
|
|
|
public void StoreSaveRam(byte[] data) { }
|
2012-10-03 15:31:04 +00:00
|
|
|
|
public bool DeterministicEmulation { get { return true; } }
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public bool SaveRamModified { get; set; }
|
|
|
|
|
public void SaveStateText(TextWriter writer) { }
|
|
|
|
|
public void LoadStateText(TextReader reader) { }
|
|
|
|
|
public void SaveStateBinary(BinaryWriter writer) { }
|
|
|
|
|
public void LoadStateBinary(BinaryReader reader) { }
|
|
|
|
|
public byte[] SaveStateBinary() { return new byte[1]; }
|
2013-05-06 20:51:28 +00:00
|
|
|
|
public bool BinarySaveStatesPreferred { get { return false; } }
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public int[] GetVideoBuffer() { return frameBuffer; }
|
2013-11-06 02:15:29 +00:00
|
|
|
|
public int VirtualWidth { get { return 256; } }
|
2014-04-30 23:48:37 +00:00
|
|
|
|
public int VirtualHeight { get { return 192; } }
|
2013-11-06 02:15:29 +00:00
|
|
|
|
public int BufferWidth { get { return 256; } }
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public int BufferHeight { get { return 192; } }
|
|
|
|
|
public int BackgroundColor { get { return 0; } }
|
2014-09-01 18:43:41 +00:00
|
|
|
|
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public void Dispose() { }
|
2013-11-11 03:20:33 +00:00
|
|
|
|
|
2013-12-18 20:37:32 +00:00
|
|
|
|
bool xmas;
|
|
|
|
|
Pleg pleg;
|
2013-12-18 19:36:17 +00:00
|
|
|
|
|
|
|
|
|
short[] sampbuff = new short[735 * 2];
|
|
|
|
|
|
|
|
|
|
public void GetSamples(out short[] samples, out int nsamp)
|
|
|
|
|
{
|
|
|
|
|
nsamp = 735;
|
|
|
|
|
samples = sampbuff;
|
2014-07-12 20:42:44 +00:00
|
|
|
|
if (!CoreComm.DispSnowyNullEmulator())
|
|
|
|
|
return;
|
2013-12-18 20:37:32 +00:00
|
|
|
|
if (xmas)
|
|
|
|
|
pleg.Generate(samples);
|
2013-12-18 19:36:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DiscardSamples()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void GetSamples(short[] samples)
|
|
|
|
|
{
|
2014-07-13 17:12:26 +00:00
|
|
|
|
if (!CoreComm.DispSnowyNullEmulator())
|
|
|
|
|
return;
|
2013-12-18 20:37:32 +00:00
|
|
|
|
if (xmas)
|
|
|
|
|
pleg.Generate(samples);
|
2013-12-18 19:36:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int MaxVolume
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
set;
|
|
|
|
|
}
|
2011-07-11 23:26:20 +00:00
|
|
|
|
}
|
2011-06-02 02:59:18 +00:00
|
|
|
|
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public class NullSound : ISoundProvider
|
|
|
|
|
{
|
|
|
|
|
public static readonly NullSound SilenceProvider = new NullSound();
|
2011-06-02 02:59:18 +00:00
|
|
|
|
|
2011-07-11 23:26:20 +00:00
|
|
|
|
public void GetSamples(short[] samples) { }
|
|
|
|
|
public void DiscardSamples() { }
|
2012-02-24 20:38:35 +00:00
|
|
|
|
public int MaxVolume { get; set; }
|
2011-07-11 23:26:20 +00:00
|
|
|
|
}
|
2013-12-18 19:36:17 +00:00
|
|
|
|
|
2013-12-18 20:37:32 +00:00
|
|
|
|
#region super tone generator
|
|
|
|
|
|
2013-12-18 19:36:17 +00:00
|
|
|
|
class Pleg
|
|
|
|
|
{
|
|
|
|
|
string data = "H4sICI/2sVICAG91dDMudHh0AOxazdLbIAy8d6bvgkFImFsvufb936Yt3YyKvjBY5UvS6XDSxOZndyULy9H3ylLD1y8/baxHs/Lb5rNG2IT7zVKq9Msmrmf7Tb/st3qcP4ff7rdhb7itw04eXrVzsYWOTuXTt7yzl/OXvYHtDWwN+0cQi0IcqzJnxtchy9lDbo5rVODAAJvbdXWk1PiQooBiMBQPnxcOnYbhfkoCSgGUMmLxbgsoCSgdoCSgFEApwxZQArZ0uryWTp227DUBxVzDpbXLNUhlAVIGJELsZ6hb+kzACdePGqFqxPiE8QnjEualCcUZtb+mRKAUP0tlfyxHQAiIZUEsJ6gZYVXtTlVOiGWBmhk29qoS+zIQ6zQvJZ3rUHFtSwm9I++q5WJUS1At90mNAywhA/CqausZIPaPG/Jtgwhq6ug3qU5GdZMRMg+OmNR7IxfjjQwbDLXD5Q09Yta9QcfqKQfkz4Aw3fptrP0xNVfsCVu++j1S55KPJem01Yi2Bw/R27N2yxfj9znNI9TnESo1dikyT7J68aledNqi6vO1yjUI5RkQplu/mTWRf8u7LVTzZeXaaBRNeUxDTozimi8HRhuNqM/XJZOoiK5IeLJFOF5bEV3XSBGxeHiwjDSbaTXRBkhmuBUBU83T9IiK/wEPUmQOf3RIZxqxI2YVEQfDy7C3VZzJuWTqDuTkDzmW9PUT49KfXHIAlzD0s+qk6CJWx2ptFdzt9mqWsuYF6KT6aBoRAmWGK3MPMfEIkoHg2JIRPfajC39U1/K2TCeQ3SrqHi4V+YSK8VUq2hJoriKDd3So+NJYtBTUnvV4jaqq1omtCVYGsdi9RVmIyDdzqJoPNLdZ6O0q5MhzKh8LUAIFGQSIraFFA8VSg0QOagAJ+5xY1xpaBrGel2I9j2Nd63Kiv8u7tBDb5Mu7xaiYH6uovAcq0ttV5KIxvq6iMxb/HxV7CmpLPV6i6vhrGZdRHp5Us/SEPEwmD5eaXQEzycN5kIfZ5GHjDS7LediftAaxH/DN0r5riPWOLXld3xiI/unqWhgqnbCHieGzU8v9/YJK2wWrSqxHA0404bv+7yjpy1G7HwGBFAoiOIJw9PsABHVVHhBc+G8UJyAAYwv1lJASaZZAiPFbzCN6Pq7zKPq+pUWdtuy7oo9qp2YCNe59xGwe0RmWco1CWaDAfeKUA95KfXmA6+qlWKOpwieUZlTW/0NNSqH9DoAcAfmosUuYx2d5wf+MpP4ZYYbqAdBpoP5x73ExrRFHXwuKpSa+Z0R0mo+aFqsygKRrj9SerYqrZu1V3CRuqRbougPdId0qxLlfR6Psgam9PBxhT+wd+71zcKmeg05bVBWQboBkIF7Zq8xWxdXJ2iuZfILTSuil/SxIqSxDu+bX+RHOYjIxwUZTQIgeKoOuQ2Ac993tbsTdjbi7EXc34u5G3N2IuxtxdyPubsTdjbi7EXc34u5G3N2IuxtxdyPubsTdjbi7EXc34o927dAGAACEgeB27D8SEoVBleRmqGg+ORqRRqQRaUQakUakEWlEGjG1rmlEGpFGpBFpRBqRRqQRaUQakUakEWlEGpFGpBFpRBqRRqQRaUQakUakEWlEGpFGpBFpRBqRRqQRaUQakUb86OhoRBqRRqQRk+qaRqQRaUQakUakEWlEGpFGpBFvGnFXiHMetSzUwqZz46p5AAA=";
|
|
|
|
|
List<string> Lines = new List<string>();
|
|
|
|
|
int LineIDX = 0;
|
|
|
|
|
|
|
|
|
|
public Pleg()
|
|
|
|
|
{
|
|
|
|
|
var gz = new System.IO.Compression.GZipStream(
|
|
|
|
|
new MemoryStream(Convert.FromBase64String(data), false),
|
|
|
|
|
System.IO.Compression.CompressionMode.Decompress);
|
|
|
|
|
var tr = new StreamReader(gz);
|
|
|
|
|
string line;
|
|
|
|
|
while ((line = tr.ReadLine()) != null)
|
|
|
|
|
Lines.Add(line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<SinMan> SinMen = new List<SinMan>();
|
|
|
|
|
|
|
|
|
|
int deadtime = 0;
|
|
|
|
|
|
|
|
|
|
void Off(int c, int n)
|
|
|
|
|
{
|
2013-12-18 20:37:32 +00:00
|
|
|
|
foreach (var s in SinMen)
|
2013-12-18 19:36:17 +00:00
|
|
|
|
{
|
2013-12-18 20:37:32 +00:00
|
|
|
|
if (s.c == c && s.n == n && !s.fading)
|
|
|
|
|
s.fading = true;
|
2013-12-18 19:36:17 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void On(int c, int n)
|
|
|
|
|
{
|
|
|
|
|
if (c == 9)
|
|
|
|
|
return;
|
|
|
|
|
var s = new SinMan(1500, n);
|
|
|
|
|
s.c = c;
|
|
|
|
|
s.n = n;
|
|
|
|
|
SinMen.Add(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
short Next()
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
2013-12-18 20:37:32 +00:00
|
|
|
|
for (int i = 0; i < SinMen.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
var s = SinMen[i];
|
|
|
|
|
if (s.Done)
|
|
|
|
|
{
|
|
|
|
|
SinMen.RemoveAt(i);
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret += s.Next();
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-18 19:36:17 +00:00
|
|
|
|
if (ret > 32767) ret = 32767;
|
|
|
|
|
if (ret < -32767) ret = -32767;
|
|
|
|
|
return (short)ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string FetchNext()
|
|
|
|
|
{
|
|
|
|
|
string ret = Lines[LineIDX];
|
|
|
|
|
LineIDX++;
|
|
|
|
|
if (LineIDX == Lines.Count)
|
|
|
|
|
LineIDX = 0;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Generate(short[] dest)
|
|
|
|
|
{
|
|
|
|
|
int idx = 0;
|
|
|
|
|
while (idx < dest.Length)
|
|
|
|
|
{
|
|
|
|
|
if (deadtime > 0)
|
|
|
|
|
{
|
|
|
|
|
short n = Next();
|
|
|
|
|
dest[idx++] = n;
|
|
|
|
|
dest[idx++] = n;
|
|
|
|
|
deadtime--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string[] s = FetchNext().Split(':');
|
|
|
|
|
char c = s[0][0];
|
|
|
|
|
if (c == 'A')
|
|
|
|
|
deadtime = int.Parse(s[1]) * 40;
|
|
|
|
|
else if (c == 'O')
|
|
|
|
|
On(int.Parse(s[2]), int.Parse(s[1]));
|
|
|
|
|
else if (c == 'F')
|
|
|
|
|
Off(int.Parse(s[2]), int.Parse(s[1]));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class SinMan
|
|
|
|
|
{
|
|
|
|
|
public int c;
|
|
|
|
|
public int n;
|
|
|
|
|
|
|
|
|
|
double theta;
|
|
|
|
|
double freq;
|
|
|
|
|
double amp;
|
|
|
|
|
|
2013-12-18 20:37:32 +00:00
|
|
|
|
public bool fading = false;
|
|
|
|
|
|
|
|
|
|
public bool Done { get { return amp < 2.0; } }
|
|
|
|
|
|
2013-12-18 19:36:17 +00:00
|
|
|
|
static double GetFreq(int note)
|
|
|
|
|
{
|
|
|
|
|
return Math.Pow(2.0, note / 12.0) * 13.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public short Next()
|
|
|
|
|
{
|
|
|
|
|
short result = (short)(Math.Sin(theta) * amp);
|
|
|
|
|
theta += freq * Math.PI / 22050.0;
|
|
|
|
|
if (theta >= Math.PI * 2.0)
|
|
|
|
|
theta -= Math.PI * 2.0;
|
2013-12-18 20:37:32 +00:00
|
|
|
|
if (fading)
|
|
|
|
|
amp *= 0.87;
|
2013-12-18 19:36:17 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public SinMan(int amp, int note)
|
|
|
|
|
{
|
|
|
|
|
this.amp = amp;
|
|
|
|
|
this.freq = GetFreq(note);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2013-12-18 20:37:32 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
2011-01-11 02:55:51 +00:00
|
|
|
|
}
|