This battleship game is the first multi-threaded application I've tried to write and it worked perfectly up until I added the multi-threading,which is only used for sound effects. It is all in one single class, the AudioManager.
I'm pretty sure I just lack experience and or/understanding regarding concurrency, even though I've read the java tutorials etc. I think I just need a little help to get it to click.
Anyway the game runs fine until enough sounds have been played that it runs out of memory and gives me this error:
Exception in thread "AWT-EventQueue-0" java.lang.OutOfMemoryError: unable to create new native thread
I was creating a new thread for each sound effect to play on because I didn't want the gui to wait for the sound to finish, and because sounds are often played very close to each other and I didn't want them conflicting on the same thread if they overlapped. The problem, I think, is that I'm not sure how to close each thread after the sound is played without stalling the main thread.
Here is the class with all the sound code. The sounds are played using the setSound() method, which sets the sound to be played and then starts a new thread with the SoundPlayer inner class for the Runnable. Thanks in advance:
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
public class AudioManager {
private static Thread backgroundThread = new Thread();
private static int loopCounter = 2;
private static Clip clip;
private static String[] backgroundFiles = {
"/40_Avalon.wav","/13_Glatisant.wav",
"/31_Lying_In_Deceit.wav","/43_Return_to_Base.wav"};
private static String[] files = {
"/bigboom.wav","/Robot_blip.wav",
"/battleStations.WAV","/beep1.wav",
"/button-47.wav","/button-35.wav",
"/beep-23.wav","/Sonar_pings.wav",
"/button-21.wav","/SONAR.WAV"};
private static AudioInputStream currentBackgroundMusic;
private static AudioInputStream currentSound;
private static boolean backgroundOn = false;
private static boolean canStart = true;
private static AudioInputStream loadSound(int s){
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(files[s]));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stream;
}
private static AudioInputStream loadBackground(int s){
AudioInputStream stream = null;
try {
stream = AudioSystem.getAudioInputStream(AudioManager.class.getClass().getResource(backgroundFiles[s]));
} catch (UnsupportedAudioFileException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return stream;
}
public static void setSound(int s){
currentSound = loadSound(s);
Thread thread = new Thread(new SoundPlayer());
thread.start();
}
private static void continueMusic(){
setBackgroundMusic(loopCounter);
loopCounter++;
if(loopCounter > 3) loopCounter = 0;
}
public static void playSound(){
try {
clip = AudioSystem.getClip();
clip.open(currentSound);
} catch (LineUnavailableException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
clip.start();
}
public static void setBackgroundMusic(int s){
if (backgroundOn) {
backgroundOn = false;
canStart = false;
try {
backgroundThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
currentBackgroundMusic = loadBackground(s);
backgroundThread = new Thread(new MusicPlayer());
backgroundOn = true;
backgroundThread.start();
canStart = true;
}
private static void playSound2(AudioInputStream audio) {
AudioFormat audioFormat = audio.getFormat();
SourceDataLine line = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,audioFormat);
try{
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
}
catch (Exception e)
{
e.printStackTrace();
}
line.start();
int nBytesRead = 0;
byte[] abData = new byte[128000];
while (nBytesRead != -1 && backgroundOn)
{
try{
nBytesRead = audio.read(abData, 0, abData.length);
} catch (IOException e){
e.printStackTrace();
}
if (nBytesRead == -1) break;
line.write(abData, 0, nBytesRead);
}
line.drain();
line.stop();
line.close();
line = null;
backgroundOn = false;
}
private static class MusicPlayer implements Runnable{
@Override
public void run() {
playSound2(currentBackgroundMusic);
}
}
private static class SoundPlayer implements Runnable{
@Override
public void run() {
playSound();
}
}
public static void loopMusic(){
Thread loop = new Thread(new Runnable(){
@Override
public void run() {
while(true){
if(backgroundThread.isAlive()){
try {
backgroundThread.join(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (canStart){
continueMusic();
}
}
}});
loop.start();
}
public static void reset(){
loopCounter = 2;
}
}