当您更改一位时,强烈更改WAV文件。隐写术
问题描述:
encode_text()函数执行LSB编码方法。首先记录消息长度,然后是消息本身。当您更改一位时,强烈更改WAV文件。隐写术
numberOfBits为写入消息分配的位数。写一个字节或两个或三个中的一位......
使用备案一个位编码的这种方法,声音的变化不应该是明显的耳朵。以及图像在眼睛中的变化。但这种情况并非如此。由于某种原因,在声音的开头更改为“噪音”。这不应该。
阅读()和保存() OK,如果你读的数据文件,然后刻录没有变化。
的问题是,什么是错的encode_text()功能。或者,也许我做错了什么?
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);
}
}
答
您正在修改每个字节的最低有效位。因此,无论您的声音文件中一个样本有多少位,您只会引入~48dB的满刻度噪音。这在沉默中显然是可听见的,我想这就是“在声音开始时”的意思。
你大概意思修改只有平时的16位这将使再满刻度〜-96dB只有噪声最小显著位。
如果modifyied只有LSB的16个样本,那么你的噪音会更柔和得多。但请注意,它仍然可以在大音量播放的良好设备上的声音文件的无声部分中被正常听力人所察觉。任何人在使用音频编辑器或类似软件查看声音文件时都会很明显。对于有效的隐写术,你必须(至少)使你的编码适应时间相关的声级。