I can read and play sounds using the "Playing a Clip
" solution from the javasound tag wiki page. However, for sounds that are played frequently (e.g., a quick laser gun sound, a footstep, etc.), it's jarring to me to be opening streams and re-reading files every time you want to create a new Clip
. So, I'm trying to cache read files into a byte[]
and, subsequently, load them from the cache.
The loading part is easy:
// Get a BAIS.
ByteArrayInputStream bais = new ByteArrayInputStream(cache.get(fileName));
// Convert to an audio stream.
AudioInputStream ais = AudioSystem.getAudioInputStream(bais);
However, initially getting the file contents into a byte array is proving to be a challenge. The problem is that I'm trying to read sounds from a file included in the .jar – so using java.io.File
isn't an option (as I understand), and the various solutions I've seen (links below) don't apply.
It seems to me that the hardest part would be getting the length of the file to create the byte array without using java.io.File
. I can read the bytes with a Scanner
, but I need to read them into some array. Should I just use ArrayList<Byte>
? (See "Suboptimal Example" below.)
So, my question: what is the best way that I can read an embedded file into a byte[]
for repeated access later?
Limitations
- I have to be able to access files within a jarfile. I believe this limits me to
Class.getResource
orClass.getResourceAsStream
. - The file bytes should be stored in a standard
byte[]
variable. - I'd prefer to do this without introducing unnecessary dependencies such as Guava or Apache Commons. My entire project so far is in vanilla Java (JDK6) and I'd like to keep it that way.
What have I tried?
I've tried using RandomAccessFile
, like so:
// Get the file.
RandomAccessFile f = new RandomAccessFile(fullPath, "r");
// Create a byte array.
theseBytes = new byte[(int) f.length()];
// Read into the array.
f.read(theseBytes);
// Close the file.
f.close();
// Put in map for later reference.
byteCache.put(fullPath, theseBytes);
However, apparently this only works for disk-referenced file; I get the following error:
java.io.FileNotFoundException: \path\to\sound\in\jar\file.wav (The system cannot find the path specified)
Suboptimal Example
While this example works, I don't think an ArrayList
is the best way to do this, due to constant resizing, etc.
// Get a stream.
InputStream s = clazz.getResourceAsStream(fullPath);
// Get a byte array.
ArrayList<Byte> byteArrayList = new ArrayList<Byte>();
// Create a storage variable.
int last = 0;
// Loop.
while ((last = s.read()) != -1) {
// Get it.
byteArrayList.add((byte) last);
}
// Create a byte array.
theseBytes = new byte[byteArrayList.size()];
// Loop over each element.
for (int i = 0; i < theseBytes.length; i++) {
// Set the byte.
theseBytes[i] = byteArrayList.get(i);
}