diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java index 33e65156..799c0460 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/Speaker.java @@ -52,6 +52,9 @@ public class Speaker extends Device { @ConfigurableField(category = "sound", name = "1mhz timing", description = "Force speaker output to 1mhz?") public static boolean force1mhz = true; + @ConfigurableField(category = "sound", name = "Show sound", description = "Use black color value to show sound output") + public static boolean showSound = false; + @InvokableAction(category = "sound", name = "Record sound", description="Toggles recording (saving) sound output to a file", defaultKeyMapping = "ctrl+shift+w") public static void toggleFileOutput() { if (fileOutputActive) { @@ -95,20 +98,23 @@ public class Speaker extends Device { */ @ConfigurableField(name = "Speaker Volume", shortName = "vol", description = "Should be under 1400") public static int VOLUME = 400; - /** - * Number of idle cycles until speaker playback is deactivated - */ - @ConfigurableField(name = "Idle cycles before sleep", shortName = "idle") - public static int MAX_IDLE_CYCLES = 2000000; + private int currentVolume = 0; + private int fadeOffAmount = 1; /** * Manifestation of the apple speaker softswitch */ private boolean speakerBit = false; - private double TICKS_PER_SAMPLE = ((double) TimedDevice.NTSC_1MHZ) / SoundMixer.RATE; - private double TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE); + private static double TICKS_PER_SAMPLE = ((double) TimedDevice.NTSC_1MHZ) / SoundMixer.RATE; private RAMListener listener = null; private SoundBuffer buffer = null; + /** + * Number of idle cycles until speaker playback is deactivated + */ + @ConfigurableField(name = "Idle cycles before sleep", shortName = "idle") + // public static int MAX_IDLE_CYCLES = (int) (SoundMixer.BUFFER_SIZE * TICKS_PER_SAMPLE * 2); + public static int MAX_IDLE_CYCLES = (int) TimedDevice.NTSC_1MHZ / 4; + /** * Suspend playback of sound * @@ -118,7 +124,6 @@ public class Speaker extends Device { public boolean suspend() { boolean result = super.suspend(); speakerBit = false; - Emulator.withComputer(c->c.getMotherboard().cancelSpeedRequest(this)); if (buffer != null) { try { buffer.shutdown(); @@ -128,6 +133,7 @@ public class Speaker extends Device { buffer = null; } } + Emulator.withComputer(c->c.getMotherboard().cancelSpeedRequest(this)); return result; } @@ -164,7 +170,6 @@ public class Speaker extends Device { } else { TICKS_PER_SAMPLE = Emulator.withComputer(c-> ((double) c.getMotherboard().getSpeedInHz()) / SoundMixer.RATE, 0.0); } - TICKS_PER_SAMPLE_FLOOR = Math.floor(TICKS_PER_SAMPLE); super.resume(); } @@ -172,9 +177,9 @@ public class Speaker extends Device { * Reset idle counter whenever sound playback occurs */ public void resetIdle() { + currentVolume = VOLUME; idleCycles = 0; if (!isRunning()) { - speakerBit = false; resume(); } } @@ -186,21 +191,32 @@ public class Speaker extends Device { */ @Override public void tick() { - if (idleCycles++ >= MAX_IDLE_CYCLES) { - suspend(); - } if (speakerBit) { level++; + if (showSound) { + VideoNTSC.CHANGE_BLACK_COLOR(40, 20, 20); + } + } else if (showSound) { + VideoNTSC.CHANGE_BLACK_COLOR(20,20,40); + } + if (idleCycles++ >= MAX_IDLE_CYCLES && (currentVolume <= 0 || !speakerBit)) { + suspend(); + if (showSound) { + VideoNTSC.CHANGE_BLACK_COLOR(0,0,0); + } } counter += 1.0d; if (counter >= TICKS_PER_SAMPLE) { - playSample(level * VOLUME); - Emulator.withComputer(c->c.getMotherboard().requestSpeed(this)); + if (idleCycles >= MAX_IDLE_CYCLES) { + currentVolume -= fadeOffAmount; + } + playSample(level * currentVolume); + // Emulator.withComputer(c->c.getMotherboard().requestSpeed(this)); // Set level back to 0 level = 0; // Set counter to 0 - counter -= TICKS_PER_SAMPLE_FLOOR; + counter -= TICKS_PER_SAMPLE; } } diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java index e429cd48..779a14db 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoDHGR.java @@ -640,7 +640,7 @@ public class VideoDHGR extends Video { Logger.getLogger(getClass().getName()).warning("Went out of bounds in video display"); } } - static final Color BLACK = Color.BLACK; + static Color BLACK = Color.BLACK; static Color WHITE = Color.WHITE; static final int[][] XY_OFFSET; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java index acbf66f7..216a692a 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/apple2e/VideoNTSC.java @@ -27,6 +27,7 @@ import jace.config.InvokableAction; import jace.core.Computer; import jace.core.RAMEvent; import jace.core.RAMListener; +import jace.core.Video; import javafx.scene.image.PixelWriter; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; @@ -331,6 +332,19 @@ public class VideoNTSC extends VideoDHGR { {1.0, 0.0, 0.0}, //1111 f }; + public static void CHANGE_BLACK_COLOR(int r, int g, int b) { + Emulator.withVideo(v->{ + VideoNTSC vntsc = (VideoNTSC) v; + BLACK = Color.rgb(r, g, b); + int c = colorToInt(BLACK); + for (int i1 = 0; i1 < 4; i1++) { + vntsc.SOLID_PALETTE[i1][0] = c; + vntsc.TEXT_PALETTE[i1][0] = c; + } + }); + Video.forceRefresh(); + } + private void initDivideTables() { for (int i = 0; i < 560; i++) { divBy28[i] = i / 28; @@ -361,10 +375,18 @@ public class VideoNTSC extends VideoDHGR { } static public int yiqToRgb(double y, double i, double q) { + return colorToInt(yiqToRgbColor(y, i, q)); + } + + static public Color yiqToRgbColor(double y, double i, double q) { int r = (int) (normalize((y + 0.956 * i + 0.621 * q), 0, 1) * 255); int g = (int) (normalize((y - 0.272 * i - 0.647 * q), 0, 1) * 255); int b = (int) (normalize((y - 1.105 * i + 1.702 * q), 0, 1) * 255); - return (255 << 24) | (r << 16) | (g << 8) | b; + return Color.rgb(r, g, b); + } + + static public int colorToInt(Color c) { + return (int) (255 << 24) | (int) (c.getRed() * 255) << 16 | (int) (c.getGreen() * 255) << 8 | (int) (c.getBlue() * 255); } public static double normalize(double x, double minX, double maxX) { diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java b/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java index b9d88389..310a039b 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/TimedDevice.java @@ -188,6 +188,8 @@ public abstract class TimedDevice extends Device { return null; } cycleTimer = 0; + long retVal = nextSync; + nextSync = Math.max(nextSync, System.nanoTime()) + nanosPerInterval; if (isMaxSpeed() || useParentTiming()) { if (tempSpeedDuration > 0) { tempSpeedDuration -= cyclesPerInterval; @@ -195,11 +197,8 @@ public abstract class TimedDevice extends Device { disableTempMaxSpeed(); } } - nextSync += nanosPerInterval; return null; } - long retVal = nextSync; - nextSync = Math.min(nextSync + nanosPerInterval, System.nanoTime() + nanosPerInterval * 2); // Avoid drift (but not too much! return retVal; } } \ No newline at end of file diff --git a/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java b/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java index 58afb7f2..b7cc088d 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/core/Video.java @@ -274,7 +274,7 @@ public abstract class Video extends TimedDevice { Emulator.withVideo(v->v._forceRefresh()); } - private void _forceRefresh() { + protected void _forceRefresh() { lineDirty = true; screenDirty = true; forceRedrawRowCount = APPLE_SCREEN_LINES + 1; diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java index 5a744952..5945c19f 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/CardAppleMouse.java @@ -426,7 +426,7 @@ public class CardAppleMouse extends Card { * Described in Apple Mouse technical note #7 * Cn1A: Read mouse clamping values * Register number is stored in $478 and ranges from x47 to x4e - * Return value should be stored in $5782 + * Return value should be stored in $578 * Values should be returned in this order: * MinXH, MinYH, MinXL, MinYL, MaxXH, MaxYH, MaxXL, MaxYL */ diff --git a/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java b/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java index a1cca19c..285ae55e 100644 --- a/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java +++ b/Platform/Apple/tools/jace/src/main/java/jace/hardware/ZipWarpAccelerator.java @@ -24,6 +24,7 @@ import jace.core.RAMListener; /** * Implements a basic hardware accelerator that is able to adjust the speed of the emulator */ +// TODO: Support the registers used here: https://github.com/a2-4am/4cade/blob/main/src/hw.accel.a#L238 public class ZipWarpAccelerator extends Device { @ConfigurableField(category = "debug", name = "Debug messages") public boolean debugMessagesEnabled = false;