encode_text() function implements the LSB coding method. First recorded message length, then the message itself.
numberOfBits number of bits which are allocated for writing messages. Write to one bit of a byte or two or three ...
This method of encoding using one bit for the record, the sound changes should not be noticeable to the ear. As well as changes in the image to the eye. But this is not the case. For some reason, at the beginning of the sound is changed to "noise". That should not be.
read() and save() OK, if you read the data file and then burn them there are no changes.
The question is what is wrong with encode_text() function. Or maybe I did something wrong?
import java.io.*;
import java.util.Arrays;
public class wavIO
{
private String myPath;
private long myChunkSize;
private long mySubChunk1Size;
private int myFormat;
private long myChannels;
private long mySampleRate;
private long myByteRate;
private int myBlockAlign;
private int myBitsPerSample;
private long myDataSize;
public byte[] myData;
public String getPath()
{
return myPath;
}
public void setPath(String newPath)
{
myPath = newPath;
}
public wavIO()
{
myPath = "";
}
public wavIO(String tmpPath)
{
myPath = tmpPath;
}
// read a wav file into this class
public boolean read()
{
DataInputStream inFile = null;
myData = null;
byte[] tmpLong = new byte[4];
byte[] tmpInt = new byte[2];
try
{
inFile = new DataInputStream(new FileInputStream(myPath));
//System.out.println("Reading wav file...\n"); // for debugging only
String chunkID = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
inFile.read(tmpLong); // read the ChunkSize
myChunkSize = byteArrayToLong(tmpLong);
String format = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
// print what we've read so far
//System.out.println("chunkID:" + chunkID + " chunk1Size:" + myChunkSize + " format:" + format); // for debugging only
String subChunk1ID = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
inFile.read(tmpLong); // read the SubChunk1Size
mySubChunk1Size = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the audio format. This should be 1 for PCM
myFormat = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the # of channels (1 or 2)
myChannels = byteArrayToInt(tmpInt);
inFile.read(tmpLong); // read the samplerate
mySampleRate = byteArrayToLong(tmpLong);
inFile.read(tmpLong); // read the byterate
myByteRate = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the blockalign
myBlockAlign = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the bitspersample
myBitsPerSample = byteArrayToInt(tmpInt);
// print what we've read so far
//System.out.println("SubChunk1ID:" + subChunk1ID + " SubChunk1Size:" + mySubChunk1Size + " AudioFormat:" + myFormat + " Channels:" + myChannels + " SampleRate:" + mySampleRate);
// read the data chunk header - reading this IS necessary, because not all wav files will have the data chunk here - for now, we're just assuming that the data chunk is here
String dataChunkID = "" + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte() + (char)inFile.readByte();
inFile.read(tmpLong); // read the size of the data
myDataSize = byteArrayToLong(tmpLong);
// read the data chunk
myData = new byte[(int)myDataSize];
inFile.read(myData);
// close the input stream
inFile.close();
}
catch(Exception e)
{
return false;
}
return true; // this should probably be something more descriptive
}
// write out the wav file
public boolean save(String outputPath)
{
try
{
//DataOutputStream outFile = new DataOutputStream(new FileOutputStream(myPath));
DataOutputStream outFile = new DataOutputStream(new FileOutputStream(outputPath));
// write the wav file per the wav file format
outFile.writeBytes("RIFF"); // 00 - RIFF
outFile.write(intToByteArray((int)myChunkSize), 0, 4); // 04 - how big is the rest of this file?
outFile.writeBytes("WAVE"); // 08 - WAVE
outFile.writeBytes("fmt "); // 12 - fmt
outFile.write(intToByteArray((int)mySubChunk1Size), 0, 4); // 16 - size of this chunk
outFile.write(shortToByteArray((short)myFormat), 0, 2); // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation
outFile.write(shortToByteArray((short)myChannels), 0, 2); // 22 - mono or stereo? 1 or 2? (or 5 or ???)
outFile.write(intToByteArray((int)mySampleRate), 0, 4); // 24 - samples per second (numbers per second)
outFile.write(intToByteArray((int)myByteRate), 0, 4); // 28 - bytes per second
outFile.write(shortToByteArray((short)myBlockAlign), 0, 2); // 32 - # of bytes in one sample, for all channels
outFile.write(shortToByteArray((short)myBitsPerSample), 0, 2); // 34 - how many bits in a sample(number)? usually 16 or 24
outFile.writeBytes("data"); // 36 - data
outFile.write(intToByteArray((int)myDataSize), 0, 4); // 40 - how big is this data chunk
outFile.write(myData); // 44 - the actual data itself - just a long string of numbers
}
catch(Exception e)
{
System.out.println(e.getMessage());
return false;
}
return true;
}
// return a printable summary of the wav file
public String getSummary()
{
//String newline = System.getProperty("line.separator");
String newline = "<br>";
String summary = "<html>Format: " + myFormat + newline + "Channels: " + myChannels + newline + "SampleRate: " + mySampleRate + newline + "ByteRate: " + myByteRate + newline + "BlockAlign: " + myBlockAlign + newline + "BitsPerSample: " + myBitsPerSample + newline + "DataSize: " + myDataSize + "</html>";
return summary;
}
// ===========================
// CONVERT BYTES TO JAVA TYPES
// ===========================
// these two routines convert a byte array to a unsigned short
public static int byteArrayToInt(byte[] b)
{
int start = 0;
int low = b[start] & 0xff;
int high = b[start+1] & 0xff;
return (int)( high << 8 | low );
}
// these two routines convert a byte array to an unsigned integer
public static long byteArrayToLong(byte[] b)
{
int start = 0;
int i = 0;
int len = 4;
int cnt = 0;
byte[] tmp = new byte[len];
for (i = start; i < (start + len); i++)
{
tmp[cnt] = b[i];
cnt++;
}
long accum = 0;
i = 0;
for ( int shiftBy = 0; shiftBy < 32; shiftBy += 8 )
{
accum |= ( (long)( tmp[i] & 0xff ) ) << shiftBy;
i++;
}
return accum;
}
// ===========================
// CONVERT JAVA TYPES TO BYTES
// ===========================
// returns a byte array of length 4
private static byte[] intToByteArray(int i)
{
byte[] b = new byte[4];
b[0] = (byte) (i & 0x00FF);
b[1] = (byte) ((i >> 8) & 0x000000FF);
b[2] = (byte) ((i >> 16) & 0x000000FF);
b[3] = (byte) ((i >> 24) & 0x000000FF);
return b;
}
// convert a short to a byte array
public static byte[] shortToByteArray(short data)
{
return new byte[]{(byte)(data & 0xff),(byte)((data >>> 8) & 0xff)};
}
public void encode(String text, int numberOfBits)
{
byte[] byteMessage = text.getBytes();
byte[] messageLength = bit_conversion(byteMessage.length);
encodeText(messageLength, myData, 0, numberOfBits);
encodeText(byteMessage, myData, 32, numberOfBits);
}
private void encodeText(byte[] addition, byte[] byteDataInputWav, int offset, int numberOfBits)
{
if(addition.length + offset > byteDataInputWav.length)
{
System.out.println("File not long enough!");
}
else
{
for(int i=0; i<addition.length; ++i)
{
int add = addition[i];
for(int bit=7; bit>=0; --bit, ++offset)
{
int b = (add >>> bit) & 1;
byteDataInputWav[offset] = (byte)((byteDataInputWav[offset] & numberOfBits) | b );
}
}
}
}
private byte[] bit_conversion(int i)
{
byte byte3 = (byte)((i & 0xFF000000) >>> 24);
byte byte2 = (byte)((i & 0x00FF0000) >>> 16);
byte byte1 = (byte)((i & 0x0000FF00) >>> 8 );
byte byte0 = (byte)((i & 0x000000FF) );
return(new byte[]{byte3,byte2,byte1,byte0});
}
public String decode(String inputPath)
{
byte[] byteDataOutputWav = myData;
int length = 0;
int offset2 = 32;
for(int i=0; i<32; ++i)
{
length = (length << 1) | (byteDataOutputWav[i] & 1);
}
byte[] result = new byte[length];
for(int b=0; b<result.length; ++b )
{
for(int i=0; i<8; ++i, ++offset2)
{
result[b] = (byte)((result[b] << 1) | (byteDataOutputWav[offset2] & 1));
}
}
return new String(result);
}
}
You're modifying the least significant bit of every byte. So you are introducing noise of only ~ -48dB re Full Scale, regardless of how many bits one sample has in your sound file. This would be clearly Audible in silence, which I guess is what you mean with "At the beginning of the sound".
You probably meant to modify only the least significant bit of the usual 16 bit which would make only noise of ~ -96dB re Full Scale.
If you modifyied only the LSB of 16 bit samples, then your noise would be much much softer. But be aware that it would still be perceptible to normal hearing people in silent parts of the sound file on good equipment at loud playback volumes. It would also be fairly obvious to anyone looking at your sound file with an audio editor or similar. For effective steganography, you would have to (at least) adapt your coding to the time-dependent sound levels.
Related
I am trying to add data to the zip file header field. Specifically into the extra field specified in the zip header using java. Is it possible ?
public class test {
public static void main(String[] args) {
String path = "finalName.zip";
RandomAccessFile file = null;
try {
file = new RandomAccessFile(path, "rw");
byte[] buf = new byte[(int) file.length() / 1024];
file.read(buf);
byte[] newHeader = "NEW DATA".getBytes();
System.out.println("Original zip : " + bytesToHex(buf));
System.out.println("newHeader : " + bytesToHex(newHeader));
int length = newHeader.length;
buf[28] = (byte) length;
byte[] firstPart = Arrays.copyOf(buf, 30);
byte[] endPart = Arrays.copyOfRange(buf, 30, buf.length );
System.out.println("firstPart zip original : " + bytesToHex(firstPart));
System.out.println("endPart zip original : " + bytesToHex(endPart));
byte[] firstPartAndHeader = new byte[30 + length];
System.arraycopy(firstPart, 0, firstPartAndHeader, 0, firstPart.length);
System.arraycopy(newHeader, 0, firstPartAndHeader, firstPart.length, newHeader.length);
System.out.println("firstPartAndHeader zip original : " + bytesToHex(firstPartAndHeader));
byte[] combined = new byte[firstPartAndHeader.length + endPart.length];
System.arraycopy(firstPartAndHeader, 0, combined, 0, firstPartAndHeader.length);
System.arraycopy(endPart, 0, combined, firstPartAndHeader.length, endPart.length);
System.out.println("combined zip original : " + bytesToHex(combined));
file.seek(0);
file.write(combined);
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for ( int j = 0; j < bytes.length; j++ ) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}
The way I did is corrupting the file. I have added the length of the extra header in byte 28 and the extra header from byte 30. Is there something more that needs to be done ?
I was writing some code to simulate the proof-of-work done by the Bitcoin network while generating blocks when I suddenly became curious: How can you create a datum that contains it's own hash?
Just for fun, I wrote a program that attempts to create data that contains it's own hash. 4 random bytes are generated, then a nonce is added at the end and the whole value is hashed with CRC32. The nonce in incremented and the process repeats until the program finds a hash that matches the original 4 bytes.
Note: The nonce can increase indefinitely.
Here's an example of the output after approx 1,980,000,000 attempts:
Found a match!
Data: 7a73a2d4ab833876
Original hash: 7a73a2d4 new hash: 7a73a2d4
Is there a potential use for this?
package selfhash;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
/**
*
* #author dylan
*/
public class SelfHash {
static byte[] data;
static byte[] hash = new byte[4];
public static void main(String[] args) {
// TODO code application logic here
SecureRandom random = new SecureRandom();
random.nextBytes(hash);
data = new byte[hash.length + 1];
System.arraycopy(hash, 0, data, 0, hash.length);
long c = 0;
while (true) {
recalculateData();
byte[] dataHash = crc32AsByteArray(data);
if (c % 10000000 == 0) {
System.out.println("Calculated " + c + " hashes");
System.out.println("Data: " + byteArrayToHex(data));
System.out.println("Original hash: " + byteArrayToHex(hash) + " new hash: " + byteArrayToHex(dataHash));
}
if (Arrays.equals(hash, dataHash)) {
System.out.println("Found a match!");
System.out.println("Data: " + byteArrayToHex(data));
System.out.println("Original hash: " + byteArrayToHex(hash) + " new hash: " + byteArrayToHex(dataHash));
break;
}
c++;
}
}
public static void recalculateData() {
int position = hash.length;
while (true) {
int valueAtPosition = unsignedToBytes(data[position]);
if (valueAtPosition == 255) {
//increase size of data
if (position == data.length-1) {
byte[] newData = new byte[data.length + 1];
System.arraycopy(data, 0, newData, 0, data.length);
data = newData;
}
data[position] = (byte) (0);
position++;
} else {
data[position] = (byte) (valueAtPosition + 1);
break;
}
}
}
public static byte[] hexToByteArray(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i + 1), 16));
}
return data;
}
private static final char[] BYTE2HEX = ("000102030405060708090A0B0C0D0E0F"
+ "101112131415161718191A1B1C1D1E1F"
+ "202122232425262728292A2B2C2D2E2F"
+ "303132333435363738393A3B3C3D3E3F"
+ "404142434445464748494A4B4C4D4E4F"
+ "505152535455565758595A5B5C5D5E5F"
+ "606162636465666768696A6B6C6D6E6F"
+ "707172737475767778797A7B7C7D7E7F"
+ "808182838485868788898A8B8C8D8E8F"
+ "909192939495969798999A9B9C9D9E9F"
+ "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
+ "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
+ "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
+ "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
+ "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
+ "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toLowerCase().toCharArray();
;
public static String byteArrayToHex(byte[] bytes) {
final int len = bytes.length;
final char[] chars = new char[len << 1];
int hexIndex;
int idx = 0;
int ofs = 0;
while (ofs < len) {
hexIndex = (bytes[ofs++] & 0xFF) << 1;
chars[idx++] = BYTE2HEX[hexIndex++];
chars[idx++] = BYTE2HEX[hexIndex];
}
return new String(chars);
}
public static String sha256AsHexString(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return byteArrayToHex(digest.digest(bytes));
} catch (Exception e) {
throw new Error(e);
}
}
public static byte[] sha256AsByteArray(byte[] bytes) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return digest.digest(bytes);
} catch (Exception e) {
throw new Error(e);
}
}
public static byte[] crc32AsByteArray(byte[] bytes) {
Checksum checksum = new CRC32();
checksum.update(bytes, 0, bytes.length);
long value = checksum.getValue();
byte[] resultExcess = ByteBuffer.allocate(8).putLong(value).array();
byte[] result = new byte[4];
System.arraycopy(resultExcess, 4, result, 0, 4);
return result;
}
public static int unsignedToBytes(byte b) {
return b & 0xFF;
}
}
I can't think of a use.
CRC's are linear, and so the equations can be solved very fast to get the second four bytes. You don't need two billion trials to find it. See spoof.c.
Read an audio file, for example a wav file, then get its time length. Then get its bytes value from second by second, or by half a second. Like I have a 20 seconds wav and i will output its bytes[] by the time I specified. Because getting the bytes for all the length of the file takes very large of space.
This is me getting the bytes from the audio file, but i need just the bytes by its seconds. Any help?
FileInputStream s = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath()+"/audio_raw.wav");
BufferedInputStream b = new BufferedInputStream(s);
byte[] data = new byte[128];
while((bytes = b.read(data)) > 0)
{
for(int i = 0; i<bytes; i++)
{
unsigned = data[i] & 0xFF;
bw.write(unsigned+"+");
}
}
b.read(data);
b.close();
This is a function from a class that processes WAV files I use. You can see how it reads in the file and can extract some data about the WAV file. Perhaps it can assist you:
// read a wav file into this class
public boolean read()
{
DataInputStream inFile = null;
myData = null;
byte[] tmpLong = new byte[4];
byte[] tmpInt = new byte[2];
try
{
inFile = new DataInputStream(new FileInputStream(myPath));
//System.out.println("Reading wav file...\n"); // for debugging only
String chunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the ChunkSize
myChunkSize = byteArrayToLong(tmpLong);
String format = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
// print what we've read so far
//System.out.println("chunkID:" + chunkID + " chunk1Size:" + myChunkSize + " format:" + format); // for debugging only
String subChunk1ID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the SubChunk1Size
mySubChunk1Size = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the audio format. This should be 1 for PCM
myFormat = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the # of channels (1 or 2)
myChannels = byteArrayToInt(tmpInt);
inFile.read(tmpLong); // read the samplerate
mySampleRate = byteArrayToLong(tmpLong);
inFile.read(tmpLong); // read the byterate
myByteRate = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the blockalign
myBlockAlign = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the bitspersample
myBitsPerSample = byteArrayToInt(tmpInt);
// print what we've read so far
//System.out.println("SubChunk1ID:" + subChunk1ID + " SubChunk1Size:" + mySubChunk1Size + " AudioFormat:" + myFormat + " Channels:" + myChannels + " SampleRate:" + mySampleRate);
// read the data chunk header - reading this IS necessary, because not all wav files will have the data chunk here - for now, we're just assuming that the data chunk is here
String dataChunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the size of the data
myDataSize = byteArrayToLong(tmpLong);
// read the data chunk
myData = new byte[(int) myDataSize];
inFile.read(myData);
// close the input stream
inFile.close();
}
catch (Exception e)
{
return false;
}
return true; // this should probably be something more descriptive
}
I have a java client and a C server. I server wants to send a data packet to the client containing some information in a specific order as shown below:
char *buf = NULL;
if(!(buf = malloc(sizeof(char) * pkt_len)))
{
printf("Could not malloc\n");
return -1;
}
memcpy(buf, &pkt_type, 2);
memcpy(buf + 2, &pkt_len, 4);
memcpy(buf + 6, &more_to_come, 1);
memcpy(buf + 7, &fb_id, 8);
memcpy(buf + 15, &match, 4);
memcpy(buf + 19, el->name, name_len);
memcpy(buf + 19 + name_len, "\n\r", 2);
if(send(clientSock, buf, pkt_len, 0) < 0)
{
printf("Can not write to socket %d\n", clientSock);
return -1;
}
Ofcourse I have convereted all the shorts, integers and long integers to network bytes order before writing them to the buffer. The data is received as a string by the Java client. My problem is how to parse this string. For example, I would to know a way to read off the 2 bytes that indicate the pkt length and cast it to a short in host-byte-order. I am aware that Java provides a method to convert a string to an array of bytes. But what do I do after I have obtained the bytes array. Some code to perform this task would be appreciated
You mean something like this?:
char[] charArray = new char[2];
charArray[0] = "a".charAt(0);
charArray[1] = "b".charAt(0);
String string = new String(charArray);
I assume a char is one byte in length here.
You could use a DataInputStream. Depending on your data types, something like the following might get you started. Note the example uses ASCII as the character encoding and doesn't try to be efficient in any way.
package grimbo.test.bytes;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.IOException;
public class BytesTest {
public static void main(String[] args) throws IOException {
// start setup test data
byte[] msgStart = {
/*pkt_type*/0, 1,
/*pkt_len*/0, 0, 0, 1,
/*more_to_come*/1,
/*fb_id*/1, 2, 3, 4, 5, 6, 7, 8,
/*match*/2, 2, 2, 2 };
String name = "John Smith\n\r";
byte[] nameBytes = name.getBytes("ASCII");
byte[] msg = new byte[msgStart.length + nameBytes.length];
System.arraycopy(msgStart, 0, msg, 0, msgStart.length);
System.arraycopy(nameBytes, 0, msg, msgStart.length, nameBytes.length);
// end setup test data
DataInputStream in = new DataInputStream(new ByteArrayInputStream(msg));
new BytesTest().read(in);
}
void read(DataInputStream in) throws IOException {
// assuming pkt_type is an unsigned 2-byte value
int pkt_type = in.readUnsignedShort();
print(pkt_type);
// assuming pkt_len is an unsigned 4-byte value
// Java doesn't have those, so read a signed int and mask to a long
long pkt_len = in.readInt() & 0xFFFFFFFFL;
print(pkt_len);
// assuming vanilla byte is ok for this, but Java bytes are signed, not unsigned
byte more_to_come = in.readByte();
print(more_to_come);
// don't know the format of this, so left as bytes
byte[] fb_id = new byte[8];
in.readFully(fb_id);
print(fb_id);
// don't know the format of this, so left as bytes
byte[] match = new byte[4];
in.readFully(match);
print(match);
char[] nr = { '\n', '\r' };
byte[] name = readUntil(in, nr);
print(name);
System.out.println(">" + new String(name, "ASCII") + "<");
}
private byte[] readUntil(DataInputStream in, /* stop reading when these chars are found */char[] terminate)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int terminateIdx = 0;
int input = -1;
while ((input = in.read()) > -1) {
if (input == terminate[terminateIdx]) {
if (terminateIdx == (terminate.length - 1)) {
// we've found the termination sequence
byte[] buf = baos.toByteArray();
// - terminateIdx because we don't include the termination sequence
byte[] result = new byte[buf.length - terminateIdx];
System.arraycopy(buf, 0, result, 0, result.length);
return result;
}
terminateIdx++;
} else {
// no match, reset count
terminateIdx = 0;
}
baos.write(input);
}
return baos.toByteArray();
}
private void print(long l) {
System.out.println(l);
}
void print(byte[] bytes) {
for (int i = 0; i < bytes.length; i++) {
if (i > 0) {
System.out.print(",");
}
System.out.print(bytes[i]);
}
System.out.println();
}
}
And the output is:
1
1
1
1,2,3,4,5,6,7,8
2,2,2,2
74,111,104,110,32,83,109,105,116,104
>John Smith<
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I would like to write the Wav file from byte array and I also want to split channels from the input wav file
The Java Sound API shows that you can record music from a TargetDataLine and as an example shows the data being written to a byte array. But writing this byte array out into its own file is fairly useless since it is not in the WAV file format and cannot be played in other applications.
How do I write sound files using the javax.sound.sampled package?
I have used this in the past for going from Wav -> byte[] and byte[] -> Wav
package GlobalUtilities;
import java.applet.Applet;
import java.applet.AudioClip;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import javax.sound.sampled.*;
/**
* This class handles the reading, writing, and playing of wav files. It is
* also capable of converting the file to its raw byte [] form.
*
* based on code by Evan Merz modified by Dan Vargo
* #author dvargo
*/
public class Wav {
/*
WAV File Specification
FROM http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
The canonical WAVE format starts with the RIFF header:
0 4 ChunkID Contains the letters "RIFF" in ASCII form
(0x52494646 big-endian form).
4 4 ChunkSize 36 + SubChunk2Size, or more precisely:
4 + (8 + SubChunk1Size) + (8 + SubChunk2Size)
This is the size of the rest of the chunk
following this number. This is the size of the
entire file in bytes minus 8 bytes for the
two fields not included in this count:
ChunkID and ChunkSize.
8 4 Format Contains the letters "WAVE"
(0x57415645 big-endian form).
The "WAVE" format consists of two subchunks: "fmt " and "data":
The "fmt " subchunk describes the sound data's format:
12 4 Subchunk1ID Contains the letters "fmt "
(0x666d7420 big-endian form).
16 4 Subchunk1Size 16 for PCM. This is the size of the
rest of the Subchunk which follows this number.
20 2 AudioFormat PCM = 1 (i.e. Linear quantization)
Values other than 1 indicate some
form of compression.
22 2 NumChannels Mono = 1, Stereo = 2, etc.
24 4 SampleRate 8000, 44100, etc.
28 4 ByteRate == SampleRate * NumChannels * BitsPerSample/8
32 2 BlockAlign == NumChannels * BitsPerSample/8
The number of bytes for one sample including
all channels. I wonder what happens when
this number isn't an integer?
34 2 BitsPerSample 8 bits = 8, 16 bits = 16, etc.
The "data" subchunk contains the size of the data and the actual sound:
36 4 Subchunk2ID Contains the letters "data"
(0x64617461 big-endian form).
40 4 Subchunk2Size == NumSamples * NumChannels * BitsPerSample/8
This is the number of bytes in the data.
You can also think of this as the size
of the read of the subchunk following this
number.
44 * Data The actual sound data.
The thing that makes reading wav files tricky is that java has no unsigned types. This means that the
binary data can't just be read and cast appropriately. Also, we have to use larger types
than are normally necessary.
In many languages including java, an integer is represented by 4 bytes. The issue here is
that in most languages, integers can be signed or unsigned, and in wav files the integers
are unsigned. So, to make sure that we can store the proper values, we have to use longs
to hold integers, and integers to hold shorts.
Then, we have to convert back when we want to save our wav data.
It's complicated, but ultimately, it just results in a few extra functions at the bottom of
this file. Once you understand the issue, there is no reason to pay any more attention
to it.
ALSO:
This code won't read ALL wav files. This does not use to full specification. It just uses
a trimmed down version that most wav files adhere to.
*/
ByteArrayOutputStream byteArrayOutputStream;
AudioFormat audioFormat;
TargetDataLine targetDataLine;
AudioInputStream audioInputStream;
SourceDataLine sourceDataLine;
float frequency = 8000.0F; //8000,11025,16000,22050,44100
int samplesize = 16;
private String myPath;
private long myChunkSize;
private long mySubChunk1Size;
private int myFormat;
private long myChannels;
private long mySampleRate;
private long myByteRate;
private int myBlockAlign;
private int myBitsPerSample;
private long myDataSize;
// I made this public so that you can toss whatever you want in here
// maybe a recorded buffer, maybe just whatever you want
public byte[] myData;
public Wav()
{
myPath = "";
}
// constructor takes a wav path
public Wav(String tmpPath) {
myPath = tmpPath;
}
// get/set for the Path property
public String getPath()
{
return myPath;
}
public void setPath(String newPath)
{
myPath = newPath;
}
// read a wav file into this class
public boolean read() {
DataInputStream inFile = null;
myData = null;
byte[] tmpLong = new byte[4];
byte[] tmpInt = new byte[2];
try {
inFile = new DataInputStream(new FileInputStream(myPath));
//System.out.println("Reading wav file...\n"); // for debugging only
String chunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the ChunkSize
myChunkSize = byteArrayToLong(tmpLong);
String format = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
// print what we've read so far
//System.out.println("chunkID:" + chunkID + " chunk1Size:" + myChunkSize + " format:" + format); // for debugging only
String subChunk1ID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the SubChunk1Size
mySubChunk1Size = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the audio format. This should be 1 for PCM
myFormat = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the # of channels (1 or 2)
myChannels = byteArrayToInt(tmpInt);
inFile.read(tmpLong); // read the samplerate
mySampleRate = byteArrayToLong(tmpLong);
inFile.read(tmpLong); // read the byterate
myByteRate = byteArrayToLong(tmpLong);
inFile.read(tmpInt); // read the blockalign
myBlockAlign = byteArrayToInt(tmpInt);
inFile.read(tmpInt); // read the bitspersample
myBitsPerSample = byteArrayToInt(tmpInt);
// print what we've read so far
//System.out.println("SubChunk1ID:" + subChunk1ID + " SubChunk1Size:" + mySubChunk1Size + " AudioFormat:" + myFormat + " Channels:" + myChannels + " SampleRate:" + mySampleRate);
// read the data chunk header - reading this IS necessary, because not all wav files will have the data chunk here - for now, we're just assuming that the data chunk is here
String dataChunkID = "" + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte() + (char) inFile.readByte();
inFile.read(tmpLong); // read the size of the data
myDataSize = byteArrayToLong(tmpLong);
// read the data chunk
myData = new byte[(int) myDataSize];
inFile.read(myData);
// close the input stream
inFile.close();
} catch (Exception e) {
return false;
}
return true; // this should probably be something more descriptive
}
// write out the wav file
public boolean save() {
try {
DataOutputStream outFile = new DataOutputStream(new FileOutputStream(myPath + "temp"));
// write the wav file per the wav file format
outFile.writeBytes("RIFF"); // 00 - RIFF
outFile.write(intToByteArray((int) myChunkSize), 0, 4); // 04 - how big is the rest of this file?
outFile.writeBytes("WAVE"); // 08 - WAVE
outFile.writeBytes("fmt "); // 12 - fmt
outFile.write(intToByteArray((int) mySubChunk1Size), 0, 4); // 16 - size of this chunk
outFile.write(shortToByteArray((short) myFormat), 0, 2); // 20 - what is the audio format? 1 for PCM = Pulse Code Modulation
outFile.write(shortToByteArray((short) myChannels), 0, 2); // 22 - mono or stereo? 1 or 2? (or 5 or ???)
outFile.write(intToByteArray((int) mySampleRate), 0, 4); // 24 - samples per second (numbers per second)
outFile.write(intToByteArray((int) myByteRate), 0, 4); // 28 - bytes per second
outFile.write(shortToByteArray((short) myBlockAlign), 0, 2); // 32 - # of bytes in one sample, for all channels
outFile.write(shortToByteArray((short) myBitsPerSample), 0, 2); // 34 - how many bits in a sample(number)? usually 16 or 24
outFile.writeBytes("data"); // 36 - data
outFile.write(intToByteArray((int) myDataSize), 0, 4); // 40 - how big is this data chunk
outFile.write(myData); // 44 - the actual data itself - just a long string of numbers
} catch (Exception e) {
System.out.println(e.getMessage());
return false;
}
return true;
}
// return a printable summary of the wav file
public String getSummary() {
//String newline = System.getProperty("line.separator");
String newline = "";
String summary = "Format: " + myFormat + newline + "Channels: " + myChannels + newline + "SampleRate: " + mySampleRate + newline + "ByteRate: " + myByteRate + newline + "BlockAlign: " + myBlockAlign + newline + "BitsPerSample: " + myBitsPerSample + newline + "DataSize: " + myDataSize + "";
return summary;
}
public byte[] getBytes() {
read();
return myData;
}
/**
* Plays back audio stored in the byte array using an audio format given by
* freq, sample rate, ect.
* #param data The byte array to play
*/
public void playAudio(byte[] data) {
try {
byte audioData[] = data;
//Get an input stream on the byte array containing the data
InputStream byteArrayInputStream = new ByteArrayInputStream(audioData);
AudioFormat audioFormat = getAudioFormat();
audioInputStream = new AudioInputStream(byteArrayInputStream, audioFormat, audioData.length / audioFormat.getFrameSize());
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
//Create a thread to play back the data and start it running. It will run \
//until all the data has been played back.
Thread playThread = new Thread(new PlayThread());
playThread.start();
} catch (Exception e) {
System.out.println(e);
}
}
/**
* This method creates and returns an AudioFormat object for a given set
* of format parameters. If these parameters don't work well for
* you, try some of the other allowable parameter values, which
* are shown in comments following the declarations.
* #return
*/
private AudioFormat getAudioFormat() {
float sampleRate = frequency;
//8000,11025,16000,22050,44100
int sampleSizeInBits = samplesize;
//8,16
int channels = 1;
//1,2
boolean signed = true;
//true,false
boolean bigEndian = false;
//true,false
//return new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 8000.0f, 8, 1, 1,
//8000.0f, false );
return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
}
public void playWav(String filePath) {
try {
AudioClip clip = (AudioClip) Applet.newAudioClip(new File(filePath).toURI().toURL());
clip.play();
} catch (Exception e) {
Logger.getLogger(Wav.class.getName()).log(Level.SEVERE, null, e);
}
}
// ===========================
// CONVERT BYTES TO JAVA TYPES
// ===========================
// these two routines convert a byte array to a unsigned short
public static int byteArrayToInt(byte[] b) {
int start = 0;
int low = b[start] & 0xff;
int high = b[start + 1] & 0xff;
return (int) (high > 8) & 0x000000FF);
b[2] = (byte) ((i >> 16) & 0x000000FF);
b[3] = (byte) ((i >> 24) & 0x000000FF);
return b;
}
// convert a short to a byte array
public static byte[] shortToByteArray(short data) {
return new byte[]{(byte) (data & 0xff), (byte) ((data >>> 8) & 0xff)};
}
/**
* Inner class to play back the data that was saved
*/
class PlayThread extends Thread {
byte tempBuffer[] = new byte[10000];
public void run() {
try {
int cnt;
//Keep looping until the input
// read method returns -1 for
// empty stream.
while ((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1) {
if (cnt > 0) {
//Write data to the internal
// buffer of the data line
// where it will be delivered
// to the speaker.
sourceDataLine.write(tempBuffer, 0, cnt);
}
}
//Block and wait for internal
// buffer of the data line to
// empty.
sourceDataLine.drain();
sourceDataLine.close();
} catch (Exception e) {
System.out.println(e);
System.exit(0);
}
}
}
}
Writing file from InputStream to OutputStream by reading bytes:
File srcFile = new File("c:/src.wav");
File dstFile = new File("c:/dst.wav");
FileInputStream in = new FileInputStream(srcFile);
FileOutputStream out = new FileOutputStream(dstFile);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
And here are some already answered questions on splitting wav channels, may be of use:
How to split a Wav file into channels in java?
wav amplitude in java (stereo or more channels)