Hello everyone,
I’m trying to get a game working on Linux (Link: http://www.ludumdare.com/compo/ludum-dare-21/?uid=398 [yes, that’s Notch’s Ludum Dare entry]) - that is, I want to fix the sound, because the rest seems fine. The sound implementation in the game is very basic, but while trying to tinker with it, I’ve had trouble getting JavaSound to work with Linux at all (using Linux Mint 11).
So, the sound effects are used like this (taken from the source files):
package com.mojang.escape;
import javax.sound.sampled.*;
public class Sound {
public static Sound altar = loadSound("/snd/altar.wav");
// load a bunch more sounds...
public static Sound loadSound(String fileName) {
Sound sound = new Sound();
try {
AudioInputStream ais = AudioSystem.getAudioInputStream(Sound.class.getResource(fileName));
Clip clip = AudioSystem.getClip();
clip.open(ais);
sound.clip = clip;
} catch (Exception e) {
System.out.println(e);
}
return sound;
}
private Clip clip;
public void play() {
try {
if (clip != null) {
new Thread() {
public void run() {
synchronized (clip) {
clip.stop();
clip.setFramePosition(0);
clip.start();
}
}
}.start();
}
} catch (Exception e) {
System.out.println(e);
}
}
}
The input files are very short .wav files of the type “PCM_SIGNED 44100.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian” (according to “ais.getFormat().toString()”).
While this works fine on Windows, it fails on my Linux machine using OpenJDK 1.6 at the line “clip.open(ais);”:
java.lang.IllegalArgumentException: Invalid format
at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.createStream(PulseAudioDataLine.java:143)
at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.open(PulseAudioDataLine.java:100)
at org.classpath.icedtea.pulseaudio.PulseAudioDataLine.open(PulseAudioDataLine.java:289)
at org.classpath.icedtea.pulseaudio.PulseAudioClip.open(PulseAudioClip.java:402)
at org.classpath.icedtea.pulseaudio.PulseAudioClip.open(PulseAudioClip.java:453)
at com.mojang.escape.Sound.loadSound(Sound.java:33)
at com.mojang.escape.Sound.<clinit>(Sound.java:6)
So, I changed the “loadSound()” method to the following:
public static Sound loadSound(String fileName) {
Sound sound = new Sound();
try {
AudioInputStream ais = AudioSystem.getAudioInputStream(Sound.class.getResource(fileName));
Clip clip = (Clip) AudioSystem.getLine(new DataLine.Info(Clip.class, ais.getFormat()));
clip.open(ais);
sound.clip = clip;
} catch (Exception e) {
e.printStackTrace();
}
return sound;
}
This actually works, but if you try to play the same sound many times, there’s a pretty large delay (about 1 second) between each play. I’ve noticed that the “Clip.stop()” method takes a very long time on my machine, which it doesn’t on Windows. So, because playing the sound is synchronized in the “play()” method (see above), any attempts to play the sound again have to be queued up and wait until that’s finished. In the game, if you play the same sound again very quickly, that obviously doesn’t work because the action that triggered the sound is long over.
Because I don’t know very much about JavaSound, I decided to switch to the Sun JDK 1.6 and see if that works better. No matter if I use the first or second version of the “loadSound()” method, I get this at “clip.open(ais)”:
javax.sound.sampled.LineUnavailableException: line with format PCM_SIGNED 44100.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian not supported.
at com.sun.media.sound.DirectAudioDevice$DirectDL.implOpen(DirectAudioDevice.java:494)
at com.sun.media.sound.DirectAudioDevice$DirectClip.implOpen(DirectAudioDevice.java:1280)
at com.sun.media.sound.AbstractDataLine.open(AbstractDataLine.java:107)
at com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1061)
at com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1151)
at com.mojang.escape.Sound.loadSound(Sound.java:33)
at com.mojang.escape.Sound.<clinit>(Sound.java:7)
So, I thought, maybe the format actually isn’t supported. So I tried with a .wav file of the following format: “PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, little-endian”. I didn’t get any exceptions this time, but there’s a problem: I can’t hear anything. As I tried out various things from the forums, I looped through all the mixers on my system via “AudioSystem.getMixerInfo()”. With the Sun JDK, this gave me:
NVidia [plughw:0,0], version 1.0.23
NVidia [plughw:0,1], version 1.0.23
NVidia [plughw:0,2], version 1.0.23
Generic [plughw:1,3], version 1.0.23
Port NVidia [hw:0], version 1.0.23
Port Generic [hw:1], version 1.0.23
while with OpenJDK, I got:
PulseAudio Mixer, version 0.02
default [default], version 1.0.23
NVidia [plughw:0,0], version 1.0.23
NVidia [plughw:0,1], version 1.0.23
NVidia [plughw:0,2], version 1.0.23
Generic [plughw:1,3], version 1.0.23
Port NVidia [hw:0], version 1.0.23
Port Generic [hw:1], version 1.0.23
Now, because sound on Linux is generally a big pile of chaos, I don’t really know too much about it and I also don’t know what all those mixers are referring to. I do know, however, that I have PulseAudio, so perhaps the missing entries in the Sun JDK list explain why I got no exception, but still couldn’t hear any sound.
So, at this point, I’m stuck. To recap, I can’t play sound with the Sun JDK at all and with OpenJDK, I don’t know how to play the same sound multiple times, quickly. Is there any way I could get this running? I’d really like to stay with JavaSound and avoid any external libraries - the application is extremely simple. I mean, basically, I just want to play some sounds!