Skip to content

Instantly share code, notes, and snippets.

@tkaczenko
Last active July 27, 2020 10:44
Show Gist options
  • Save tkaczenko/56e784f563783155fe2706f692ef61e2 to your computer and use it in GitHub Desktop.
Save tkaczenko/56e784f563783155fe2706f692ef61e2 to your computer and use it in GitHub Desktop.
Console utility for reading Wave format (.wav) header written in Java
import wave.WavHeader;
import wave.WavHeaderReader;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* Console utility for reading meta data of the wave files
*/
public class MetaReader {
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Illegal command line arguments.\n" +
"Usage MetaReader\n\t MetaReader <source file path>");
return;
}
WavHeader wavHeader;
try {
WavHeaderReader wavHeaderReader = new WavHeaderReader(args[0]);
wavHeader = wavHeaderReader.read();
System.out.println(wavHeader.toString());
} catch (FileNotFoundException e) {
System.out.println("Error: File " + args[0] + " not found!");
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}
}
}
package wave;
/**
* This class contains a structure of WAVE file.
*
* @see <a href =
* "https://github.com/tkacz-/WavReader/blob/master/wav-sound-format.gif">
* Structure of wave format
* </a>
*/
public class WavHeader {
private byte[] chunkID = new byte[4];
private int chunkSize;
private byte[] format = new byte[4];
private byte[] subChunk1ID = new byte[4];
private int subChunk1Size;
private short audioFormat;
private short numChannels;
private int sampleRate;
private int byteRate;
private short blockAlign;
private short bitsPerSample;
private byte[] subChunk2ID = new byte[4];
private int subChunk2Size;
@Override
public String toString() {
return "The RIFF chunk desriptor: " + new String(this.getChunkID()) + "\n" +
"Size of this chunk: " + this.getChunkSize() + "\n" +
"Format: " + new String(this.getFormat()) + "\n" + "\n" +
"fmt subchunk: " + new String(this.getSubChunk1ID()) + "\n" +
"Size of this chunk: " + this.getSubChunk1Size() + "\n" +
"Audio format: " + this.getAudioFormat() + "\n" +
"Number of channels: " + this.getNumChannels() + "\n" +
"Sample rate: " + this.getSampleRate() + "\n" +
"Byte rate: " + this.getByteRate() + "\n" +
"Block align: " + this.getBlockAlign() + "\n" +
"Bits per sample: " + this.getBitsPerSample() + "\n" + "\n" +
"data subchunk: " + new String(this.getSubChunk2ID()) + "\n" +
"Size of this chunk: " + this.getSubChunk2Size();
}
public byte[] getChunkID() {
return chunkID;
}
public void setChunkID(byte[] chunkID) {
this.chunkID = chunkID;
}
public int getChunkSize() {
return chunkSize;
}
public void setChunkSize(int chunkSize) {
this.chunkSize = chunkSize;
}
public byte[] getFormat() {
return format;
}
public void setFormat(byte[] format) {
this.format = format;
}
public byte[] getSubChunk1ID() {
return subChunk1ID;
}
public void setSubChunk1ID(byte[] subChunk1ID) {
this.subChunk1ID = subChunk1ID;
}
public int getSubChunk1Size() {
return subChunk1Size;
}
public void setSubChunk1Size(int subChunk1Size) {
this.subChunk1Size = subChunk1Size;
}
public short getAudioFormat() {
return audioFormat;
}
public void setAudioFormat(short audioFormat) {
this.audioFormat = audioFormat;
}
public short getNumChannels() {
return numChannels;
}
public void setNumChannels(short numChannels) {
this.numChannels = numChannels;
}
public int getSampleRate() {
return sampleRate;
}
public void setSampleRate(int sampleRate) {
this.sampleRate = sampleRate;
}
public int getByteRate() {
return byteRate;
}
public void setByteRate(int byteRate) {
this.byteRate = byteRate;
}
public short getBlockAlign() {
return blockAlign;
}
public void setBlockAlign(short blockAlign) {
this.blockAlign = blockAlign;
}
public short getBitsPerSample() {
return bitsPerSample;
}
public void setBitsPerSample(short bitsPerSample) {
this.bitsPerSample = bitsPerSample;
}
public byte[] getSubChunk2ID() {
return subChunk2ID;
}
public void setSubChunk2ID(byte[] subChunk2ID) {
this.subChunk2ID = subChunk2ID;
}
public int getSubChunk2Size() {
return subChunk2Size;
}
public void setSubChunk2Size(int subChunk2Size) {
this.subChunk2Size = subChunk2Size;
}
}
package wave;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* Custom wave header reader which use class
*
* @see WavHeader
*/
public class WavHeaderReader {
/**
* Size for the wave header
*/
private static final int HEADER_SIZE = 44;
/**
* Buffer which contain bytes of the wave header
*/
private byte[] buf = new byte[HEADER_SIZE];
/**
* Wave header
*/
private WavHeader header = new WavHeader();
/**
* InputStream to the wave file
*/
private InputStream inputStream;
/**
* Empty constructor
*/
public WavHeaderReader() {
}
/**
* Constructor which create own FileInputStream
*
* @param source absolute path to the file
* @throws IOException
*/
public WavHeaderReader(String source) throws IOException {
inputStream = new FileInputStream(source);
}
/**
* Constructor which use created InputStream
*
* @param inputStream InputStream to the wave file
*/
public WavHeaderReader(InputStream inputStream) {
this.inputStream = inputStream;
}
/**
* Read wave header of the file
*
* @return WavHeader object
* @throws IOException
* @see WavHeader
*/
public WavHeader read() throws IOException {
int res = inputStream.read(buf);
if (res != HEADER_SIZE) {
throw new IOException("Could not read header.");
}
header.setChunkID(Arrays.copyOfRange(buf, 0, 4));
if (new String(header.getChunkID()).compareTo("RIFF") != 0) {
throw new IOException("Illegal format.");
}
header.setChunkSize(toInt(4, false));
header.setFormat(Arrays.copyOfRange(buf, 8, 12));
header.setSubChunk1ID(Arrays.copyOfRange(buf, 12, 16));
header.setSubChunk1Size(toInt(16, false));
header.setAudioFormat(toShort(20, false));
header.setNumChannels(toShort(22, false));
header.setSampleRate(toInt(24, false));
header.setByteRate(toInt(28, false));
header.setBlockAlign(toShort(32, false));
header.setBitsPerSample(toShort(34, false));
header.setSubChunk2ID(Arrays.copyOfRange(buf, 36, 40));
header.setSubChunk2Size(toInt(40, false));
return header;
}
/**
* Convert byte[] array to int number
*
* @param start start position of the buffer
* @param endian <code>true</code> for big-endian
* <code>false</code> for little-endian
* @return converted number
*/
private int toInt(int start, boolean endian) {
int k = (endian) ? 1 : -1;
if (!endian) {
start += 3;
}
return (buf[start] << 24) + (buf[start + k * 1] << 16) +
(buf[start + k * 2] << 8) + buf[start + k * 3];
}
/**
* Convert byte[] array to short number
*
* @param start start position of the buffer
* @param endian <code>true</code> for big-endian
* <code>false</code> for little-endian
* @return converted number
*/
private short toShort(int start, boolean endian) {
short k = (endian) ? (short) 1 : -1;
if (!endian) {
start++;
}
return (short) ((buf[start] << 8) + (buf[start + k * 1]));
}
/**
* Return the buffer which contain wave hader
*
* @return buffer
*/
public byte[] getBuf() {
return buf;
}
/**
* Return WavHeader object which contain wave header
*
* @return WavHeader object
*/
public WavHeader getHeader() {
return header;
}
/**
* Return input stream to the wave file
*
* @return Input steam to the wave file
*/
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}
package wave;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test reading wave header
*/
public class WavHeaderReaderTest {
private String source;
private WavHeaderReader wavHeaderReader;
@Before
public void setUp() throws Exception {
source = "src/test/resources/bugsbunny1.wav";
wavHeaderReader = new WavHeaderReader(source);
}
@After
public void tearDown() throws Exception {
source = null;
wavHeaderReader = null;
}
@Test
public void read() throws Exception {
WavHeader wavHeader = wavHeaderReader.read();
String expected = new String(wavHeader.getChunkID());
String actual = "RIFF";
assertEquals(expected, actual);
expected = new String(wavHeader.getFormat());
actual = "WAVE";
assertEquals(expected, actual);
expected = new String(wavHeader.getSubChunk1ID());
actual = "fmt ";
assertEquals(expected, actual);
expected = new String(wavHeader.getSubChunk2ID());
actual = "data";
assertEquals(expected, actual);
}
}
@asarkar
Copy link

asarkar commented Jul 27, 2020

There's a much easier way, see https://stackoverflow.com/a/63113503/839733

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment