package javaforce.media; import java.io.*; import java.nio.*; import javaforce.*; /** Load .wav audio files (supports 16,24,32bit,1-2 channels,any frequency,PCM only) * Note:readAllSamples() does not convert 24bit samples. */ public class Wav { public String errmsg; //last errmsg if any public int chs = -1; //# channels public int rate = -1; //sample rate (freq) public int bits = -1; //bits per sample public int bytes = -1; //byte per sample public byte samples8[]; //if readAllSamples() was called public short samples16[]; //if readAllSamples() was called public int samples32[]; //if readAllSamples() was called public int dataLength; //bytes private InputStream wav = null; public boolean load(String fn) { try { wav = new FileInputStream(fn); return load(wav); } catch (Exception e) { JFLog.log(e); return false; } } public boolean load(InputStream is) { wav = is; errmsg = ""; try { byte data[] = new byte[30]; //read RIFF header (20 bytes); wav.read(data, 0, 20); if (!LE.getString(data, 0, 4).equals("RIFF")) throw new Exception("wav is not a valid WAV file (RIFF)"); if (!LE.getString(data, 8, 4).equals("WAVE")) throw new Exception("wav is not a valid WAV file (WAVE)"); if (!LE.getString(data, 12, 4).equals("fmt ")) throw new Exception("wav is not a valid WAV file (fmt )"); int fmtsiz = LE.getuint32(data, 16); if ((fmtsiz < 16) || (fmtsiz > 30)) throw new Exception("wav is not a valid WAV file (fmtsiz)"); wav.read(data, 0, fmtsiz); if (LE.getuint16(data, 0) != 1) throw new Exception("wav is not PCM"); chs = LE.getuint16(data, 2); if (chs < 1 || chs > 2) throw new Exception("wav is not supported (# chs)"); rate = LE.getuint32(data, 4); bits = LE.getuint16(data, 14); switch (bits) { // case 8: bytes = 1; break; //can't support 8bit for now (upscale later ???) case 16: bytes = 2; break; case 24: bytes = 3; break; case 32: bytes = 4; break; default: throw new Exception("wav is not supported (bits="+bits+")"); } wav.read(data, 0, 8); while (true) { dataLength = LE.getuint32(data, 4); if (LE.getString(data, 0, 4).equals("data")) break; //ignore chunk (FACT, INFO, etc.) wav.skip(dataLength); wav.read(data, 0, 8); } } catch (java.io.FileNotFoundException e2) { errmsg = "WAV file not found"; try { if (wav != null) wav.close(); } catch (Exception e3) {} return false; } catch (Exception e1) { errmsg = e1.toString(); try { if (wav != null) wav.close(); } catch (Exception e4) {} return false; } return true; } /** Closes wav file */ public void close() { try { wav.close(); } catch (Exception e) {} } /** Reads all samples and closes file. */ public boolean readAllSamples() { try { samples8 = JF.readAll(wav, dataLength); wav.close(); switch (bits) { case 8: return true; case 24: //TODO!!!??? break; case 16: samples16 = new short[dataLength / 2]; ByteBuffer.wrap(samples8).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples16); break; case 32: samples32 = new int[dataLength / 4]; ByteBuffer.wrap(samples8).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get(samples32); break; } return true; } catch (Exception e) { JFLog.log(e); return false; } } /** Returns next chunk of samples. */ public byte[] readSamples(int nSamples) { int byteLength = nSamples*bytes*chs; byte read8[]; read8 = JF.readAll(wav, byteLength); if (read8 == null) return null; byte read32[]; int lenXchs = nSamples * chs, pos = 0, pos24 = 0; switch (bits) { case 16: read32 = new byte[nSamples*2*chs]; for(int a=0;a<lenXchs;a++) { read32[pos+0] = read8[pos+0]; read32[pos+1] = read8[pos+1]; pos += 2; } return read32; case 32: read32 = new byte[nSamples*4*chs]; for(int a=0;a<lenXchs;a++) { read32[pos+0] = read8[pos+0]; read32[pos+1] = read8[pos+1]; read32[pos+2] = read8[pos+2]; read32[pos+3] = read8[pos+3]; pos += 4; } return read32; case 24: //must convert to 32bits read32 = new byte[nSamples*4*chs]; for(int a=0;a<lenXchs;a++) { read32[pos+0] = read8[pos24+0]; read32[pos+1] = read8[pos24+1]; read32[pos+2] = read8[pos24+2]; pos += 4; pos24 += 3; } return read32; } return null; } public boolean save(String fn) { try { FileOutputStream fos = new FileOutputStream(fn); boolean ret = save(fos); fos.close(); return ret; } catch (Exception e) { JFLog.log(e); return false; } } /** Save entire wav file (supports 16/32bit only) */ public boolean save(OutputStream os) { if (bits != 16 && bits != 32) return false; int size = 0; switch (bits) { case 16: bytes = 2; size = samples16.length * 2; break; case 32: bytes = 4; size = samples32.length * 4; break; } try { byte data[] = new byte[20]; //write RIFF header (20 bytes); LE.setString(data, 0, 4, "RIFF"); LE.setuint32(data, 4, size + 36); //rest of file size LE.setString(data, 8, 4, "WAVE"); LE.setString(data, 12, 4, "fmt "); LE.setuint32(data, 16, 16); //fmt size os.write(data, 0, 20); //write fmt header (16 bytes) data = new byte[16 + 4 + 4]; LE.setuint16(data, 0, 1); //PCM LE.setuint16(data, 2, chs); LE.setuint32(data, 4, rate); LE.setuint32(data, 8, bytes * chs * rate); //bytes rate/sec LE.setuint32(data, 12, bytes * chs); //block align LE.setuint16(data, 14, bits); LE.setString(data, 16, 4, "data"); LE.setuint32(data, 20, size); os.write(data, 0, 16 + 4 + 4); switch (bits) { case 16: os.write(LE.shortArray2byteArray(samples16, null)); break; case 32: os.write(LE.intArray2byteArray(samples32, null)); break; } } catch (Exception e) { errmsg = e.toString(); return false; } return false; } }