From 45f56756e43f783a703258adcf89d9176a9fcf71 Mon Sep 17 00:00:00 2001 From: Brendan Robert Date: Sun, 6 Sep 2015 00:47:58 -0500 Subject: [PATCH] Went through the sound generation with a fine-tooth comb and made it sound a lot better! --- .../java/jace/hardware/CardMockingboard.java | 119 +++++++++--------- .../mockingboard/EnvelopeGenerator.java | 74 +++++------ .../hardware/mockingboard/NoiseGenerator.java | 13 +- .../hardware/mockingboard/SoundGenerator.java | 15 +-- 4 files changed, 113 insertions(+), 108 deletions(-) diff --git a/src/main/java/jace/hardware/CardMockingboard.java b/src/main/java/jace/hardware/CardMockingboard.java index b12e2b1..79bdb98 100644 --- a/src/main/java/jace/hardware/CardMockingboard.java +++ b/src/main/java/jace/hardware/CardMockingboard.java @@ -33,6 +33,7 @@ import jace.hardware.mockingboard.R6522; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; @@ -184,6 +185,7 @@ public class CardMockingboard extends Card implements Runnable { } if (isRunning() && !pause) { +// buildMixerTable(); timerSync.lock(); try { ticksSinceLastPlayback++; @@ -246,13 +248,13 @@ public class CardMockingboard extends Card implements Runnable { out = out * 2.0 / 3.0 / numChips; double delta = 1.15; for (int i = 15; i > 0; i--) { - VolTable[i] = (int) Math.round(out); /* round to nearest */ // [TC: unsigned int cast] + VolTable[i] = (int) (out / Math.pow(Math.sqrt(2),(15-i))); // out /= 1.188502227; /* = 10 ^ (1.5/20) = 1.5dB */ // out /= 1.15; /* = 10 ^ (3/20) = 3dB */ - - delta += 0.0225; - out /= delta; // As per applewin's source, the levels don't scale as documented. +// delta += 0.0225; +// out /= delta; // As per applewin's source, the levels don't scale as documented. } + VolTable[0] = 0; } Thread playbackThread = null; @@ -321,74 +323,75 @@ public class CardMockingboard extends Card implements Runnable { ticksSinceLastPlayback = 0; int zeroSamples = 0; setRun(true); + LockSupport.parkNanos(5000); while (isRunning()) { - while (!computer.isRunning()) { - Thread.sleep(500); + while (isRunning() && !computer.isRunning()) { + Thread.currentThread().yield(); } - computer.getMotherboard().requestSpeed(this); - playSound(leftBuffer, rightBuffer); - int p = 0; - for (int idx = 0; idx < BUFFER_LENGTH; idx++) { - int sampleL = leftBuffer[idx]; - int sampleR = rightBuffer[idx]; - // Convert left + right samples into buffer format - if (sampleL == 0 && sampleR == 0) { - zeroSamples++; - } else { + if (isRunning()) { + computer.getMotherboard().requestSpeed(this); + playSound(leftBuffer, rightBuffer); + int p = 0; + for (int idx = 0; idx < BUFFER_LENGTH; idx++) { + int sampleL = leftBuffer[idx]; + int sampleR = rightBuffer[idx]; + // Convert left + right samples into buffer format + if (sampleL == 0 && sampleR == 0) { + zeroSamples++; + } else { + zeroSamples = 0; + } + for (int shift = SoundMixer.BITS - 8, index = 0; shift >= 0; shift -= 8, index++) { + buffer[p + index] = (byte) (sampleR >> shift); + buffer[p + index + bytesPerSample] = (byte) (sampleL >> shift); + } + p += frameSize; + } + try { + timerSync.lock(); + ticksSinceLastPlayback -= ticksBeteenPlayback; + } finally { + timerSync.unlock(); + } + out.write(buffer, 0, buffer.length); + if (zeroSamples >= MAX_IDLE_SAMPLES) { zeroSamples = 0; - } - for (int shift = SoundMixer.BITS - 8, index = 0; shift >= 0; shift -= 8, index++) { - buffer[p + index] = (byte) (sampleR >> shift); - buffer[p + index + bytesPerSample] = (byte) (sampleL >> shift); - } - p += frameSize; - } - try { - timerSync.lock(); - ticksSinceLastPlayback -= ticksBeteenPlayback; - } finally { - timerSync.unlock(); - } - out.write(buffer, 0, buffer.length); - if (zeroSamples >= MAX_IDLE_SAMPLES) { - zeroSamples = 0; - pause = true; - computer.getMotherboard().cancelSpeedRequest(this); - while (pause && isRunning()) { - try { - Thread.sleep(50); - timerSync.lock(); - playbackFinished.signalAll(); - } catch (InterruptedException ex) { - return; - } catch (IllegalMonitorStateException ex) { - // Do nothing - } finally { + pause = true; + computer.getMotherboard().cancelSpeedRequest(this); + while (pause && isRunning()) { try { - timerSync.unlock(); + Thread.sleep(50); + timerSync.lock(); + playbackFinished.signalAll(); + } catch (InterruptedException ex) { + return; } catch (IllegalMonitorStateException ex) { - // Do nothing -- this is probably caused by a suspension event + // Do nothing + } finally { + try { + timerSync.unlock(); + } catch (IllegalMonitorStateException ex) { + // Do nothing -- this is probably caused by a suspension event + } } } } - } - try { - timerSync.lock(); - playbackFinished.signalAll(); - while (isRunning() && ticksSinceLastPlayback < ticksBeteenPlayback) { - cpuCountReached.await(); + try { + timerSync.lock(); + playbackFinished.signalAll(); + while (isRunning() && ticksSinceLastPlayback < ticksBeteenPlayback) { + cpuCountReached.await(); + } + } catch (InterruptedException ex) { + // Do nothing, probably killing playback thread on purpose + } finally { + timerSync.unlock(); } - } catch (InterruptedException ex) { - // Do nothing, probably killing playback thread on purpose - } finally { - timerSync.unlock(); } } } catch (LineUnavailableException ex) { Logger.getLogger(CardMockingboard.class .getName()).log(Level.SEVERE, null, ex); - } catch (InterruptedException ex) { - Logger.getLogger(CardMockingboard.class.getName()).log(Level.SEVERE, null, ex); } finally { computer.getMotherboard().cancelSpeedRequest(this); System.out.println("Mockingboard playback stopped"); diff --git a/src/main/java/jace/hardware/mockingboard/EnvelopeGenerator.java b/src/main/java/jace/hardware/mockingboard/EnvelopeGenerator.java index 10783ea..44e9d00 100644 --- a/src/main/java/jace/hardware/mockingboard/EnvelopeGenerator.java +++ b/src/main/java/jace/hardware/mockingboard/EnvelopeGenerator.java @@ -32,6 +32,11 @@ public class EnvelopeGenerator extends TimedGenerator { int direction; int amplitude; + boolean start1high = false; + boolean start2high = false; + boolean oneShot = false; + boolean oddEven = false; + public EnvelopeGenerator(int _clock, int _sampleRate) { super(_clock, _sampleRate); } @@ -50,55 +55,54 @@ public class EnvelopeGenerator extends TimedGenerator { } } + int effectiveAmplitude = 0; public void step() { int stateChanges = updateCounter(); + int total = 0; for (int i = 0; i < stateChanges; i++) { - if (amplitude == 0 && direction == -1) { - if (!cont) { - direction = 0; - } else if (hold) { - direction = 0; - if (alt) { - amplitude = 15; - } - } else if (alt) { - direction = 1; - } else { - amplitude = 15; - } - } - if (amplitude == 15 && direction == 1) { - if (!cont) { - direction = 0; - amplitude = 0; - } else if (hold) { - direction = 0; - if (alt) { - amplitude = 0; - } - } else if (alt) { - direction = -1; - } else { - amplitude = 0; - } - } amplitude += direction; + if (amplitude > 15 || amplitude < 0) { + setPhase(oddEven ? start1high : start2high); + oddEven = !oddEven; + if (hold) { + direction = 0; + } + } + total += amplitude; + } + if (stateChanges == 0) { + effectiveAmplitude = amplitude; + } else { + effectiveAmplitude = Math.min(15, total / stateChanges); } } public void setShape(int shape) { + oddEven = false; counter = 0; cont = (shape & 8) != 0; attk = (shape & 4) != 0; alt = (shape & 2) != 0; - hold = (shape & 1) != 0; - if (attk) { + hold = ((shape ^ 8) & 9) != 0; + + start1high = !attk; + start2high = cont && ! (attk ^ alt ^ hold); + + setPhase(start1high); + } + + public void setPhase(boolean isHigh) { + if (isHigh) { + amplitude = 15; + direction = -1; + } else { amplitude = 0; direction = 1; - } else { - amplitude = 15; - direction = -1; - } + } + } + + public int getEffectiveAmplitude() { + return effectiveAmplitude; } public int getAmplitude() { diff --git a/src/main/java/jace/hardware/mockingboard/NoiseGenerator.java b/src/main/java/jace/hardware/mockingboard/NoiseGenerator.java index c6e5767..b0e9251 100644 --- a/src/main/java/jace/hardware/mockingboard/NoiseGenerator.java +++ b/src/main/java/jace/hardware/mockingboard/NoiseGenerator.java @@ -30,7 +30,7 @@ public class NoiseGenerator extends TimedGenerator { } @Override public int stepsPerCycle() { - return 8; + return 4; } public void step() { int stateChanges = updateCounter(); @@ -39,11 +39,14 @@ public class NoiseGenerator extends TimedGenerator { } public static final int BIT17 = 0x010000; public void updateRng() { - // noise = (noise >> 1) ^ ((noise & 1) ? 0x14000 : 0); - int newBit17 = (rng & 0x04) > 0 == (rng & 0x01) > 0 ? BIT17 : 0; - rng = newBit17 + (rng >> 1); + rng = ((rng & 1) != 0 ? rng ^ 0x24000 : rng) >> 1; + if ((rng & 1) == 1) { + state = !state; + } } + + boolean state = false; public boolean isOn() { - return ((rng & 1) == 1); + return state; } } \ No newline at end of file diff --git a/src/main/java/jace/hardware/mockingboard/SoundGenerator.java b/src/main/java/jace/hardware/mockingboard/SoundGenerator.java index f1010f2..24d06b4 100644 --- a/src/main/java/jace/hardware/mockingboard/SoundGenerator.java +++ b/src/main/java/jace/hardware/mockingboard/SoundGenerator.java @@ -58,16 +58,11 @@ public class SoundGenerator extends TimedGenerator { public int step(NoiseGenerator noiseGen, EnvelopeGenerator envGen) { int stateChanges = updateCounter(); if (((stateChanges & 1) == 1)) inverted = !inverted; - if (amplitude == 0 && !useEnvGen) return 0; - if (!active && !noiseActive) return 0; - boolean invert; - int vol = useEnvGen ? envGen.getAmplitude() : amplitude; - if (active) { - invert = noiseActive && noiseGen.isOn() ? false : inverted; - } else { - invert = noiseActive && !noiseGen.isOn(); - } - return invert ? -CardMockingboard.VolTable[vol] : CardMockingboard.VolTable[vol]; + double amp = stateChanges == 0 ? 1 : 1.0 / Math.max(stateChanges-1, 1); + int vol = useEnvGen ? envGen.getEffectiveAmplitude() : amplitude; + boolean on = noiseActive && noiseGen.isOn() || (active && inverted); +// return invert ? -CardMockingboard.VolTable[vol] : CardMockingboard.VolTable[vol]; + return on ? (int) (CardMockingboard.VolTable[vol] * amp) : 0; } @Override