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

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

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

一、声波通讯编码

package home.gz.com.sinvoicetool;

import android.util.Log;

import java.util.List;

/**
 * @author pc
 * @date 2018/10/16
 */
public class SinEncoder {

    private final static String TAG = "SinEncoder";
    /**
     * 状态判断
     */
    private static final int STATE_START = 1;
    private static final int STATE_STOP = 2;
    private int mState;

    public SinEncoder() {
        mState = STATE_STOP;
    }

    public void stop() {
        if (STATE_START == mState) {
            mState = STATE_STOP;
        }
    }

    public void start(List<Integer> codeStream) {
        if (STATE_STOP == mState) {
            mState = STATE_START;
            for (int i = 0; i < codeStream.size(); i++) {
                // 根据正弦波公式进行转换
                if(mState==STATE_START) {
                    genSinEquation(CommonUtil.CODE_FREQUENCY[codeStream.get(i)]
                            , CommonUtil.DEFAULT_SAMPLE_RATE
                            , CommonUtil.DEFAULT_DURATION);
                }else {
                    Log.d(TAG,"force stop");
                }
            }
        }
    }

    /**
     * 每个采样点的存储方式,采用16为PCM存储
     */
    private final int MAX_SHORT = 32768;
    private final int MIN_SHORT = -32768;

    /**
     * @param frequency  生成正弦波的频率
     * @param sampleRate 默认采样率
     * @param duration   传输每个字持续时间(duration),即每个字传输周期正弦波持续时间
     * @return
     */
    private void genSinEquation(int frequency, int sampleRate, int duration) {
        if (mState == STATE_START) {
            // 从消费队列中获取
            SinBuffer.SinBufferData sinBufferData = sinGeneratorCallback.getGenBuffer();
            // 每个字发出频率的采样点数
            int frameRateCount = (duration * sampleRate) / 1000;
            // 步数
            double thea = 0;

            int n = MAX_SHORT / 2;

            int index = 0;
            // 正弦波 的频率
            double thetaIncrement = (frequency / (double) sampleRate) * 2 * Math.PI;

            Log.d(TAG, "thetaIncrement " + String.valueOf(thetaIncrement) + "frequency  " + frequency);
            for (int frame = 0; frame < frameRateCount; frame++) {
                if (mState == STATE_START) {
                    // 算出不同点的正弦值
                    int out = (int) (Math.sin(thea) * n) + 128;
                    if (index >= SinBuffer.DEFAULT_BUFFER_SIZE - 1) {
                        // 超过限定值之后重置大小
                        sinBufferData.setBufferSize(index);
                        sinGeneratorCallback.freeGenBuffer(sinBufferData);
                        index = 0;
                        sinBufferData = sinGeneratorCallback.getGenBuffer();
                    }
                    // 转码为short类型并保存,& 0xff是为了防止负数转换出现异常
                    sinBufferData.shortData[index++] = (byte) (out & 0xff);
                    if (MAX_SHORT == n * 2) {
                        sinBufferData.shortData[index++] = (byte) ((out >> 8) & 0xff);
                    }
                    thea += thetaIncrement;
                }
            }
            // 加入到消费队列中去
            if (sinBufferData != null) {
                sinBufferData.setBufferSize(index);
                sinGeneratorCallback.freeGenBuffer(sinBufferData);
            }
        }
    }

    private SinGeneratorCallback sinGeneratorCallback;

    public boolean isStop() {
        return (STATE_STOP == mState);
    }

    public interface SinGeneratorCallback {
        /**
         * 获取队列中的数据
         * @return
         */
        SinBuffer.SinBufferData getGenBuffer();

        /**
         * 释放队列中的资源
         * @param buffer
         */
        void freeGenBuffer(SinBuffer.SinBufferData buffer);
    }

    public void setSinGeneratorCallback(SinGeneratorCallback sinGeneratorCallback) {
        this.sinGeneratorCallback = sinGeneratorCallback;
    }
}

二、声波通讯音频播放

package home.gz.com.sinvoicetool;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;

/**
 * @author Wudi
 * @date 2018/10/26
 */
public class SinPcmPlayer {
    /**
     * 播放状态,用于控制播放或者是停止
     */
    private int mState;
    private final static int STATE_START = 1;
    private final static int STATE_STOP = 2;

    /**
     * 音频播放
     */
    private AudioTrack audioTrack;
    /**
     * 播放字节数
     */
    private long playedCharLength = 0;

    public SinPcmPlayer() {
        mState = STATE_STOP;
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, CommonUtil.DEFAULT_SAMPLE_RATE,
                AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, SinBuffer.DEFAULT_BUFFER_SIZE, AudioTrack.MODE_STREAM);
    }

    public void start() {
        if(mState==STATE_STOP&&audioTrack!=null) {
            mState = STATE_START;
            playedCharLength=0;
            if(sinPcmCallback!=null) {
                // 初始化AudioTrack对象(音频流类型,采样率,通道,格式,缓冲区大小,模式)
                while (mState == STATE_START) {
                    SinBuffer.SinBufferData sinBufferData = sinPcmCallback.getPcmBuffer();
                    if (sinBufferData != null && sinBufferData.shortData != null) {
                        if (playedCharLength == 0) {
                            audioTrack.play();
                        }
                        int len = audioTrack.write(sinBufferData.getShortData(), 0, sinBufferData.getBufferSize());

                        playedCharLength += len;

                        // 释放已播放的数据
                        sinPcmCallback.freePcmBuffer(sinBufferData);
                    }else {
                        break;
                    }
                }
                if (STATE_STOP == mState) {
                    audioTrack.pause();
                    audioTrack.flush();
                    audioTrack.stop();
                    audioTrack.release();
                }
            }else {
                throw new IllegalArgumentException("PcmCallback can't be null");
            }
        }
    }
    public void stop() {
        if (STATE_START == mState && null != audioTrack) {
            mState = STATE_STOP;
        }
    }
    private SinPcmCallback sinPcmCallback;

    public interface SinPcmCallback {
        /**
         * 获取队列中的数据
         * @return
         */
        SinBuffer.SinBufferData getPcmBuffer();

        /**
         * 释放队列中的资源
         * @param buffer
         */
        void freePcmBuffer(SinBuffer.SinBufferData buffer);
    }

    public void setSinPcmCallback(SinPcmCallback sinPcmCallback) {
        this.sinPcmCallback = sinPcmCallback;
    }
}

三、声波通讯音频录制

package home.gz.com.sinvoicetool;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

/**
 * @author Wudi
 * @date 2018/10/26
 */
public class SinRecord {

    private AudioRecord record;

    private int mState;
    private final static int STATE_START = 1;
    private final static int STATE_STOP = 2;

    public SinRecord() {

    }

    /**
     * 录制音频,开始解码
     */
    public void start() {
        mState = STATE_START;
        record=new AudioRecord(MediaRecorder.AudioSource.MIC
                , CommonUtil.DEFAULT_SAMPLE_RATE
                , AudioFormat.CHANNEL_IN_MONO
                , AudioFormat.ENCODING_PCM_16BIT
                , SinBuffer.DEFAULT_BUFFER_SIZE);
        record.startRecording();
        while (mState == STATE_START) {
            SinBuffer.SinBufferData data = sinRecordCallBack.getDecodingBuff();
            if (null != data) {
                if (null != data.shortData) {
                    int bufferReadResult = record.read(data.shortData, 0, SinBuffer.DEFAULT_BUFFER_SIZE);
                    data.setBufferSize(bufferReadResult);
                    sinRecordCallBack.freeRecordBuffer(data);
                } else {
                    // 结束输入
                    break;
                }
            } else {
                break;
            }
        }
        record.stop();
        record.release();
    }
    public void stop() {
        if (STATE_START == mState) {
            mState = STATE_STOP;
        }
    }

    private SinRecordCallBack sinRecordCallBack;

    public interface SinRecordCallBack {
        /**
         * 获取录音缓存空间
         *
         * @return
         */
        SinBuffer.SinBufferData getDecodingBuff();

        /**
         * 释放缓存空间
         *
         * @param sinBufferData
         */
        void freeRecordBuffer(SinBuffer.SinBufferData sinBufferData);
    }

    public void setSinDecodingCallBack(SinRecordCallBack sinRecordCallBack) {
        this.sinRecordCallBack = sinRecordCallBack;
    }
}

四、音频解码

 /**
     * 解码执行
     *
     * @param sinBufferData
     */
    private void process(SinBuffer.SinBufferData sinBufferData) {
        int size = sinBufferData.getBufferSize();
        short sh = 0;

        for (int i = 0; i < size; i++) {
            short sh1 = sinBufferData.shortData[i];
            // 有符号转化为无符号位
            sh1 &= 0xff;
            short sh2 = sinBufferData.shortData[++i];
            // <<      :     左移运算符,num << 1,相当于num乘以2
            // 在编码中   bufferData.byteData[mFilledSize++] = (byte) ((out >> 8) & 0xff);
            sh2 <<= 8;
            // 位运算,求两者之和
            sh = (short) ((sh1) | (sh2));
            if (!mIsStartCounting) {
                // 判断此频率是否是有周期的,进行滤波处理
                if (STATE_STEP1 == mStep) {
                    // 通过一次高低峰值对比
                    if (sh < 0) {
                        mStep = STATE_STEP2;
                    }
                } else if (STATE_STEP2 == mStep) {
                    if (sh > 0) {
                        mIsStartCounting = true;
                        mCirclePointCount = 0;
                        mStep = STATE_STEP1;
                    }
                }
            } else {
                // 计算周期正负变换
                ++mCirclePointCount;
                if (STATE_STEP1 == mStep) {
                    if (sh < 0) {
                        mStep = STATE_STEP2;
                    }
                } else if (STATE_STEP2 == mStep) {
                    if (sh > 0) {
                        Log.d(TAG, "mCirclePointCount " + mCirclePointCount);
                        // 预处理 。取周期中间值,增加传输容错率
                        int circleCount = preRecognition(mCirclePointCount);
                        // 识别语音,处理音频周期
                        recognition(circleCount);
                        mCirclePointCount = 0;
                        mStep = STATE_STEP1;
                    }
                }
            }
        }
    }


    /**
     * 处理周期数偏差
     *
     * @param circleCount
     * @return
     */
    private int preRecognition(int circleCount) {
        switch (circleCount) {
            case 4:
            case 5:
            case 6:
                circleCount = 5;
                break;
            case 7:
            case 8:
            case 9:
                circleCount = 8;
                break;
            case 10:
            case 11:
            case 12:
                circleCount = 11;
                break;
            case 13:
            case 14:
            case 15:
                circleCount = 14;
                break;
            default:
                circleCount = 0;
                break;
        }

        return circleCount;
    }

    private StringBuffer stringBuffer = new StringBuffer();

    /**
     * 正式处理周期
     *
     * @param circleCount
     */
    private void recognition(int circleCount) {
        // 判断是否是指定的字符开头
        if (!mIsBeginning) {
            if (!mStartingDet) {
                // 8的周期取样数量 对应的是指定开头字符 1所对应周期 ,开始标志 进入取样模式
                if (START_CIRCLE == circleCount) {
                    mStartingDet = true;
                    mStartingDetCount = 0;
                    Log.d("周期 起始  ", circleCount + "");
                }
            } else {
                // 再次为 8 的话计数+1,否则重置
                if (START_CIRCLE == circleCount) {
                    ++mStartingDetCount;
                    Log.d("周期 再次  ", circleCount + "");
                    //  计算周期采样数为 8的 周期数大于最小周期采样数的时候,开始记录有效数字周期数
                    if (mStartingDetCount >= END_CIRCLE) {
                        mIsBeginning = true;
                        mIsRegStart = false;
                        mRegCount = 0;
                    }
                } else {
                    mStartingDet = false;
                }
            }
        } else {
            // 如果是1开头且判断是否是在注册文本中的数字
            if (!mIsRegStart) {
                // 是否有效值注册开始
                if (circleCount > 0) {
                    mRegValue = circleCount;
                    // 判断是 文本表对应注册的文本数字
                    mIsRegStart = true;
                    mRegCount = 1;
                    Log.d("周期 确定  ", circleCount + "");
                }
            } else {
                // 判断是文本表对应的数字后就不再判断,直接进行计数
                if (circleCount == mRegValue) {
                    ++mRegCount;
                    Log.d("周期 计数", " mRegCount" + mRegCount + " Value " + mRegValue);
                    // 当注册的计数个数大于最小计数个数的时候
                    if (mRegCount >= END_CIRCLE) {
                        if (mPreRegCircle != mRegValue) {

                            if (null != onRecognitionListener) {
                                if (circleCount == 8) {
                                    stringBuffer.append("1");
                                }
                                if (circleCount == 11) {
                                    stringBuffer.append("0");
                                }
                            }
                            if (circleCount == 14) {
                                onRecognitionListener.onRecognition(stringBuffer.toString());
                                Log.d("CommonUtil", stringBuffer.toString());
                                stringBuffer = new StringBuffer();
                            }
                            mPreRegCircle = mRegValue;
                        }

                        mIsRegStart = false;
                    }
                } else {
                    mIsRegStart = false;
                }
            }
        }
    }


相关文章

  • 声波通讯、配网、声波互动(二)

    一、声波通讯编码 二、声波通讯音频播放 三、声波通讯音频录制 四、音频解码

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

    一、什么是声波通讯 声波通信的原理其实比较简单,主要是用单频率声音信号对数据进行编码,然后播放这些单频率声音,接收...

  • 声波传输解码

    这篇文章是系列文章的第3篇。 第一篇在这里声波配网原理。 关于声波传输编码的部分在这里声波传输编码。 前面说过,声...

  • 声波传输编码

    这篇文章是系列文章的第2篇。第一篇在这里声波配网原理。 在声波传输的发送端,我们要做的事情用下面这张图就可以概括:...

  • 识别成功率100%的声波配网,看完你也可以实现一个

    好久没更新文章了,蠢作者刚好最近写了个有意思的小工具,所以就写它好啦~ 1.什么是声波配网2.声波传输的技术原理3...

  • 智能设备Socket通讯Wifi配网(一)

    一、 序言 之前公司项目采用了声波通讯,但是对于封闭的金属外壳的智能设备来说效果还是不太理想,毕竟金属对于声波的屏...

  • 超声波和次声波对人体的伤害

    Posted on 2017年3月16日 声波的分类: 听不到的次声波:小于20Hz的声波,次声波的波长往往很长,...

  • 认识次声波

    一.声波类型 次声波是频率小于20Hz(赫兹)的声波,不容易衰减,[1]不易被水和空气吸收。 次声波的波...

  • 超声波测厚仪和涂层测厚仪有什么不同

    超声波测厚仪和涂层测厚仪有什么不同?超声波测厚仪是利用超声波的原理对金属、塑料、陶瓷、玻璃及其他任何超声波的良导体...

  • 声波

    我怎么如此幸运-99将帅挑战赛80-重生260-戴红霞(2021-05-23) 我怎么如此幸运-声波 1.我怎么如...

网友评论

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

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