美文网首页
声波通讯、配网、声波互动(一)

声波通讯、配网、声波互动(一)

作者: 唔笛plk | 来源:发表于2018-10-30 15:57 被阅读0次

    一、什么是声波通讯

    声波通信的原理其实比较简单,主要是用单频率声音信号对数据进行编码,然后播放这些单频率声音,接收方在收到声音后, 识别出频率,然后根据频率解码出数据。比如:我们可以将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进行扩充。实现中英文字符传输

    二、应用场景

    支付宝声波支付、微信加好友、附近建群、基于超声波的电视/视频互动,基于超声波的电视/视频互动、歌曲识别,摇一摇;缺点也是相当明显,参考资料少,技术不开源,传输距离短,数据传输安全性低;

    三、原理剖析

    声波通讯.png

    1.数据存储

    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

    相关文章

      网友评论

          本文标题:声波通讯、配网、声波互动(一)

          本文链接:https://www.haomeiwen.com/subject/utugtqtx.html