diff --git a/D/Conversion.cs b/D/Conversion.cs
index 0019d5d..ef464b7 100644
--- a/D/Conversion.cs
+++ b/D/Conversion.cs
@@ -45,5 +45,10 @@ public static class Conversion
/// Conversion from microseconds to nanoseconds
///
public static readonly ulong UsecToNsec = 1000;
+
+ ///
+ /// Conversion from microseconds to seconds
+ ///
+ public static readonly double UsecToSec = 0.000001;
}
}
diff --git a/D/Darkstar.csproj b/D/Darkstar.csproj
index 4289c25..1862fab 100644
--- a/D/Darkstar.csproj
+++ b/D/Darkstar.csproj
@@ -122,7 +122,7 @@
-
+
True
True
diff --git a/D/IOP/Beeper.cs b/D/IOP/Beeper.cs
new file mode 100644
index 0000000..e47a053
--- /dev/null
+++ b/D/IOP/Beeper.cs
@@ -0,0 +1,109 @@
+using D.Logging;
+using SDL2;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace D.IOP
+{
+ ///
+ /// Implements the tone generator used to generate simple beeps.
+ /// This is driven by an i8253 programmable interval timer.
+ ///
+ public class Beeper
+ {
+ public Beeper()
+ {
+ Reset();
+ }
+
+ public void Reset()
+ {
+ _lsb = 0;
+ _loadLSB = true;
+ _frequency = 0.0;
+ _enabled = false;
+ _sampleOn = false;
+ _periodInSamples = 0;
+
+ _sampleBuffer = new byte[0x10000];
+ }
+
+ public void LoadPeriod(byte value)
+ {
+ if (_loadLSB)
+ {
+ _lsb = value;
+ }
+ else
+ {
+ // "The period (in usec*1.8432) will be in the range ~29..65535."
+ double period = ((value << 8) | _lsb) / 1.8432;
+
+ // The above is in usec; convert to seconds:
+ period = period * Conversion.UsecToSec;
+
+ // Invert to get the frequency in Hz:
+ _frequency = 1.0 / period;
+
+ if (Log.Enabled) Log.Write(LogComponent.Beeper, "Tone frequency set to {0}", _frequency);
+
+ // And find out the length in samples at 44.1Khz.
+ _periodInSamples = 44100.0 / _frequency;
+ }
+
+ _loadLSB = !_loadLSB;
+ }
+
+ public void EnableTone()
+ {
+ if (Log.Enabled) Log.Write(LogComponent.Beeper, "Tone enabled.", _frequency);
+ _enabled = true;
+ }
+
+ public void DisableTone()
+ {
+ if (Log.Enabled && _enabled) Log.Write(LogComponent.Beeper, "Tone disabled.", _frequency);
+ _enabled = false;
+ }
+
+ public void AudioCallback(IntPtr userData, IntPtr stream, int length)
+ {
+ if (_enabled)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ _position++;
+
+ if (_position > _periodInSamples)
+ {
+ _position -= _periodInSamples;
+ _sampleOn = !_sampleOn;
+ }
+
+ _sampleBuffer[i] = (byte)(_enabled ? (_sampleOn ? 0x3f : 0x00) : 0x00);
+ }
+ }
+ else
+ {
+ Array.Clear(_sampleBuffer, 0, length);
+ }
+
+ Marshal.Copy(_sampleBuffer, 0, stream, length);
+ }
+
+ private byte[] _sampleBuffer;
+
+ private bool _loadLSB;
+ private byte _lsb;
+
+ private double _frequency;
+ private bool _enabled;
+ private double _position;
+ private double _periodInSamples;
+ private bool _sampleOn;
+ }
+}
diff --git a/D/IOP/IOProcessor.cs b/D/IOP/IOProcessor.cs
index 64314ff..adb9cad 100644
--- a/D/IOP/IOProcessor.cs
+++ b/D/IOP/IOProcessor.cs
@@ -54,7 +54,7 @@ public IOProcessor(DSystem system)
_floppyController = new FloppyController(_floppyDrive, _system);
_dma = new DMAController(this);
_tty = new Printer();
- _tone = new Tone();
+ _beeper = new Beeper();
//
// Register DMA devices with controller
@@ -143,9 +143,9 @@ public Printer Printer
get { return _tty; }
}
- public Tone Tone
+ public Beeper Beeper
{
- get { return _tone; }
+ get { return _beeper; }
}
private i8085 _cpu;
@@ -161,7 +161,7 @@ public Tone Tone
private Keyboard _keyboard;
private Mouse _mouse;
private Printer _tty;
- private Tone _tone;
+ private Beeper _beeper;
private DSystem _system;
//
diff --git a/D/IOP/MiscIO.cs b/D/IOP/MiscIO.cs
index dd6c275..a5937b0 100644
--- a/D/IOP/MiscIO.cs
+++ b/D/IOP/MiscIO.cs
@@ -120,7 +120,7 @@ public void WritePort(int port, byte value)
// i8253 Timer channel #1 - used to set the Keyboard bell (tone) frequency.
// This is a 16-bit value loaded one byte at a time, LSB first.
// Send the word off to the tone generator.
- _iop.Tone.LoadInterval(value);
+ _iop.Beeper.LoadPeriod(value);
break;
case 0x8f:
@@ -180,11 +180,11 @@ public void WritePort(int port, byte value)
if ((value & 0x20) != 0)
{
- _iop.Tone.EnableTone();
+ _iop.Beeper.EnableTone();
}
else
{
- _iop.Tone.DisableTone();
+ _iop.Beeper.DisableTone();
}
if ((value & 0x10) != 0)
diff --git a/D/IOP/Tone.cs b/D/IOP/Tone.cs
deleted file mode 100644
index 4e1fdd0..0000000
--- a/D/IOP/Tone.cs
+++ /dev/null
@@ -1,91 +0,0 @@
-using D.Logging;
-using SDL2;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace D.IOP
-{
- ///
- /// Implements the tone generator used to generate simple beeps.
- /// This is driven by an i8253 programmable interval timer.
- ///
- public class Tone
- {
- public Tone()
- {
- Reset();
- }
-
- public void Reset()
- {
- _lsb = 0;
- _loadLSB = true;
- _frequency = 0.0;
- _enabled = false;
- _sampleOn = false;
- _periodInSamples = 0;
- }
-
- public void LoadInterval(byte value)
- {
- if (_loadLSB)
- {
- _lsb = value;
- }
- else
- {
- //;Frequency constant (1843.2/f, f in kHz)
- _frequency = ((value << 8) | value) / 1.8432;
- if (Log.Enabled) Log.Write(LogComponent.Tone, "Tone frequency set to {0}", _frequency);
- _periodInSamples = (44100.0 / _frequency) / 2;
- }
-
- _loadLSB = false;
- }
-
- public void EnableTone()
- {
- if (Log.Enabled) Log.Write(LogComponent.Tone, "Tone enabled.", _frequency);
- _enabled = true;
- }
-
- public void DisableTone()
- {
- // if (Log.Enabled) Log.Write(LogComponent.Tone, "Tone disabled.", _frequency);
- _enabled = false;
- }
-
- public void AudioCallback(IntPtr userData, IntPtr stream, int length)
- {
- byte[] samples = new byte[length];
-
- for (int i = 0; i < length; i++)
- {
- _position++;
-
- if (_position > _periodInSamples)
- {
- _position -= _periodInSamples;
- _sampleOn = !_sampleOn;
- }
-
- samples[i] = (byte)(_enabled ? (_sampleOn ? 0xff : 0x00) : 0x00);
- }
-
- // Marshal.Copy(samples, 0, stream, length);
- }
-
- private bool _loadLSB;
- private byte _lsb;
-
- private double _frequency;
- private bool _enabled;
- private double _position;
- private double _periodInSamples;
- private bool _sampleOn;
- }
-}
diff --git a/D/Logging/Log.cs b/D/Logging/Log.cs
index fc85083..bbde329 100644
--- a/D/Logging/Log.cs
+++ b/D/Logging/Log.cs
@@ -82,7 +82,7 @@ public enum LogComponent
EthernetPacket = 0x10000000,
// Keyboard tone
- Tone = 0x20000000,
+ Beeper = 0x20000000,
// Configuration
Configuration = 0x40000000,
@@ -111,9 +111,9 @@ public static class Log
{
static Log()
{
- Enabled = true;
- _components = LogComponent.Tone | LogComponent.IOPPrinter;
- _type = LogType.All;
+ Enabled = false;
+ _components = LogComponent.None;
+ _type = LogType.None;
_logIndex = 0;
}
diff --git a/D/UI/DWindow-IO.cs b/D/UI/DWindow-IO.cs
index 09fdffe..9a5a875 100644
--- a/D/UI/DWindow-IO.cs
+++ b/D/UI/DWindow-IO.cs
@@ -974,6 +974,8 @@ private void InitializeSDL()
{
int retVal;
+ SDL.SDL_SetHint("SDL_WINDOWS_DISABLE_THREAD_NAMING", "1");
+
// Get SDL humming
if ((retVal = SDL.SDL_Init(SDL.SDL_INIT_EVERYTHING)) < 0)
{
@@ -1030,25 +1032,28 @@ private void InitializeSDL()
_renderEvent = new SDL.SDL_Event();
_renderEvent.type = (SDL.SDL_EventType)_renderEventType;
-
-
+ //
+ // Initialize SDL Audio:
+ // I'd prefer to do this in the Beeper class but there's some undocumented
+ // dependency on having an active SDL window, so we do it here to keep
+ // things simple.
+ //
SDL.SDL_AudioSpec desired = new SDL.SDL_AudioSpec();
SDL.SDL_AudioSpec obtained = new SDL.SDL_AudioSpec();
+
+ _audioCallback = _system.IOP.Beeper.AudioCallback;
desired.freq = 44100;
desired.format = SDL.AUDIO_U8;
desired.channels = 1;
- desired.callback = _system.IOP.Tone.AudioCallback;
- desired.samples = 1;
+ desired.callback = _audioCallback;
+ desired.samples = 1024;
uint deviceId = SDL.SDL_OpenAudioDevice(null, 0, ref desired, out obtained, 0);
-
SDL.SDL_PauseAudioDevice(deviceId, 0);
- if (Log.Enabled) Log.Write(LogComponent.Tone, "SDL Audio initialized, device id {0}", deviceId);
-
-
+ if (Log.Enabled) Log.Write(LogComponent.Beeper, "SDL Audio initialized, device id {0}", deviceId);
}
private void CreateDisplayTexture(bool filter)
@@ -1206,5 +1211,13 @@ private void ToggleFullScreen(bool fullScreen)
// Rendering textures
private IntPtr _displayTexture = IntPtr.Zero;
private ReaderWriterLockSlim _textureLock;
+
+ //
+ // Local reference for the SDL Audio callback:
+ // SDL-CS doesn't hold this reference which causes
+ // problems when the GC runs. We keep this reference for as long
+ // as the Display Window is active.
+ //
+ private SDL.SDL_AudioCallback _audioCallback;
}
}