The purpose of this code is to pull the byte array from a wav file and change that byte array into audio data that I can then do signal processing with. There seems to be a problem with the code that parses the bytes into shorts. Can anyone tell me what I have done wrong?
Here is the code:
buttonDecode.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
buttonStart.setEnabled(false);
buttonDecode.setEnabled(false);
buttonPlay.setEnabled(false);
GraphView thegraph = (GraphView) findViewById(R.id.thegraph);
series = new LineGraphSeries<DataPoint>();
double x,y;
x = 0;
try {
//old wav byte array code
ByteArrayOutputStream outt = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(wavfile));
int read;
byte[] buff = new byte[RECORDER_SAMPLERATE]; //was 1024
in.skip(44);
while ((read = in.read(buff)) > 0)
{
outt.write(buff, 0, read);
}
outt.flush();
byte[] audioBytes = outt.toByteArray();
int[] theRealData = null;
//parsing the vector
for(int i = 0; i < ((audioBytes.length)/2) ; i++) {
byte highByte = audioBytes[(2*i)+1];
byte lowByte = audioBytes[(2*i)];
short value = twoBytesToShort(highByte,lowByte);
theRealData[(i+1)/2] = value;
}
for(int i = 0; i < theRealData.length;i++){
int curByte = theRealData[i];
y = (curByte);
series.appendData(new DataPoint(x,y), true, theRealData.length);
x = x + 1;
}
thegraph.addSeries(series);
} catch (IOException e) {
e.printStackTrace();
}
buttonStart.setEnabled(true);
buttonDecode.setEnabled(true);
buttonPlay.setEnabled(true);
}
});
And the function:
public static short twoBytesToShort(byte b1, byte b2) {
return (short) ((b1 << 8) | (b2 & 0xFF));
}
When experimenting with ZLib compression, I have run across a strange problem. Decompressing a zlib-compressed byte array with random data fails reproducibly if the source array is at least 32752 bytes long. Here's a little program that reproduces the problem, you can see it in action on IDEOne. The compression and decompression methods are standard code picked off tutorials.
public class ZlibMain {
private static byte[] compress(final byte[] data) {
final Deflater deflater = new Deflater();
deflater.setInput(data);
deflater.finish();
final byte[] bytesCompressed = new byte[Short.MAX_VALUE];
final int numberOfBytesAfterCompression = deflater.deflate(bytesCompressed);
final byte[] returnValues = new byte[numberOfBytesAfterCompression];
System.arraycopy(bytesCompressed, 0, returnValues, 0, numberOfBytesAfterCompression);
return returnValues;
}
private static byte[] decompress(final byte[] data) {
final Inflater inflater = new Inflater();
inflater.setInput(data);
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length)) {
final byte[] buffer = new byte[Math.max(1024, data.length / 10)];
while (!inflater.finished()) {
final int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
final byte[] output = outputStream.toByteArray();
return output;
} catch (DataFormatException | IOException e) {
throw new RuntimeException(e);
}
}
public static void main(final String[] args) {
roundTrip(100);
roundTrip(1000);
roundTrip(10000);
roundTrip(20000);
roundTrip(30000);
roundTrip(32000);
for (int i = 32700; i < 33000; i++) {
if(!roundTrip(i))break;
}
}
private static boolean roundTrip(final int i) {
System.out.printf("Starting round trip with size %d: ", i);
final byte[] data = new byte[i];
for (int j = 0; j < data.length; j++) {
data[j]= (byte) j;
}
shuffleArray(data);
final byte[] compressed = compress(data);
try {
final byte[] decompressed = CompletableFuture.supplyAsync(() -> decompress(compressed))
.get(2, TimeUnit.SECONDS);
System.out.printf("Success (%s)%n", Arrays.equals(data, decompressed) ? "matching" : "non-matching");
return true;
} catch (InterruptedException | ExecutionException | TimeoutException e) {
System.out.println("Failure!");
return false;
}
}
// Implementing Fisher–Yates shuffle
// source: https://stackoverflow.com/a/1520212/342852
static void shuffleArray(byte[] ar) {
Random rnd = ThreadLocalRandom.current();
for (int i = ar.length - 1; i > 0; i--) {
int index = rnd.nextInt(i + 1);
// Simple swap
byte a = ar[index];
ar[index] = ar[i];
ar[i] = a;
}
}
}
Is this a known bug in ZLib? Or do I have an error in my compress / decompress routines?
It is an error in the logic of the compress / decompress methods; I am not this deep in the implementations but with debugging I found the following:
When the buffer of 32752 bytes is compressed, the deflater.deflate() method returns a value of 32767, this is the size to which you initialized the buffer in the line:
final byte[] bytesCompressed = new byte[Short.MAX_VALUE];
If you increase the buffer size for example to
final byte[] bytesCompressed = new byte[4 * Short.MAX_VALUE];
the you will see, that the input of 32752 bytes actually is deflated to 32768 bytes. So in your code, the compressed data does not contain all the data which should be in there.
When you then try to decompress, the inflater.inflate()method returns zero which indicates that more input data is needed. But as you only check for inflater.finished() you end in an endless loop.
So you can either increase the buffer size on compressing, but that probably just means haveing the problem with bigger files, or you better need to rewrite to compress/decompress logic to process your data in chunks.
Apparently the compress() method was faulty.
This one works:
public static byte[] compress(final byte[] data) {
try (final ByteArrayOutputStream outputStream =
new ByteArrayOutputStream(data.length);) {
final Deflater deflater = new Deflater();
deflater.setInput(data);
deflater.finish();
final byte[] buffer = new byte[1024];
while (!deflater.finished()) {
final int count = deflater.deflate(buffer);
outputStream.write(buffer, 0, count);
}
final byte[] output = outputStream.toByteArray();
return output;
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
I'm trying to make a simplified HDFS (Hadoop Distributed File System) for a final project in a Distributed System course.
So, the first thing that I'm trying is to write a program which split an arbitrary file into blocks (chunks) of an arbitrary dimension.
I found this useful example, which code is:
package javabeat.net.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* Split File Example
*
* #author Krishna
*
*/
public class SplitFileExample {
private static String FILE_NAME = "TextFile.txt";
private static byte PART_SIZE = 5;
public static void main(String[] args) {
File inputFile = new File(FILE_NAME);
FileInputStream inputStream;
String newFileName;
FileOutputStream filePart;
int fileSize = (int) inputFile.length();
int nChunks = 0, read = 0, readLength = PART_SIZE;
byte[] byteChunkPart;
try {
inputStream = new FileInputStream(inputFile);
while (fileSize > 0) {
if (fileSize <= 5) {
readLength = fileSize;
}
byteChunkPart = new byte[readLength];
read = inputStream.read(byteChunkPart, 0, readLength);
fileSize -= read;
assert (read == byteChunkPart.length);
nChunks++;
newFileName = FILE_NAME + ".part"
+ Integer.toString(nChunks - 1);
filePart = new FileOutputStream(new File(newFileName));
filePart.write(byteChunkPart);
filePart.flush();
filePart.close();
byteChunkPart = null;
filePart = null;
}
inputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
But I think that there is a big issue: the value of PART_SIZE cannot be greater than 127, otherwise an error: possible loss of precision will occur.
How can I solve without totally changing the code?
The problem is that PART_SIZE is a byte; its maximum value is therefore indeed 127.
The code you have at the moment however is full of problems; for one, incorrect resource handling etc.
Here is a version using java.nio.file:
private static final String FILENAME = "TextFile.txt";
private static final int PART_SIZE = xxx; // HERE
public static void main(final String... args)
throws IOException
{
final Path file = Paths.get(FILENAME).toRealPath();
final String filenameBase = file.getFileName().toString();
final byte[] buf = new byte[PART_SIZE];
int partNumber = 0;
Path part;
int bytesRead;
byte[] toWrite;
try (
final InputStream in = Files.newInputStream(file);
) {
while ((bytesRead = in.read(buf)) != -1) {
part = file.resolveSibling(filenameBase + ".part" + partNumber);
toWrite = bytesRead == PART_SIZE ? buf : Arrays.copyOf(buf, bytesRead);
Files.write(part, toWrite, StandardOpenOption.CREATE_NEW);
partNumber++;
}
}
}
List<PDDocument> Pages=new ArrayList<PDDocument>();
Document.load(filePath);
try {
Splitter splitter = new Splitter();
splitter.setSplitAtPage(NoOfPagesDocumentWillContain);
Pages = splitter.split(document);
}catch(Exception e)
{
l
e.getCause().printStackTrace();
}
I have an audio file that I am converting into a byte array, but then you cannot tell when that byte value is actually played in the song. So I am trying to stretch it out over the length of the song.
So while the song is playing, it outputs the byte value. How is this possible?
Here is my code so far:
public class Main {
private static final String FILENAME = "assets/pf.wav";
private static double[] endResult = null;
public static void convert() throws IOException{
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(FILENAME));
int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0)
{
out.write(buff, 0, read);
}
out.flush();
byte[] audioBytes = out.toByteArray();
endResult = calculateFFT(audioBytes);
}
public static double[] calculateFFT(byte[] signal)
{
final int mNumberOfFFTPoints =1024;
double mMaxFFTSample;
double temp;
Complex[] y;
Complex[] complexSignal = new Complex[mNumberOfFFTPoints];
double[] absSignal = new double[mNumberOfFFTPoints/2];
for(int i = 0; i < mNumberOfFFTPoints; i++){
temp = (double)((signal[2*i] & 0xFF) | (signal[2*i+1] << 8)) / 32768.0F;
complexSignal[i] = new Complex(temp,0.0);
}
y = FFT.fft(complexSignal);
mMaxFFTSample = 0.0;
int mPeakPos = 0;
for(int i = 0; i < (mNumberOfFFTPoints/2); i++)
{
absSignal[i] = Math.sqrt(Math.pow(y[i].re(), 2) + Math.pow(y[i].im(), 2));
if(absSignal[i] > mMaxFFTSample)
{
mMaxFFTSample = absSignal[i];
mPeakPos = i;
}
}
return absSignal;
}
public static void main(String[] args) throws UnsupportedAudioFileException, IOException {
File file = new File(FILENAME);
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
AudioFormat format = audioInputStream.getFormat();
long frames = audioInputStream.getFrameLength();
final double durationInSeconds = (frames+0.0) / format.getFrameRate();
try {
convert();
for(int i = 0; i < endResult.length; i++) {
System.out.println(endResult[i]);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
How can I print out the value of the byte array (endResult[i]) over time and not immediately?
Whenever you print out a value, do:
Thread.sleep(100);
To wait 100 milliseconds (0.1 seconds) before printing the next value. This is adjustable of course.
My project is 'Speech Recognition of Azeri speech'. I have to write a program that converts wav files to byte array.
How to convert audio file to byte[]?
Basically as described by the snippet in the first answer, but instead of the BufferedInputStream use AudioSystem.getAudioInputStream(File) to get the InputStream.
Using the audio stream as obtained from AudioSystem will ensure that the headers are stripped, and the input file decode to a byte[] that represents the actual sound frames/samples - which can then be used for FFT etc.
Write this file into ByteArrayOutputStream
ByteArrayOutputStream out = new ByteArrayOutputStream();
BufferedInputStream in = new BufferedInputStream(new FileInputStream(WAV_FILE));
int read;
byte[] buff = new byte[1024];
while ((read = in.read(buff)) > 0)
{
out.write(buff, 0, read);
}
out.flush();
byte[] audioBytes = out.toByteArray();
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.LinkedHashMap;
import javax.sound.sampled.*;
/**
* This class reads a .wav file and converts it to a bunch of byte arrays.
*
* The info represented by these byte arrays is then printed out.
*
* An example of playing these byte arrays with the speakers is used.
*
* It also converts the byte arrays to a .wav file.
*
* An extension of this concept can record from a microphone.
* In this case, some values like sampling rate would need to be assumed.
*
* See https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ for .wav file spec
*
* #author sizu
*/
public class WavFileHelper {
public static void main(String[] args) {
final String NEWLINE = "\n";
int recordingSampleRate = 22050;
short recordingBitsPerSample = 16;
short recordingNumChannels = 2;
String inputFile = "/input.wav"; // Place the wav file in the top level directory, ie S:/input.wav
String outputFile = "/output.wav";
String recordedFile = "/capture.wav";
System.out.println("START");
try {
WavData wavInputData = new WavData();
WavData wavRecordData = new WavData();
wavRecordData.put(WaveSection.SAMPLE_RATE, recordingSampleRate);
wavRecordData.put(WaveSection.BITS_PER_SAMPLE, recordingBitsPerSample);
wavRecordData.put(WaveSection.NUM_CHANNELS, recordingNumChannels);
System.out.println(NEWLINE+"CONVERT WAV FILE TO BYTE ARRAY");
wavInputData.read(inputFile);
System.out.println(NEWLINE+"CONVERT BYTE ARRAY TO WAV FILE");
wavInputData.write(outputFile);
System.out.println(NEWLINE+"DISPLAY BYTE ARRAY INFORMATION FOR INPUT FILE");
wavInputData.printByteInfo();
System.out.println(NEWLINE+"START RECORDING - You can connect the microphone to the speakers");
WavAudioRecorder recorder = new WavFileHelper.WavAudioRecorder(wavRecordData);
recorder.startRecording();
System.out.println(NEWLINE+"PLAY BYTE ARRAY (THIS WILL BE RECORDED)");
WavAudioPlayer player = new WavFileHelper.WavAudioPlayer(wavInputData);
player.playAudio();
System.out.println(NEWLINE+"STOP RECORDING FOR RECORDING");
recorder.stopRecording();
System.out.println(NEWLINE+"DISPLAY BYTE ARRAY INFORMATION");
wavRecordData.printByteInfo();
System.out.println(NEWLINE+"SAVE RECORDING IN WAV FILE");
wavRecordData.write(recordedFile);
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("FINISH");
}
public static enum WaveSection {
// 12 Bytes
CHUNK_ID(4, ByteOrder.BIG_ENDIAN),
CHUNK_SIZE(4, ByteOrder.LITTLE_ENDIAN),
FORMAT(4, ByteOrder.BIG_ENDIAN),
// 24 Bytes
SUBCHUNK1_ID(4, ByteOrder.BIG_ENDIAN),
SUBCHUNK1_SIZE(4, ByteOrder.LITTLE_ENDIAN),
AUDIO_FORMAT(2, ByteOrder.LITTLE_ENDIAN),
NUM_CHANNELS(2, ByteOrder.LITTLE_ENDIAN),
SAMPLE_RATE(4, ByteOrder.LITTLE_ENDIAN),
BYTE_RATE(4, ByteOrder.LITTLE_ENDIAN),
BLOCK_ALIGN(2, ByteOrder.LITTLE_ENDIAN),
BITS_PER_SAMPLE(2, ByteOrder.LITTLE_ENDIAN),
// 8 Bytes
SUBCHUNK2_ID(4, ByteOrder.BIG_ENDIAN),
SUBCHUNK2_SIZE(4, ByteOrder.LITTLE_ENDIAN),
DATA(0, ByteOrder.LITTLE_ENDIAN),
;
private Integer numBytes;
private ByteOrder endian;
WaveSection(Integer numBytes, ByteOrder endian){
this.numBytes = numBytes;
this.endian = endian;
}
}
public static class WavData extends LinkedHashMap<WaveSection, byte[]>{
static int HEADER_SIZE = 44; // There are 44 bits before the data section
static int DEFAULT_SUBCHUNK1_SIZE = 16;
static short DEFAULT_AUDIO_FORMAT = 1;
static short DEFAULT_BLOCK_ALIGN = 4;
static String DEFAULT_CHUNK_ID = "RIFF";
static String DEFAULT_FORMAT = "WAVE";
static String DEFAULT_SUBCHUNK1_ID = "fmt ";
static String DEFAULT_SUBCHUNK2_ID = "data";
public WavData(){
this.put(WaveSection.CHUNK_ID, DEFAULT_CHUNK_ID);
this.put(WaveSection.FORMAT, DEFAULT_FORMAT);
this.put(WaveSection.SUBCHUNK1_ID, DEFAULT_SUBCHUNK1_ID);
this.put(WaveSection.SUBCHUNK1_SIZE, DEFAULT_SUBCHUNK1_SIZE);
this.put(WaveSection.AUDIO_FORMAT, DEFAULT_AUDIO_FORMAT);
this.put(WaveSection.BLOCK_ALIGN, DEFAULT_BLOCK_ALIGN);
this.put(WaveSection.SUBCHUNK2_ID, DEFAULT_SUBCHUNK2_ID);
this.put(WaveSection.CHUNK_SIZE, 0);
this.put(WaveSection.SUBCHUNK2_SIZE, 0);
this.put(WaveSection.BYTE_RATE, 0);
}
public void put(WaveSection waveSection, String value){
byte[] bytes = value.getBytes();
this.put(waveSection, bytes);
}
public void put(WaveSection waveSection, int value) {
byte[] bytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(value).array();
this.put(waveSection, bytes);
}
public void put(WaveSection waveSection, short value) {
byte[] bytes = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN).putShort(value).array();
this.put(waveSection, bytes);
}
public byte[] getBytes(WaveSection waveSection) {
return this.get(waveSection);
}
public String getString(WaveSection waveSection) {
byte[] bytes = this.get(waveSection);
return new String(bytes);
}
public int getInt(WaveSection waveSection) {
byte[] bytes = this.get(waveSection);
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
}
public short getShort(WaveSection waveSection) {
byte[] bytes = this.get(waveSection);
return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
}
public void printByteInfo() {
for (WaveSection waveSection : WaveSection.values()) {
if (waveSection.numBytes == 4
&& waveSection.endian == ByteOrder.BIG_ENDIAN) {
System.out.println("SECTION:" + waveSection + ":STRING:"
+ this.getString(waveSection));
} else if (waveSection.numBytes == 4
&& waveSection.endian == ByteOrder.LITTLE_ENDIAN) {
System.out.println("SECTION:" + waveSection + ":INTEGER:"
+ this.getInt(waveSection));
} else if (waveSection.numBytes == 2
&& waveSection.endian == ByteOrder.LITTLE_ENDIAN) {
System.out.println("SECTION:" + waveSection + ":SHORT:"
+ this.getShort(waveSection));
} else {
// Data Section
}
}
}
public void read(String inputPath) throws Exception {
// Analyze redundant info
int dataSize = (int) new File(inputPath).length() - HEADER_SIZE;
WaveSection.DATA.numBytes = dataSize; // Can't have two threads using this at the same time
// Read from File
DataInputStream inFile = new DataInputStream(new FileInputStream(inputPath));
for (WaveSection waveSection : WaveSection.values()) {
byte[] readBytes = new byte[waveSection.numBytes];
for (int i = 0; i < waveSection.numBytes; i++) {
readBytes[i] = inFile.readByte();
}
this.put(waveSection, readBytes);
}
inFile.close();
}
public void write(String outputPath) throws Exception {
// Analyze redundant info
int dataSize = this.get(WaveSection.DATA).length;
this.put(WaveSection.CHUNK_SIZE, dataSize+36);
this.put(WaveSection.SUBCHUNK2_SIZE, dataSize);
int byteRate = this.getInt(WaveSection.SAMPLE_RATE)*this.getShort(WaveSection.BLOCK_ALIGN);
this.put(WaveSection.BYTE_RATE, byteRate);
// Write to File
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(outputPath));
for (WaveSection waveSection : WaveSection.values()) {
dataOutputStream.write(this.getBytes(waveSection));
}
dataOutputStream.close();
}
public AudioFormat createAudioFormat() {
boolean audioSignedSamples = true; // Samples are signed
boolean audioBigEndian = false;
float sampleRate = (float) this.getInt(WaveSection.SAMPLE_RATE);
int bitsPerSample = (int) this.getShort(WaveSection.BITS_PER_SAMPLE);
int numChannels = (int) this.getShort(WaveSection.NUM_CHANNELS);
return new AudioFormat(sampleRate, bitsPerSample,
numChannels, audioSignedSamples, audioBigEndian);
}
}
public static class WavAudioPlayer {
WavData waveData = new WavData();
public WavAudioPlayer(WavData waveData){
this.waveData = waveData;
}
public void playAudio() throws Exception {
byte[] data = waveData.getBytes(WaveSection.DATA);
// Create an audio input stream from byte array
AudioFormat audioFormat = waveData.createAudioFormat();
InputStream byteArrayInputStream = new ByteArrayInputStream(data);
AudioInputStream audioInputStream = new AudioInputStream(byteArrayInputStream,
audioFormat, data.length / audioFormat.getFrameSize());
// Write audio input stream to speaker source data line
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class,
audioFormat);
SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
// Loop through input stream to write to source data line
byte[] tempBuffer = new byte[10000];
int cnt;
while ((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1) {
sourceDataLine.write(tempBuffer, 0, cnt);
}
// Cleanup
sourceDataLine.drain();
sourceDataLine.close();
byteArrayInputStream.close();
}
}
public static class WavAudioRecorder implements Runnable {
WavData waveData = new WavData();
boolean recording = true;
Thread runningThread;
ByteArrayOutputStream byteArrayOutputStream;
public WavAudioRecorder(WavData waveData){
this.waveData = waveData;
}
public void startRecording(){
this.recording = true;
this.runningThread = new Thread(this);
runningThread.start();
}
public WavData stopRecording() throws Exception{
this.recording = false;
runningThread.stop();
waveData.put(WaveSection.DATA, byteArrayOutputStream.toByteArray());
return waveData;
}
public void run() {
try {
// Create an audio output stream for byte array
byteArrayOutputStream = new ByteArrayOutputStream();
// Write audio input stream to speaker source data line
AudioFormat audioFormat = waveData.createAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
TargetDataLine targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
targetDataLine.open(audioFormat);
targetDataLine.start();
// Loop through target data line to write to output stream
int numBytesRead;
byte[] data = new byte[targetDataLine.getBufferSize() / 5];
while(recording) {
numBytesRead = targetDataLine.read(data, 0, data.length);
byteArrayOutputStream.write(data, 0, numBytesRead);
}
// Cleanup
targetDataLine.stop();
targetDataLine.close();
byteArrayOutputStream.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
Convert file to byte array
fileToByteArray("C:\..\my.mp3");
`public static byte[] fileToByteArray(String name){
Path path = Paths.get(name);
try {
return Files.readAllBytes(path);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}`