summaryrefslogtreecommitdiffstats
path: root/src/jake2/sound/WaveLoader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/jake2/sound/WaveLoader.java')
-rw-r--r--src/jake2/sound/WaveLoader.java302
1 files changed, 302 insertions, 0 deletions
diff --git a/src/jake2/sound/WaveLoader.java b/src/jake2/sound/WaveLoader.java
new file mode 100644
index 0000000..020ab52
--- /dev/null
+++ b/src/jake2/sound/WaveLoader.java
@@ -0,0 +1,302 @@
+/*
+ * SND_MEM.java
+ * Copyright (C) 2004
+ *
+ * $Id: WaveLoader.java,v 1.1 2004-07-09 06:50:48 hzi Exp $
+ */
+/*
+Copyright (C) 1997-2001 Id Software, Inc.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+package jake2.sound;
+
+import jake2.Defines;
+import jake2.qcommon.Com;
+import jake2.qcommon.FS;
+import jake2.sys.Sys;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.sound.sampled.*;
+
+/**
+ * SND_MEM
+ */
+public class WaveLoader {
+
+ private static final AudioFormat sampleFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 22050, 16, 1, 2, 22050, false);
+
+ /*
+ ==============
+ S_LoadSound
+ ==============
+ */
+ public static sfxcache_t LoadSound(sfx_t s) {
+ String namebuffer;
+ byte[] data;
+ wavinfo_t info;
+ int len;
+ float stepscale;
+ sfxcache_t sc = null;
+ int size;
+ String name;
+
+ if (s.name.charAt(0) == '*')
+ return null;
+
+ // see if still in memory
+ sc = s.cache;
+ if (sc != null)
+ return sc;
+
+ // load it in
+ if (s.truename != null)
+ name = s.truename;
+ else
+ name = s.name;
+
+ if (name.charAt(0) == '#')
+ namebuffer = name.substring(1);
+
+ else
+ namebuffer = "sound/" + name;
+
+ data = FS.LoadFile(namebuffer);
+
+ if (data == null) {
+ Com.DPrintf("Couldn't load " + namebuffer + "\n");
+ return null;
+ }
+ size = data.length;
+
+ info = GetWavinfo(s.name, data, size);
+
+ AudioInputStream in = null;
+ AudioInputStream out = null;
+ try {
+ in = AudioSystem.getAudioInputStream(new ByteArrayInputStream(data));
+ if (in.getFormat().getSampleSizeInBits() == 8) {
+ in = convertTo16bit(in);
+ }
+ out = AudioSystem.getAudioInputStream(sampleFormat, in);
+ int l = (int)out.getFrameLength();
+ sc = s.cache = new sfxcache_t(l*2);
+ sc.length = l;
+ int c = out.read(sc.data, 0, l * 2);
+ out.close();
+ in.close();
+ } catch (Exception e) {
+ Com.Printf("Couldn't load " + namebuffer + "\n");
+ return null;
+ }
+
+ sc.loopstart = info.loopstart * ((int)sampleFormat.getSampleRate() / info.rate);
+ sc.speed = (int)sampleFormat.getSampleRate();
+ sc.width = sampleFormat.getSampleSizeInBits() / 8;
+ sc.stereo = 0;
+
+ data = null;
+
+ return sc;
+ }
+
+ static AudioInputStream convertTo16bit(AudioInputStream in) throws IOException {
+ AudioFormat format = in.getFormat();
+ int length = (int)in.getFrameLength();
+ byte[] samples = new byte[2*length];
+
+ for (int i = 0; i < length; i++) {
+ in.read(samples, 2*i+1, 1);
+ samples[2*i+1] -= 128;
+ }
+ in.close();
+
+ AudioFormat newformat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), 16, format.getChannels(), 2, format.getFrameRate(), false);
+ return new AudioInputStream(new ByteArrayInputStream(samples), newformat, length);
+ }
+
+ /*
+ ===============================================================================
+
+ WAV loading
+
+ ===============================================================================
+ */
+
+ static byte[] data_b;
+ static int data_p;
+ static int iff_end;
+ static int last_chunk;
+ static int iff_data;
+ static int iff_chunk_len;
+
+
+ static short GetLittleShort() {
+ int val = 0;
+ val = data_b[data_p] & 0xFF;
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 8);
+ data_p++;
+ return (short)val;
+ }
+
+ static int GetLittleLong() {
+ int val = 0;
+ val = data_b[data_p] & 0xFF;
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 8);
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 16);
+ data_p++;
+ val |= ((data_b[data_p] & 0xFF) << 24);
+ data_p++;
+ return val;
+ }
+
+ static void FindNextChunk(String name) {
+ while (true) {
+ data_p = last_chunk;
+
+ if (data_p >= iff_end) { // didn't find the chunk
+ data_p = 0;
+ return;
+ }
+
+ data_p += 4;
+
+ iff_chunk_len = GetLittleLong();
+
+ if (iff_chunk_len < 0) {
+ data_p = 0;
+ return;
+ }
+ if (iff_chunk_len > 1024*1024)
+ Sys.Error("FindNextChunk: length is past the 1 meg sanity limit");
+
+ data_p -= 8;
+ last_chunk = data_p + 8 + ((iff_chunk_len + 1) & ~1);
+ String s = new String(data_b, data_p, 4);
+ if (s.equals(name))
+ return;
+ }
+ }
+
+ static void FindChunk(String name) {
+ last_chunk = iff_data;
+ FindNextChunk(name);
+ }
+
+ /*
+ ============
+ GetWavinfo
+ ============
+ */
+ static wavinfo_t GetWavinfo(String name, byte[] wav, int wavlength) {
+ wavinfo_t info = new wavinfo_t();
+ int i;
+ int format;
+ int samples;
+
+ if (wav == null)
+ return info;
+
+ iff_data = 0;
+ iff_end = wavlength;
+ data_b = wav;
+
+ // find "RIFF" chunk
+ FindChunk("RIFF");
+ String s = new String(data_b, data_p + 8, 4);
+ if (!s.equals("WAVE")) {
+ Com.Printf("Missing RIFF/WAVE chunks\n");
+ return info;
+ }
+
+ // get "fmt " chunk
+ iff_data = data_p + 12;
+ // DumpChunks ();
+
+ FindChunk("fmt ");
+ if (data_p == 0) {
+ Com.Printf("Missing fmt chunk\n");
+ return info;
+ }
+ data_p += 8;
+ format = GetLittleShort();
+ if (format != 1) {
+ Com.Printf("Microsoft PCM format only\n");
+ return info;
+ }
+
+ info.channels = GetLittleShort();
+ info.rate = GetLittleLong();
+ data_p += 4 + 2;
+ info.width = GetLittleShort() / 8;
+
+ // get cue chunk
+ FindChunk("cue ");
+ if (data_p != 0) {
+ data_p += 32;
+ info.loopstart = GetLittleLong();
+ // Com_Printf("loopstart=%d\n", sfx->loopstart);
+
+ // if the next chunk is a LIST chunk, look for a cue length marker
+ FindNextChunk("LIST");
+ if (data_p != 0) {
+ s = new String(data_b, data_p + 28, 4);
+ if (s.equals("MARK")) { // this is not a proper parse, but it works with cooledit...
+ data_p += 24;
+ i = GetLittleLong(); // samples in loop
+ info.samples = info.loopstart + i;
+ // Com_Printf("looped length: %i\n", i);
+ }
+ }
+ } else
+ info.loopstart = -1;
+
+ // find data chunk
+ FindChunk("data");
+ if (data_p == 0) {
+ Com.Printf("Missing data chunk\n");
+ return info;
+ }
+
+ data_p += 4;
+ samples = GetLittleLong() / info.width;
+
+ if (info.samples != 0) {
+ if (samples < info.samples)
+ Com.Error(Defines.ERR_DROP, "Sound " + name + " has a bad loop length");
+ } else
+ info.samples = samples;
+
+ info.dataofs = data_p;
+
+ return info;
+ }
+
+ static class wavinfo_t {
+ int rate;
+ int width;
+ int channels;
+ int loopstart;
+ int samples;
+ int dataofs; // chunk starts this many bytes from file start
+ }
+}