一、什么是声波通讯
声波通信的原理其实比较简单,主要是用单频率声音信号对数据进行编码,然后播放这些单频率声音,接收方在收到声音后, 识别出频率,然后根据频率解码出数据。比如:我们可以将1500HZ的正弦波对应数字1,1600HZ的正弦波对应数字2,1700HZ 的正弦波对应数字3。那么数字串3123就对应4段正弦波,规定每段正弦波持续100ms,则3123对应400毫秒的声音段。接收方 录制声音,对收到的声音进行解析,识别出1700HZ,1500HZ,1600HZ,1700HZ四段正弦波频率,然后查找码本,解码出的数字 就是3123。本文是参考https://blog.csdn.net/zhaokaiqiang1992/article/details/41149621开源Demo进行扩充。实现中英文字符传输
二、应用场景
支付宝声波支付、微信加好友、附近建群、基于超声波的电视/视频互动,基于超声波的电视/视频互动、歌曲识别,摇一摇;缺点也是相当明显,参考资料少,技术不开源,传输距离短,数据传输安全性低;
三、原理剖析
声波通讯.png1.数据存储
package home.gz.com.sinvoicetool;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
/**
* @author Wudi
* @date 2018/10/17
* 数据的存储采用阻塞队列(BlockingQueue)
*/
public class SinBuffer {
private final static String TAG = "SinBuffer";
/**
* 默认缓冲区大小 大于最100ms 内的采样率
*/
public final static int DEFAULT_BUFFER_SIZE = 4096;
/**
* 默认缓冲区数量
*/
public final static int DEFAULT_BUFFER_COUNT = 3;
/**
* 生产队列
*/
private BlockingQueue<SinBufferData> sinBufferDataProducer;
/**
* 消费队列
*/
private BlockingQueue<SinBufferData> sinBufferDataConsumer;
// 静态空缓冲区
private static SinBuffer.SinBufferData sEmptyBuffer = new SinBuffer.SinBufferData(0);
public static SinBuffer.SinBufferData getsEmptyBuffer() {
return sEmptyBuffer;
}
public SinBuffer() {
this(DEFAULT_BUFFER_COUNT, DEFAULT_BUFFER_SIZE);
}
public SinBuffer(int mSinBufferCount, int mSinBufferSize) {
sinBufferDataProducer = new LinkedBlockingDeque<>(mSinBufferCount);
// 需要结束缓存,所以数量比生产者要+1
sinBufferDataConsumer = new LinkedBlockingDeque<>(mSinBufferCount + 1);
// 初始化生产者对立
for (int i = 0; i < mSinBufferCount; i++) {
try {
sinBufferDataProducer.put(new SinBufferData(mSinBufferSize));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 重置数据
*/
public void resetSinBuffer() {
// 将生产者的空头结点剔除
int size = sinBufferDataProducer.size();
for (int i = 0; i < size; i++) {
SinBufferData sinBufferData = sinBufferDataProducer.peek();
if (null == sinBufferData || null == sinBufferData.shortData) {
sinBufferDataProducer.poll();
}
}
// 将消费者中的非空数据添加到生产者当中
size = sinBufferDataConsumer.size();
for (int i = 0; i < size; i++) {
SinBufferData sinBufferData = sinBufferDataConsumer.poll();
if (null != sinBufferData && null != sinBufferData.shortData) {
sinBufferDataProducer.add(sinBufferData);
}
}
}
/**
* 获取生产者头节点
*/
public SinBuffer.SinBufferData getFirstProducerNode() {
return getBufferDataImpl(sinBufferDataProducer);
}
/**
* 从消费者队列获取
*
* @return
*/
public SinBuffer.SinBufferData getSinBufferConsumeData() {
return getBufferDataImpl(sinBufferDataConsumer);
}
/**
* 加入到消费者和队列
* @param sinBufferData
* @return
*/
public boolean putSinBufferConsumeData(SinBufferData sinBufferData) {
return putBufferDataImpl(sinBufferDataConsumer, sinBufferData);
}
/**
* 写入数据到生产者队列
*
* @param sinBufferData
* @return
*/
public boolean putBufferData(SinBufferData sinBufferData) {
return putBufferDataImpl(sinBufferDataProducer, sinBufferData);
}
/**
* 读取缓存数据,FIFO
*
* @param dataBlockingQueue
* @return
*/
private SinBufferData getBufferDataImpl(BlockingQueue<SinBufferData> dataBlockingQueue) {
if (dataBlockingQueue != null) {
try {
return dataBlockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 写入缓存数据
*
* @param dataBlockingQueue
* @param sinBufferData
* @return
*/
private boolean putBufferDataImpl(BlockingQueue<SinBufferData> dataBlockingQueue, SinBufferData sinBufferData) {
if (dataBlockingQueue != null && sinBufferData != null) {
try {
dataBlockingQueue.put(sinBufferData);
return true;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return false;
}
public static class SinBufferData {
/**
* 数据存储采用16喂short
*/
public byte[] shortData;
/**
* 填充体积大小
*/
private int bufferSize;
/**
* 最大填充提及
*/
private int maxBufferSize;
/**
* 静态的空缓冲区
*/
public SinBufferData(int mMaxBufferSize) {
this.maxBufferSize = mMaxBufferSize;
bufferSize = 0;
if (mMaxBufferSize > 0) {
shortData = new byte[mMaxBufferSize];
} else {
shortData = null;
}
}
public byte[] getShortData() {
return shortData;
}
public void setShortData(byte[] shortData) {
this.shortData = shortData;
}
public int getBufferSize() {
return bufferSize;
}
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
public int getMaxBufferSize() {
return maxBufferSize;
}
public void setMaxBufferSize(int maxBufferSize) {
this.maxBufferSize = maxBufferSize;
shortData = new byte[maxBufferSize];
}
}
}
2.通讯工具类
package home.gz.com.sinvoicetool;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* @author Wudi
* @date 2018/10/16
*/
public class CommonUtil {
private static final String TAG = "CommonUtil";
/**
* ASCII 最小和最大字符限定
*/
private final static int MIN_ASCII = 32, MAX_ASCII = 127;
/**
* 默认采样率
*/
public final static int DEFAULT_SAMPLE_RATE = 44100;
/**
* 默认每个字发送的正弦波持续时间
*/
public final static int DEFAULT_DURATION = 100;
/**
* 字符映射频率,采用二进制传输 8820代表符号间隔, 5512:代表1与开始标志 4409 :代表0 3150 :代表结束标志
* 5 8 10 14
*/
public static final int[] CODE_FREQUENCY = new int[]{8820, 5512, 4409, 3150};
private static List<Integer> mCodes = new ArrayList<Integer>();
/**
* 将字符串转化为二进制流
*
* @param text
* @return
*/
public static List<Integer> convertStringToBit(String text) {
// 存放二进制流
mCodes.clear();
if (!TextUtils.isEmpty(text)) {
char[] chars = text.toCharArray();
for (int i = 0; i < chars.length; i++) {
addBitString(Integer.toBinaryString(chars[i]));
mCodes.add(3);
Log.d(TAG, String.valueOf(mCodes));
}
}
return mCodes;
}
private static void addBitString(String bitString) {
Log.d(TAG, "二进制编码:" + bitString);
for (int j = 0; j < bitString.length(); j++) {
if (bitString.charAt(j) == '1') {
mCodes.add(1);
} else {
mCodes.add(2);
}
mCodes.add(0);
}
}
/**
* 将二进制转化为字符串
*
* @param bitStream
* @return
*/
public static String convertBitToString(String bitStream) {
String text = "";
if (!TextUtils.isEmpty(bitStream)) {
Log.d(TAG, "二进制解码: " + bitStream);
int[] temp = bitToIntArray(bitStream);
int sum = 0;
for (int i = 0; i < temp.length; i++) {
sum += temp[temp.length - 1 - i] << i;
}
text = String.valueOf((char) sum);
}
return text;
}
/**
* 将二进制字符串转换成int数组
*
* @param binStr
* @return
*/
public static int[] bitToIntArray(String binStr) {
char[] temp = binStr.toCharArray();
int[] result = new int[temp.length];
for (int i = 0; i < temp.length; i++) {
result[i] = temp[i] - 48;
}
return result;
}
}
四、参考资料
声波通讯参考:
https://www.jianshu.com/p/56bfa1668568
https://blog.csdn.net/zhaokaiqiang1992/article/details/41149621
PCM数据参考:
https://www.cnblogs.com/TianFang/p/7894630.html
AudiotTrack与AudioRecord参考:
https://www.cnblogs.com/innost/archive/2011/01/09/1931457.html
https://www.cnblogs.com/renhui/p/7457321.html
二进制编码
https://www.jb51.net/article/114746.htm
网友评论