Went through the sound generation with a fine-tooth comb and made it sound a lot better!

This commit is contained in:
Brendan Robert 2015-09-06 00:47:58 -05:00
parent 11b26305f8
commit 45f56756e4
4 changed files with 113 additions and 108 deletions

View File

@ -33,6 +33,7 @@ import jace.hardware.mockingboard.R6522;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -184,6 +185,7 @@ public class CardMockingboard extends Card implements Runnable {
} }
if (isRunning() && !pause) { if (isRunning() && !pause) {
// buildMixerTable();
timerSync.lock(); timerSync.lock();
try { try {
ticksSinceLastPlayback++; ticksSinceLastPlayback++;
@ -246,13 +248,13 @@ public class CardMockingboard extends Card implements Runnable {
out = out * 2.0 / 3.0 / numChips; out = out * 2.0 / 3.0 / numChips;
double delta = 1.15; double delta = 1.15;
for (int i = 15; i > 0; i--) { 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.188502227; /* = 10 ^ (1.5/20) = 1.5dB */
// out /= 1.15; /* = 10 ^ (3/20) = 3dB */ // out /= 1.15; /* = 10 ^ (3/20) = 3dB */
// delta += 0.0225;
delta += 0.0225; // out /= delta; // As per applewin's source, the levels don't scale as documented.
out /= delta; // As per applewin's source, the levels don't scale as documented.
} }
VolTable[0] = 0; VolTable[0] = 0;
} }
Thread playbackThread = null; Thread playbackThread = null;
@ -321,74 +323,75 @@ public class CardMockingboard extends Card implements Runnable {
ticksSinceLastPlayback = 0; ticksSinceLastPlayback = 0;
int zeroSamples = 0; int zeroSamples = 0;
setRun(true); setRun(true);
LockSupport.parkNanos(5000);
while (isRunning()) { while (isRunning()) {
while (!computer.isRunning()) { while (isRunning() && !computer.isRunning()) {
Thread.sleep(500); Thread.currentThread().yield();
} }
computer.getMotherboard().requestSpeed(this); if (isRunning()) {
playSound(leftBuffer, rightBuffer); computer.getMotherboard().requestSpeed(this);
int p = 0; playSound(leftBuffer, rightBuffer);
for (int idx = 0; idx < BUFFER_LENGTH; idx++) { int p = 0;
int sampleL = leftBuffer[idx]; for (int idx = 0; idx < BUFFER_LENGTH; idx++) {
int sampleR = rightBuffer[idx]; int sampleL = leftBuffer[idx];
// Convert left + right samples into buffer format int sampleR = rightBuffer[idx];
if (sampleL == 0 && sampleR == 0) { // Convert left + right samples into buffer format
zeroSamples++; if (sampleL == 0 && sampleR == 0) {
} else { 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; zeroSamples = 0;
} pause = true;
for (int shift = SoundMixer.BITS - 8, index = 0; shift >= 0; shift -= 8, index++) { computer.getMotherboard().cancelSpeedRequest(this);
buffer[p + index] = (byte) (sampleR >> shift); while (pause && isRunning()) {
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 {
try { try {
timerSync.unlock(); Thread.sleep(50);
timerSync.lock();
playbackFinished.signalAll();
} catch (InterruptedException ex) {
return;
} catch (IllegalMonitorStateException ex) { } 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 {
try { timerSync.lock();
timerSync.lock(); playbackFinished.signalAll();
playbackFinished.signalAll(); while (isRunning() && ticksSinceLastPlayback < ticksBeteenPlayback) {
while (isRunning() && ticksSinceLastPlayback < ticksBeteenPlayback) { cpuCountReached.await();
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) { } catch (LineUnavailableException ex) {
Logger.getLogger(CardMockingboard.class Logger.getLogger(CardMockingboard.class
.getName()).log(Level.SEVERE, null, ex); .getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(CardMockingboard.class.getName()).log(Level.SEVERE, null, ex);
} finally { } finally {
computer.getMotherboard().cancelSpeedRequest(this); computer.getMotherboard().cancelSpeedRequest(this);
System.out.println("Mockingboard playback stopped"); System.out.println("Mockingboard playback stopped");

View File

@ -32,6 +32,11 @@ public class EnvelopeGenerator extends TimedGenerator {
int direction; int direction;
int amplitude; int amplitude;
boolean start1high = false;
boolean start2high = false;
boolean oneShot = false;
boolean oddEven = false;
public EnvelopeGenerator(int _clock, int _sampleRate) { public EnvelopeGenerator(int _clock, int _sampleRate) {
super(_clock, _sampleRate); super(_clock, _sampleRate);
} }
@ -50,55 +55,54 @@ public class EnvelopeGenerator extends TimedGenerator {
} }
} }
int effectiveAmplitude = 0;
public void step() { public void step() {
int stateChanges = updateCounter(); int stateChanges = updateCounter();
int total = 0;
for (int i = 0; i < stateChanges; i++) { 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; 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) { public void setShape(int shape) {
oddEven = false;
counter = 0; counter = 0;
cont = (shape & 8) != 0; cont = (shape & 8) != 0;
attk = (shape & 4) != 0; attk = (shape & 4) != 0;
alt = (shape & 2) != 0; alt = (shape & 2) != 0;
hold = (shape & 1) != 0; hold = ((shape ^ 8) & 9) != 0;
if (attk) {
start1high = !attk;
start2high = cont && ! (attk ^ alt ^ hold);
setPhase(start1high);
}
public void setPhase(boolean isHigh) {
if (isHigh) {
amplitude = 15;
direction = -1;
} else {
amplitude = 0; amplitude = 0;
direction = 1; direction = 1;
} else { }
amplitude = 15; }
direction = -1;
} public int getEffectiveAmplitude() {
return effectiveAmplitude;
} }
public int getAmplitude() { public int getAmplitude() {

View File

@ -30,7 +30,7 @@ public class NoiseGenerator extends TimedGenerator {
} }
@Override @Override
public int stepsPerCycle() { public int stepsPerCycle() {
return 8; return 4;
} }
public void step() { public void step() {
int stateChanges = updateCounter(); int stateChanges = updateCounter();
@ -39,11 +39,14 @@ public class NoiseGenerator extends TimedGenerator {
} }
public static final int BIT17 = 0x010000; public static final int BIT17 = 0x010000;
public void updateRng() { public void updateRng() {
// noise = (noise >> 1) ^ ((noise & 1) ? 0x14000 : 0); rng = ((rng & 1) != 0 ? rng ^ 0x24000 : rng) >> 1;
int newBit17 = (rng & 0x04) > 0 == (rng & 0x01) > 0 ? BIT17 : 0; if ((rng & 1) == 1) {
rng = newBit17 + (rng >> 1); state = !state;
}
} }
boolean state = false;
public boolean isOn() { public boolean isOn() {
return ((rng & 1) == 1); return state;
} }
} }

View File

@ -58,16 +58,11 @@ public class SoundGenerator extends TimedGenerator {
public int step(NoiseGenerator noiseGen, EnvelopeGenerator envGen) { public int step(NoiseGenerator noiseGen, EnvelopeGenerator envGen) {
int stateChanges = updateCounter(); int stateChanges = updateCounter();
if (((stateChanges & 1) == 1)) inverted = !inverted; if (((stateChanges & 1) == 1)) inverted = !inverted;
if (amplitude == 0 && !useEnvGen) return 0; double amp = stateChanges == 0 ? 1 : 1.0 / Math.max(stateChanges-1, 1);
if (!active && !noiseActive) return 0; int vol = useEnvGen ? envGen.getEffectiveAmplitude() : amplitude;
boolean invert; boolean on = noiseActive && noiseGen.isOn() || (active && inverted);
int vol = useEnvGen ? envGen.getAmplitude() : amplitude; // return invert ? -CardMockingboard.VolTable[vol] : CardMockingboard.VolTable[vol];
if (active) { return on ? (int) (CardMockingboard.VolTable[vol] * amp) : 0;
invert = noiseActive && noiseGen.isOn() ? false : inverted;
} else {
invert = noiseActive && !noiseGen.isOn();
}
return invert ? -CardMockingboard.VolTable[vol] : CardMockingboard.VolTable[vol];
} }
@Override @Override