一、声波通讯编码
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;
}
}
}
}
网友评论