001package jmri.jmrit.sound; 002 003import java.util.Arrays; 004import org.slf4j.Logger; 005import org.slf4j.LoggerFactory; 006 007/** 008 * Wrap a byte array to provide WAV file functionality 009 * 010 * @author Bob Jacobsen Copyright (C) 2006 011 */ 012public class WavBuffer { 013 014 /** 015 * Create from already existing byte array. 016 * 017 * @param content a WAV format byte array, starting with a RIFF header 018 */ 019 public WavBuffer(byte[] content) { 020 buffer = Arrays.copyOf(content, content.length); 021 022 initFmt(); 023 initData(); 024 } 025 026 /** 027 * Create from contents of file. 028 * The file contents are expected to be in .wav format, 029 * starting with a RIFF header. 030 * @param file file containing the .wav. 031 * @throws java.io.IOException on error. 032 */ 033 public WavBuffer(java.io.File file) throws java.io.IOException { 034 if (file == null) { 035 throw new java.io.IOException("Null file during ctor"); 036 } 037 java.io.InputStream s = new java.io.BufferedInputStream( 038 new java.io.FileInputStream(file)); 039 040 try { 041 buffer = new byte[(int) file.length()]; 042 int count = s.read(buffer); 043 if (count != buffer.length) { 044 log.warn("Excepted {} bytes but read {} from file {}", buffer.length, count, file.getName()); 045 } 046 047 initFmt(); 048 initData(); 049 } catch (java.io.IOException e1) { 050 log.error("error reading file", e1); 051 throw e1; 052 } finally { 053 try { 054 s.close(); 055 } catch (java.io.IOException e2) { 056 log.error("Exception closing file", e2); 057 } 058 } 059 } 060 061 /** 062 * Find a specific header in the .wav fragment. 063 * @param i1 index 1. 064 * @param i2 index 2. 065 * @param i3 index 3. 066 * @param i4 index 4. 067 * @return offset of the 1st byte of the header in the buffer 068 */ 069 public int findHeader(int i1, int i2, int i3, int i4) { 070 // find chunk and set offset 071 int index = 12; // skip RIFF header 072 while (index < buffer.length) { 073 // new chunk 074 if (buffer[index] == i1 075 && buffer[index + 1] == i2 076 && buffer[index + 2] == i3 077 && buffer[index + 3] == i4) { 078 // found it, header in place 079 return index; 080 } else { 081 // skip 082 index = index + 8 083 + fourByte(index + 4); 084 } 085 } 086 log.error("Didn't find chunk"); 087 return 0; 088 } 089 090 /** 091 * Cache info from (first) "fmt " header 092 */ 093 private void initFmt() { 094 fmtOffset = findHeader(0x66, 0x6D, 0x74, 0x20); 095 if (fmtOffset > 0) { 096 return; 097 } 098 log.error("Didn't find fmt chunk"); 099 } 100 101 /** 102 * Cache info from (first) "data" header 103 */ 104 private void initData() { 105 dataOffset = findHeader(0x64, 0x61, 0x74, 0x61); 106 if (dataOffset > 0) { 107 return; 108 } 109 log.error("Didn't find data chunk"); 110 } 111 112 int fmtOffset; 113 int dataOffset; 114 115 byte[] buffer; 116 117 public float getSampleRate() { 118 return fourByte(fmtOffset + 12); 119 } 120 121 public int getSampleSizeInBits() { 122 return twoByte(fmtOffset + 22); 123 } 124 125 public int getChannels() { 126 return twoByte(fmtOffset + 10); 127 } 128 129 public boolean getBigEndian() { 130 return false; 131 } 132 133 public boolean getSigned() { 134 return (getSampleSizeInBits() > 8); 135 } 136 137 /** 138 * Offset to the first data byte in the buffer. 139 * @return first data byte offset. 140 */ 141 public int getDataStart() { 142 return dataOffset + 8; 143 } 144 145 /** 146 * Size of the data segment in bytes. 147 * @return data size in bytes. 148 */ 149 public int getDataSize() { 150 return fourByte(dataOffset + 4); 151 } 152 153 /** 154 * Offset to the last data byte in the buffer. 155 * One more than this points to the next header. 156 * @return data end value. 157 */ 158 public int getDataEnd() { 159 return dataOffset + 8 + getDataSize() - 1; 160 } 161 162 int twoByte(int index) { 163 return buffer[index] + buffer[index + 1] * 256; 164 } 165 166 int fourByte(int index) { 167 return (buffer[index] & 0xFF) 168 + (buffer[index + 1] & 0xFF) * 256 169 + (buffer[index + 2] & 0xFF) * 256 * 256 170 + (buffer[index + 3] & 0xFF) * 256 * 256 * 256; 171 } 172 173 public byte[] getByteArray() { 174 return Arrays.copyOf(buffer, buffer.length); 175 } 176 177 private final static Logger log = LoggerFactory.getLogger(WavBuffer.class); 178}