Кэширование звуков с использованием массивов байтов из файла jar

Я могу читать и воспроизводить звуки с помощью решения "Воспроизведение Clip" из javasound пометить вики-страницу. Однако для звуков, которые воспроизводятся часто (например, быстрый звук лазерной пушки, шаги и т. д.), меня раздражает открытие потоков и повторное чтение файлов каждый раз, когда вы хотите создать новый Clip. Итак, я пытаюсь кешировать прочитанные файлы в byte[] и впоследствии загружать их из кеша.

Загрузочная часть проста:

// Get a BAIS.
ByteArrayInputStream bais = new ByteArrayInputStream(cache.get(fileName));

// Convert to an audio stream.
AudioInputStream ais = AudioSystem.getAudioInputStream(bais);

Однако первоначальное преобразование содержимого файла в массив байтов оказывается сложной задачей. Проблема в том, что я пытаюсь прочитать звуки из файла, включенного в .jar, поэтому использование java.io.File не вариант (насколько я понимаю), и различные решения, которые я видел ( ссылки ниже) не применяются.

Мне кажется, что самой сложной частью будет получение длины файла для создания массива байтов без использования java.io.File. Я могу прочитать байты с помощью Scanner, но мне нужно прочитать их в какой-то массив. Должен ли я просто использовать ArrayList<Byte>? (См. Субоптимальный пример ниже.)

Итак, мой вопрос: как лучше всего прочитать встроенный файл в byte[] для повторного доступа позже?

Ограничения

  • Я должен иметь доступ к файлам в jarfile. Я считаю, что это ограничивает меня до Class.getResource или Class.getResourceAsStream.
  • Байты файла должны храниться в стандартной переменной byte[].
  • Я бы предпочел сделать это без введения ненужных зависимостей, таких как Guava или Apache Commons. Весь мой проект до сих пор находится на ванильной Java (JDK6), и я бы хотел, чтобы он оставался таким.

Что я пробовал?

Я пробовал использовать RandomAccessFile, например:

// 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);

Однако, по-видимому, это работает только для файла, на который ссылается диск; Я получаю следующую ошибку:

java.io.FileNotFoundException: \path\to\sound\in\jar\file.wav (Система не может найти указанный путь)

Неоптимальный пример

Хотя этот пример работает, я не думаю, что ArrayList — лучший способ сделать это из-за постоянного изменения размера и т. д.

// 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);
}

Предыдущее чтение


person wchargin    schedule 31.03.2013    source источник
comment
отличный формат вопроса!   -  person Martin Braun    schedule 02.04.2013


Ответы (1)


Попробуй это:

InputStream is = new BufferedInputStream(getClass().getResourceAsStream(name));
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int b; (b = is.read()) != -1;) {
    out.write(b);
}
byte[] a = out.toByteArray();

где name — это путь к файлу в папке .jar.

person Evgeniy Dorofeev    schedule 31.03.2013
comment
Интересно и умно. Спасибо. - person wchargin; 31.03.2013