Last active
July 27, 2020 10:44
-
-
Save tkaczenko/56e784f563783155fe2706f692ef61e2 to your computer and use it in GitHub Desktop.
Console utility for reading Wave format (.wav) header written in Java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There's a much easier way, see https://stackoverflow.com/a/63113503/839733