Skip to content

Commit

Permalink
Greatly reduce audio latency, remove popping sound when loading saves
Browse files Browse the repository at this point in the history
  • Loading branch information
garrettgu10 committed Aug 19, 2019
1 parent bdfb967 commit 43f7760
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 16 deletions.
9 changes: 7 additions & 2 deletions src/org/the429ers/gameboy/GameBoy.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.text.SimpleDateFormat;
import java.util.*;

import javax.sound.sampled.SourceDataLine;
import javax.swing.*;

class MainMenuBar extends MenuBar {
Expand Down Expand Up @@ -223,6 +224,8 @@ public class GameBoy extends JFrame{
InputStream loadFile = null;
LinkedList<ByteArrayOutputStream> autoSaves = new LinkedList<>();

SourceDataLine sourceDL;

private static GameBoy gb;

public static GameBoy getInstance() {
Expand Down Expand Up @@ -255,6 +258,7 @@ public void windowOpened(WindowEvent e) { }
};

public void switchRom(String newRom) {
this.sourceDL = mmu.soundChip.getSourceDL();
this.romFileName = newRom;
if(mmu != null) mmu.cleanUp();
mmu = new MMU(newRom);
Expand All @@ -271,11 +275,10 @@ public void switchRom(String newRom) {
ppu = new PPU(mmu, gbs);
}
mmu.setPPU(ppu);
mmu.soundChip.setSourceDL(this.sourceDL);
cable = new LinkCable(mmu, cpu.interruptHandler);
joypad = new Joypad(mmu, cpu.interruptHandler);
gbs.addKeyListener(joypad);


}

public GameBoy(String fileName) {
Expand Down Expand Up @@ -317,6 +320,7 @@ public GameBoy(String fileName) {
}

public void saveState() {
this.sourceDL = this.mmu.soundChip.getSourceDL();
try {
ObjectOutputStream saveState = new ObjectOutputStream(this.saveFile);
saveState.writeObject(mmu);
Expand Down Expand Up @@ -351,6 +355,7 @@ public void loadState() {
}
gbs.addKeyListener(this.mmu.getJoypad());
ppu.setGBS(gbs);
this.mmu.soundChip.setSourceDL(this.sourceDL);
}

public void tick() {
Expand Down
30 changes: 16 additions & 14 deletions src/org/the429ers/gameboy/SoundChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ class SoundChip implements Serializable {
public static final int WAVE = 2;
public static final int NOISE = 3;

private transient SourceDataLine sourceDL;
transient SourceDataLine sourceDL;

byte[] masterBuffer = new byte[2 * SAMPLES_PER_FRAME];
byte[] tempBuffer = new byte[SAMPLES_PER_FRAME];
byte[] masterBuffer = new byte[6 * SAMPLES_PER_FRAME];
byte[] tempBuffer = new byte[3 * SAMPLES_PER_FRAME];

long totalSamplesWritten = 0;

boolean[] leftEnabled = new boolean[4];
boolean[] rightEnabled = new boolean[4];
Expand All @@ -40,16 +42,13 @@ class SoundChip implements Serializable {
Arrays.fill(rightEnabled, true);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();

try {
sourceDL = AudioSystem.getSourceDataLine(AUDIO_FORMAT);
sourceDL.open(AUDIO_FORMAT);
sourceDL.start();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
public void setSourceDL(SourceDataLine sourceDL){
this.sourceDL = sourceDL;
this.totalSamplesWritten = sourceDL.getLongFramePosition();
}

public SourceDataLine getSourceDL(){
return this.sourceDL;
}

SoundChip() {
Expand Down Expand Up @@ -82,7 +81,10 @@ public void tick() {
e.printStackTrace();
}
}
int samplesToWrite = Math.min(sourceDL.available() / 3, SAMPLES_PER_FRAME);
long residualSamples = totalSamplesWritten - sourceDL.getLongFramePosition();
int samplesToWrite = Math.max(0, (int)(3 * SAMPLES_PER_FRAME - residualSamples)); //try to keep 3 frames buffered at all times
samplesToWrite = Math.min(sourceDL.available() / 2, Math.min(3 * SAMPLES_PER_FRAME, samplesToWrite)); //never want to block here
totalSamplesWritten += samplesToWrite;

Arrays.fill(masterBuffer, (byte) 0);

Expand Down

0 comments on commit 43f7760

Please sign in to comment.