AppleIIGo/Source/AppleSpeaker.java

241 lines
4.8 KiB
Java

/**
* AppleIIGo
* Speaker processing
* (C) 2006 by Marc S. Ressl(ressl@lonetree.com)
* Released under the GPL
*/
import javax.sound.sampled.*;
public class AppleSpeaker implements Runnable {
// Instances of other classes
private EmAppleII apple;
// Refresh
private int refreshRate;
private long refreshInterval;
// Sound stuff
private static final int SPEAKER_BITS = 16;
private static final int SPEAKER_SAMPLERATE = 44100;
private static final int SPEAKER_CHANNELS = 1;
private static final int SPEAKER_SAMPLESIZE = (SPEAKER_BITS * SPEAKER_CHANNELS / 8);
private static final boolean SPEAKER_SIGNED = true;
private static final boolean SPEAKER_BIGENDIAN = false;
private int clock, clockNextFlip, clockEnd;
private boolean isFlipsBufferEmpty = true;
private SourceDataLine line;
private int bufferSamples;
private int bufferSize;
private byte[] buffer;
private int speakerVolume;
private int speakerFlipsPointer;
private int speakerFlipState;
private int[] speakerFlipStateToVolume = new int[2];
private int speakerClocksPerSample;
// Thread stuff
private boolean isPaused = true;
private Thread thread;
private String threadError = null;
public AppleSpeaker(EmAppleII apple) {
this.apple = apple;
setVolume(4);
}
/**
* Set refresh rate
*
* @param value Speaker refresh rate in mHz
*/
private void setRefreshRate(int value) {
if (value <= 0.0f)
return;
this.refreshRate = value;
refreshInterval = (int) (1000.0 / value);
speakerClocksPerSample = (int) (apple.getCpuSpeed() * 1000.0f / SPEAKER_SAMPLERATE);
}
/**
* Get refresh rate
*/
private int getRefreshRate() {
return refreshRate;
}
/**
* Set speaker volume
*/
public void setVolume(int value) {
if ((value < 0) || (value > 7))
return;
speakerVolume = value;
int absVolume = 1 << (value + 8);
speakerFlipStateToVolume[0] = -absVolume;
speakerFlipStateToVolume[1] = absVolume;
}
/**
* Get speaker volume
*/
public int getVolume() {
return speakerVolume;
}
/**
* Set pause state
*/
public void setPaused(boolean value) {
if (isPaused == value)
return;
isPaused = value;
if (isPaused) {
try {
thread.join(1000);
} catch (InterruptedException e) {
}
if (line != null) {
line.stop();
line.close();
}
} else {
setRefreshRate(apple.getRefreshRate());
AudioFormat audioFormat = new AudioFormat(
SPEAKER_SAMPLERATE,
SPEAKER_BITS,
SPEAKER_CHANNELS,
SPEAKER_SIGNED,
SPEAKER_BIGENDIAN);
DataLine.Info info = new DataLine.Info(
DataLine.class,
audioFormat);
try {
line = (SourceDataLine) AudioSystem.getLine(info);
bufferSize = line.getBufferSize();
bufferSamples = bufferSize / SPEAKER_SAMPLESIZE;
buffer = new byte[bufferSize];
line.open(audioFormat);
line.start();
} catch (LineUnavailableException e) {
}
thread = new Thread(this);
thread.start();
}
}
/**
* Speaker refresh thread
*/
public void run() {
try {
while (!isPaused) {
long refreshStart = System.currentTimeMillis();
long refreshDelay;
refreshSpeaker();
refreshDelay = System.currentTimeMillis() - refreshStart;
if (refreshDelay < refreshInterval)
Thread.sleep(refreshInterval - refreshDelay);
}
} catch (InterruptedException e) {
};
}
/**
* Speaker refresh
*/
private void refreshSpeaker() {
clockEnd = apple.clock;
int bytes;
if (line == null)
return;
while ((bytes = fillBuffer()) > 0) {
line.write(buffer, 0, bytes);
}
}
/**
* Fill buffer
*/
private int fillBuffer() {
int value = speakerFlipStateToVolume[speakerFlipState];
int clockEndSample = clockEnd - speakerClocksPerSample;
int bufferPointer = 0;
initNextFlip();
while (bufferPointer < bufferSize) {
if (clockEndSample == clock)
break;
if (((clockEndSample - clock) & 0x7fffffff) > 0x3fffffff)
break;
// Find all flips on current sample
while (((clockNextFlip - clock) & 0x7fffffff) < speakerClocksPerSample) {
getNextFlip();
speakerFlipState = (speakerFlipState ^ 1);
value = speakerFlipStateToVolume[speakerFlipState];
}
// Write sample
buffer[bufferPointer] = (byte) (value & 0xff);
buffer[bufferPointer + 1] = (byte) (value >> 8);
bufferPointer += SPEAKER_SAMPLESIZE;
clock += speakerClocksPerSample;
}
return bufferPointer;
}
/**
* Reset next flip
*/
private void initNextFlip() {
if (isFlipsBufferEmpty) {
isFlipsBufferEmpty = false;
getNextFlip();
}
}
/**
* Gets next flip
*/
private void getNextFlip() {
if (speakerFlipsPointer == apple.speakerFlipsPointer) {
clockNextFlip = clock + 0x3fffffff;
isFlipsBufferEmpty = true;
} else {
clockNextFlip = apple.speakerFlips[speakerFlipsPointer];
speakerFlipsPointer = (speakerFlipsPointer + 1) & apple.SPEAKER_FLIPS_MASK;
}
}
}