最近我在一个队伍里参加SOLVEFOR TOMORROM探知未来2014年全国青年科普创新实践暨作品大赛。从广州赛区毫无悬念地进入到北京赛区决赛,然后决赛的规则更加恶心,这个时候队长提出要使用声音传递信号,网上是有个开源的软件:https://github.com/JesseGu/SinVoice
队长是我们专业里的大牛,把这个开源软件改写了,但是发现声音传送的的速度无法满足要求,于是转而研究音频传输文件。好了,我说了队长是大牛,可是在研究的时候遇到一个问题,就是要将声音信号采集,然后将声音的时域信号转化为频域信号,这个时候就要用到FFT(快速傅里叶变换),队长见我一直在打酱油,就把这个任务交给了我。
我当时心里就一个靠,哥去年的高数都差点挂了,傅里叶我去你大爷的,爷们儿不会啊。于是乎便上百度,找到的FFT算法采样点都特么要是2的整数次方,队长给我的却是4410个采样点,这尼玛我在百度里一番捣腾,要么就是写得太渣,要我自己去研究高数还不如弄死我。最后翻墙谷歌找到一个不错的FFT的Java算法,之后对其进行了修改。
好的,现在来谈谈,我解决的问题。我要在0.1秒钟里用声音传输2个字节的数据,首先要对这段两个字节进行编码,例如我要传送的是0x31,0xA7,首先将其编码,片段如下:
private final static double sampling_rate = 4410;
private static double[] encode = new double[4410]; //定义编码数组
private static boolean[] single = new boolean[16]; //定义标记数组
private static double[] t = new double[4410];
public static double[] t_encode(){ //初始化t
for(int i = 0; i < t.length; i++){
t[i] = (double) (1.0/sampling_rate*i);
}
return t;
}
public static boolean[] single_encode(byte[] message){ //编码single
String ZERO="00000000";
String[] s_ = new String[2];
for(int i = 0; i < message.length; i++){
String s = Integer.toBinaryString(message[i]);
if(s.length() > 8){
s = s.substring(s.length() - 8);
}
else if(s.length() < 8){
s = ZERO.substring(s.length()) + s;
}
// System.out.println(s);
s_[i] = s;
}
char[] c = new char[16];
String se = s_[0] + s_[1];
c = se.toCharArray();
// System.out.println(c);
for(int i = 0; i < 16; i++){
if( c[i] == '1'){
single[i] = true;
}
else{
single[i] = false;
}
}
return single;
}
public static double[] encode_(boolean[] single){ //初始化encode
for(int i = 0; i < encode.length; i++){
for(int j = 0; j < single.length; j++){
if(!single[j]) continue;
int rate = 4000 + 1000 * j;
encode[i] += 8 * Math.sin(2 * Math.PI * rate * t[i]);
}
}
return encode;
}
上面的三个方法完成了对byte数组的编码,然后再通过声音发射出去,然后重点来了,下一个问题就是对采集的声音信号经过FFT处理,将频率散布在0 --