1、讯飞性别年龄识别
package com.day.controller;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
/**
* 1.年龄识别
* 2.如果是一个restful接口(json格式),一般将Content-Type设置为application/json; charset=UTF-8
* 3.如果是文件上传, 一般Content-Type设置为multipart/form-data
* 4.如果普通表单提交,一般Content-Type设置为application/x-www-form-urlencoded; charset=UTF-8
*/
public class HelloPost {
public static String url="http://tupapi.xfyun.cn/v1/age";
public static String APPID = "";
public static String APIKey = "";
public static String IMAGE_NAME="src/main/resources/static/1.jpg";
public static String IMAGE_PATH="src/main/resources/static/1.jpg";
public static String XCurTime=System.currentTimeMillis() / 1000L + "";
public static String myParam = "{\"image_name\":\"" + IMAGE_NAME + "\"}";
public static String checkSum;
static {
try {
checkSum = DigestUtils.md5Hex(APIKey + XCurTime
+ new String(Base64.encodeBase64(myParam.getBytes("UTF-8"))));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/**主函数*/
public static void main(String args[]){
String result=sendPost(url,"&key=1&secret=2");
System.out.println("调用结果 ==》"+result);
}
public static String sendPost(String url, String params) {
PrintWriter printWriter = null;
BufferedReader bufferedReader = null;
String result = "";
try {
URL realUrl = new URL(url);
// 打开和URL之间的连接
URLConnection myURLConnection = realUrl.openConnection();
//设置通用的请求属性(Header里的参数)
myURLConnection.setRequestProperty("accept", "*/*");
myURLConnection.setRequestProperty("connection", "Keep-Alive");
myURLConnection.setRequestProperty("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
/**1.浏览器的原生form表单,一般用于表单提交
* 2.在 Http Request Header 中配置以下参数*/
myURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
myURLConnection.setRequestProperty("X-Appid",APPID);
myURLConnection.setRequestProperty("X-CurTime",XCurTime);
myURLConnection.setRequestProperty("X-Param",new String(Base64.encodeBase64(myParam.getBytes("UTF-8"))));
myURLConnection.setRequestProperty("X-CheckSum",checkSum);
// 发送POST请求必须设置如下两行
myURLConnection.setDoOutput(true);
myURLConnection.setDoInput(true);
/**1.printWriter:我们一般用来传的是对像
* 2.而outputStream用来传的是二进制,故上传文件时,一定要使用此。
* 3.将图片的二进制数据写入 Http Request Body 中*/
//获取URLConnection对象对应的输出流 发送图片数据
OutputStream outputStream = myURLConnection.getOutputStream();
byte[] imageByteArray=read(IMAGE_PATH);
outputStream.write(imageByteArray);
// 获取URLConnection对象对应的输出流 发送参数对象
printWriter = new PrintWriter(outputStream);
// 发送请求参数
printWriter.print(params);
// flush输出流的缓冲
printWriter.flush();
printWriter.close();
outputStream.flush();
outputStream.close();
// 定义BufferedReader输入流来读取URL的响应
bufferedReader = new BufferedReader(
new InputStreamReader(myURLConnection.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
result += line;
}
}catch (Exception e) {
System.out.println("发送 POST 请求出现异常!"+e);
e.printStackTrace();
}finally{//使用finally块来关闭输出流、输入流
try{
if(printWriter!=null){
printWriter.close();
}
if(bufferedReader!=null){
bufferedReader.close();
}
}catch(Exception ex){
ex.printStackTrace();
}
}
return result;
}
/**读取图片或音频文件*/
public static byte[] read(String filePath) throws IOException {
InputStream inputStream = new FileInputStream(filePath);
byte[] data = inputStream2ByteArray(inputStream);
inputStream.close();
return data;
}
private static byte[] inputStream2ByteArray(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n = 0;
while ((n = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, n);
}
return byteArrayOutputStream.toByteArray();
}
/**判断文件是否是以二进制流存储的*/
public static boolean isBinary(File file) {
boolean isBinaryFlag = false;
FileInputStream fileInputStream=null;
try {
fileInputStream = new FileInputStream(file);
long len = file.length();
for (int j = 0; j < (int) len; j++) {
int t = fileInputStream.read();
if (t < 32 && t != 9 && t != 10 && t != 13) {
isBinaryFlag = true;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try{
fileInputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
return isBinaryFlag;
}
}
2、讯飞离线语音合成Windows/Linux
# 1、离线语音合成调用主函数(离线语音合成调用属于简单的,无回调函数)
package com.iflytek;
import com.iflytek.util.Step2_tts_thread;
import com.iflytek.util.Step3_audioFormat;
import java.util.Scanner;
import javax.sound.sampled.*;
/**
* 请注意!!!
* 1.首选到控制台https://console.xfyun.cn/services/aisound下载普通离线语音合成的Windows MSC。
* 2.普通离线语音合成Windows MSC解压后,把bin目录下msc文件夹与dll文件拷贝到res目录下。
* 3.最后请替换Step2_tts_thread中的appid值,appid值在下载页面控制台可以看到。
*/
public class OfflineTtsMain {
//录音相关参数
public static AudioFormat audioFormat;
public static SourceDataLine sourceDataLine;
public static void main(String[] args) throws Exception {
System.out.println("输入y开始体验普通离线语音合成...");
Scanner myScanner = new Scanner(System.in);
String userCommand = myScanner.next();
long startTime = System.currentTimeMillis();
if(userCommand.equals("y")){
audioFormat = Step3_audioFormat.getAudioFormat(audioFormat);//构造具有线性 PCM 编码和给定参数的 AudioFormat。
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class,audioFormat, AudioSystem.NOT_SPECIFIED);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
Step2_tts_thread myThread=new Step2_tts_thread();
myThread.start();
}
}
}
# 2、根据MSC文档用JAVA重写方法
package com.iflytek.service;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
public interface Step1_tts_dll extends Library {
/**
* 重点:
* 1.char * 对应 String
* 2.int * 对应 IntByReference
* 3.void * 对应 byte[]/Pointer,回调函数里此类型需用String来对应。
* 4.int 对应 int
* 5.无参 对应 void
* 6.回调函数 对应 根据文档自定义回调函数,实现接口Callback,离线语音合成无回调
*/
//加载dll动态库并实例化,从而使用其内部的方法
Step1_tts_dll INSTANCE = (Step1_tts_dll) Native.loadLibrary("res/msc_x64.dll", Step1_tts_dll.class);
//定义登录方法
public int MSPLogin(String usr, String pwd, String params);
//开始一次普通离线语音合成
public String QTTSSessionBegin(String params, IntByReference errorCode);
//写入需要合成的文本
public int QTTSTextPut(String sessionID,String textString,int textLen,String params);
//获取离线合成的音频
public Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);
//结束本次普通离线语音合成
public int QTTSSessionEnd(String sessionID, String hints);
//定义退出方法
public int MSPLogout();
}
# 3、把调用步骤依次写进线程类
package com.iflytek.util;
import com.iflytek.OfflineTtsMain;
import com.iflytek.service.Step1_tts_dll;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import java.io.ByteArrayOutputStream;
import java.util.Scanner;
public class Step2_tts_thread extends Thread{
public static int total_audio_length=0;
public static ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
public void run() {
while (true) {
//登录参数
String login_params = "appid = 替换你的appid, work_dir = ./res";
//第一个参数为用户名,第二个参数为密码,传null即可
int ret = Step1_tts_dll.INSTANCE.MSPLogin(null, null, login_params);
if (ret != 0) {//登录成功标志ret为0
System.out.println("登录失败...请检查");
System.exit(1);
} else {
//System.out.println("登录成功...");
}
//请让一让,A02-02-03
//开始一次普通离线语音合成会话
String session_begin_params = "engine_type = local, voice_name = xiaoyan, text_encoding = UTF8, tts_res_path = fo|res/tts/xiaoyan.jet;fo|res/tts/common.jet, sample_rate = 16000, speed = 50, volume = 50, pitch = 50, rdn = 2";
IntByReference errorCode = new IntByReference(-100);
String session_id = Step1_tts_dll.INSTANCE.QTTSSessionBegin(session_begin_params,errorCode);
if(errorCode.getValue()==0){
//System.out.println("开启普通离线语音合成成功,session_id是:"+session_id);
}else{
System.out.println("开启普通离线语音合成失败:"+errorCode.getValue());
}
//写入需要合成的文本
System.out.println("\033[43;34;4m"+"请输入你要合成的文本并回车"+"\033[0m");
Scanner myScanner = new Scanner(System.in);
String tts_text =myScanner.nextLine();
//System.out.println(tts_text);//注意获取的文本是否完整
ret= Step1_tts_dll.INSTANCE.QTTSTextPut(session_id, tts_text, tts_text.getBytes().length,null);
if(ret==0){
//System.out.println("写入合成文本成功...");
}else{
System.out.println("写入合成文本失败:"+ret);
}
//循环获取离线合成的音频,并实时进行播放
IntByReference audio_len=new IntByReference(-100);
IntByReference synth_status=new IntByReference(-100);
errorCode=new IntByReference(-100);
try{
//实时播放
OfflineTtsMain.sourceDataLine.open(OfflineTtsMain.audioFormat);
OfflineTtsMain.sourceDataLine.start();
}catch (Exception e){
e.printStackTrace();
}
//int i=0;
while (true) {
Pointer audioPointer = Step1_tts_dll.INSTANCE.QTTSAudioGet(session_id,audio_len,synth_status,errorCode);
byte[] audioDataByteArray=null;
if(audioPointer!=null){
audioDataByteArray=audioPointer.getByteArray(0,audio_len.getValue());
}
if (errorCode.getValue()==0){
//System.out.println("正常获取音频中...");
}else{
System.out.println("获取音频发生错误:"+errorCode);
break;
}
if (audioDataByteArray!=null ){
try{
//实时播放
OfflineTtsMain.sourceDataLine.write(audioDataByteArray, 0, audio_len.getValue());
/*i=i+1;//实现暂停的效果
System.out.println(i);
if(i==5){
OfflineTtsMain.sourceDataLine.stop();
Thread.sleep(5000);
}
if(i==6){
OfflineTtsMain.sourceDataLine.start();
}*/
//将数据写入字节数组的输出流,用来生成音频文件
byteArrayOutputStream.write(audioDataByteArray, 0, audio_len.getValue());
}catch(Exception e){
e.printStackTrace();
}
//计算总音频长度,用来生成音频文件
total_audio_length=total_audio_length+audio_len.getValue();
}
if (synth_status.getValue()==2){
//说明音频已经取完,退出本次循环
try{
OfflineTtsMain.sourceDataLine.drain();
OfflineTtsMain.sourceDataLine.close();
byteArrayOutputStream.flush();
byteArrayOutputStream.close();
}catch (Exception e){
e.printStackTrace();
}
break;
}
}
//把合成的音频存放为wav格式
String dateAsFileName= String.valueOf(System.currentTimeMillis());//用当前时间标记文件名
Boolean waveProductFlag=Step4_wave_product.waveProduct(total_audio_length,byteArrayOutputStream,"./res/"+dateAsFileName+".wav");
if(waveProductFlag){
System.out.println("生成音频文件成功:"+dateAsFileName+".wav");
}else{
System.out.println("生成音频文件失败...");
}
//结束本次普通离线语音合成
ret=Step1_tts_dll.INSTANCE.QTTSSessionEnd(session_id, "正常退出");
if(ret==0){
//System.out.println("离线语音合成正常退出...");
}else{
System.out.println("离线语音合成退出异常:"+ret);
}
//执行最终退出
ret = Step1_tts_dll.INSTANCE.MSPLogout();
if (ret == 0) {
//System.out.println("正常退出...");
} else {
System.out.println("异常退出:"+ret);
}
//是否退出Java程序
System.out.println("\33[43;31;4m"+"结束合成:输入n,继续合成:输入其他内容"+"\33[0m");
Scanner myScanner2 = new Scanner(System.in);
String userCommand = myScanner2.nextLine();
if(userCommand.equals("n")){
OfflineTtsMain.sourceDataLine.stop();
OfflineTtsMain.sourceDataLine.close();
System.exit(0);
}
}
}
}
# 4、生成16K、16BIT单声道音频流
package com.iflytek.util;
import javax.sound.sampled.AudioFormat;
public class Step3_audioFormat {
//构造线程参数
//16k采样率的16bit音频,一帧的大小为640B, 时长20ms
/**
sampleRate - 每秒样品数
sampleSizeInBits - 每个样本中的位数
channels - 通道数(1为mono,2为立体声等)
signed - 表示数据是签名还是无符号
bigEndian - 指示单个样本的数据是否以大字节顺序存储( false表示小端)
*/
public static AudioFormat getAudioFormat(AudioFormat audioFormat) {
audioFormat=new AudioFormat(16000F, 16, 1,true,false);
// true,false 指示是以 big-endian 顺序还是以 little-endian 顺序存储音频数据。
return audioFormat;//构造具有线性 PCM 编码和给定参数的 AudioFormat。
}
}
# 5、生成WAV音频所需的文件头
package com.iflytek.util;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class Step4_wave_product {
public static byte[] RIFF = "RIFF".getBytes();
public static byte[] RIFF_SIZE = new byte[8];
public static byte[] RIFF_TYPE = "WAVE".getBytes();
public static byte[] FORMAT = "fmt ".getBytes();
public static byte[] FORMAT_SIZE = new byte[4];
public static byte[] FORMAT_TAG = new byte[2];
public static byte[] CHANNELS = new byte[2];
public static byte[] SamplesPerSec = new byte[4];
public static byte[] AvgBytesPerSec = new byte[4];
public static byte[] BlockAlign = new byte[2];
public static byte[] BitsPerSample = new byte[2];
public static byte[] Data = "data".getBytes();
public static byte[] DataSize = new byte[4];
public static boolean waveProduct(int audioLength, ByteArrayOutputStream byteArrayOutputStream, String outputFilePath){
Step4_wave_product.DataSize = Step4_wave_product.revers(Step4_wave_product.intToBytes(audioLength));
Step4_wave_product.RIFF_SIZE = Step4_wave_product.revers(Step4_wave_product.intToBytes(audioLength + 36 - 8));
File waveFile = new File(outputFilePath);
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(waveFile);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
init();
bufferedOutputStream.write(RIFF);
bufferedOutputStream.write(RIFF_SIZE);
bufferedOutputStream.write(RIFF_TYPE);
bufferedOutputStream.write(FORMAT);
bufferedOutputStream.write(FORMAT_SIZE);
bufferedOutputStream.write(FORMAT_TAG);
bufferedOutputStream.write(CHANNELS);
bufferedOutputStream.write(SamplesPerSec);
bufferedOutputStream.write(AvgBytesPerSec);
bufferedOutputStream.write(BlockAlign);
bufferedOutputStream.write(BitsPerSample);
bufferedOutputStream.write(Data);
bufferedOutputStream.write(DataSize);
bufferedOutputStream.write(byteArrayOutputStream.toByteArray());
bufferedOutputStream.flush();
bufferedOutputStream.close();
return true;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
public static void init() {
FORMAT_SIZE = new byte[]{(byte) 16, (byte) 0, (byte) 0, (byte) 0};
byte[] tmp = revers(intToBytes(1));
FORMAT_TAG = new byte[]{tmp[0], tmp[1]};
CHANNELS = new byte[]{tmp[0], tmp[1]};
SamplesPerSec = revers(intToBytes(16000));
AvgBytesPerSec = revers(intToBytes(32000));
tmp = revers(intToBytes(2));
BlockAlign = new byte[]{tmp[0], tmp[1]};
tmp = revers(intToBytes(16));
BitsPerSample = new byte[]{tmp[0], tmp[1]};
}
public static byte[] revers(byte[] tmp) {
byte[] reversed = new byte[tmp.length];
for (int i = 0; i < tmp.length; i++) {
reversed[i] = tmp[tmp.length - i - 1];
}
return reversed;
}
public static byte[] intToBytes(int num) {
byte[] bytes = new byte[4];
bytes[0] = (byte) (num >> 24);
bytes[1] = (byte) ((num >> 16) & 0x000000FF);
bytes[2] = (byte) ((num >> 8) & 0x000000FF);
bytes[3] = (byte) (num & 0x000000FF);
return bytes;
}
}
# 6、放置讯飞开放平台下载Linux/Windows中所带的资源,图-1
# 7、Linux代码基本类似,离线合成普通版,注意资源放置
package com.iflytek.service;
import com.iflytek.OfflineTtsMain;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
public interface Step1_tts_so extends Library {
/**
* 重点:
* 1.char * 对应 String
* 2.int * 对应 IntByReference
* 3.void * 对应 byte[]/Pointer,回调函数里此类型需用String来对应。
* 4.int 对应 int
* 5.无参 对应 void
* 6.回调函数 对应 根据文档自定义回调函数,实现接口Callback,离线语音合成无回调
*/
//加载dll动态库并实例化,从而使用其内部的方法
Step1_tts_so INSTANCE = (Step1_tts_so) Native.loadLibrary(OfflineTtsMain.userPath+"res/libmsc.so", Step1_tts_so.class);
//定义登录方法
public int MSPLogin(String usr, String pwd, String params);
//开始一次普通离线语音合成
public String QTTSSessionBegin(String params, IntByReference errorCode);
//写入需要合成的文本
public int QTTSTextPut(String sessionID,String textString,int textLen,String params);
//获取离线合成的音频
public Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);
//结束本次普通离线语音合成
public int QTTSSessionEnd(String sessionID, String hints);
//定义退出方法
public int MSPLogout();
}
# 8、Linux代码基本类似,离线合成高品质,注意资源放置
package com.iflytek.service;
import com.iflytek.OfflineHighQualityTtsMain;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
public interface Step1_tts_so extends Library {
/**
* 重点:
* 1.char * 对应 String
* 2.int * 对应 IntByReference
* 3.void * 对应 byte[]/Pointer,回调函数里此类型需用String来对应。
* 4.int 对应 int
* 5.无参 对应 void
* 6.回调函数 对应 根据文档自定义回调函数,实现接口Callback,离线语音合成无回调
*/
//加载so动态库并实例化,从而使用其内部的方法
Step1_tts_so INSTANCE = (Step1_tts_so) Native.loadLibrary(OfflineHighQualityTtsMain.userPath+"res/libmsc.so", Step1_tts_so.class);
//定义登录方法
public int MSPLogin(String usr, String pwd, String params);
//开始一次高品质离线语音合成
public String QTTSSessionBegin(String params, IntByReference errorCode);
//写入需要合成的文本
public int QTTSTextPut(String sessionID,String textString,int textLen,String params);
//获取离线合成的音频
public Pointer QTTSAudioGet(String sessionID, IntByReference audioLen, IntByReference synthStatus, IntByReference errorCode);
//结束本次高品质离线语音合成
public int QTTSSessionEnd(String sessionID, String hints);
//定义退出方法
public int MSPLogout();
}
# 9、普通版与高品质版对内存的消耗也不同,请根据实际所需进行选择
3、讯飞离线唤醒Windows/Linux
# 1、写调用主函数
package com.iflytek;
import com.iflytek.util.Step3_ivw_thread;
import com.iflytek.util.Step4_audioFormat;
import java.util.Scanner;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
/**
* 请注意!!!
* 1.首选到控制台https://console.xfyun.cn/services/awaken下载唤醒的Windows MSC。
* 2.下载的唤醒Windows MSC解压后,把bin目录下msc文件夹与dll文件拷贝到res目录下。
* 3.最后请替换Step3_ivw_thread中的appid值,appid值在下载页面控制台可以看到。
*/
public class OfflineIvwMain {
//录音相关参数
public static AudioFormat audioFormat;
public static TargetDataLine targetDataLine;
public static void main(String[] args) throws Exception {
System.out.println("y开始体验唤醒,n结束唤醒");
Scanner input = new Scanner(System.in);
String inputContent = input.next();
long startTime = System.currentTimeMillis();
if(inputContent.equals("y")){
audioFormat = Step4_audioFormat.getAudioFormat(audioFormat);//构造具有线性 PCM 编码和给定参数的 AudioFormat。
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
Step3_ivw_thread myThread=new Step3_ivw_thread();
myThread.start();
Scanner input_2 = new Scanner(System.in);
String inputContent_2 = input_2.next();
if(inputContent_2.equals("n")){
targetDataLine.stop();
targetDataLine.close();
}
System.out.println("唤醒操作持续:"+(System.currentTimeMillis()-startTime)/1000+"秒!");
System.exit(0);
}
}
}
# 2、 根据文档用JAVA重写方法
package com.iflytek.service;
import com.iflytek.util.Step2_ivw_ntf_handler;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
public interface Step1_ivw_dll extends Library {
/**
* 重点:
* 1.char * 对应 String
* 2.int * 对应 IntByReference
* 3.void * 对应 Pointer或byte[]
* 4.int 对应 int
* 5.无参 对应 无参
* 6.回调函数 对应 根据文档自定义回调函数,实现接口Callback
*/
//加载dll动态库并实例化,从而使用其内部的方法
Step1_ivw_dll INSTANCE = (Step1_ivw_dll) Native.loadLibrary
("res/msc_x64.dll", Step1_ivw_dll.class);
//定义登录方法 MSPLogin(const char *usr, const char *pwd, const char *params)
public int MSPLogin(String usr, String pwd, String params);
//定义开始方法 QIVWSessionbegin(const char *grammarList, const char *params, int *errorCode)
public String QIVWSessionBegin(String grammarList, String params, IntByReference errorCode);
//定义写音频方法 QIVWAudioWrite(const char *sessionID, const void *audioData, unsigned int audioLen, int audioStatus)
public int QIVWAudioWrite(String sessionID, byte[] audioData, int audioLen, int audioStatus);
//定义结束方法 QIVWSessionEnd(const char *sessionID, const char *hints)
public int QIVWSessionEnd(String sessionID, String hints);
//定义获取结果方法 QIVWRegisterNotify(const char *sessionID, ivw_ntf_handler msgProcCb, void *userData)
public int QIVWRegisterNotify(String sessionID, Step2_ivw_ntf_handler msgProcCb, byte[] userData);
//定义退出方法 MSPLogout()
public int MSPLogout();
}
# 3、根据文档写回调函数
package com.iflytek.util;
import com.sun.jna.Callback;
public class Step2_ivw_ntf_handler implements Callback {
//根据文档写回调方法
public int cb_ivw_msg_proc(String sessionID, int msg, int param1, int param2,
String info, String userData) {
System.out.println("..............................................");
System.out.println("回调函数返回的唤醒结果:"+info);
return 0;
}
}
# 4、把调用步骤依次写到线程类
package com.iflytek.util;
import com.iflytek.OfflineIvwMain;
import com.iflytek.service.Step1_ivw_dll;
import com.sun.jna.ptr.IntByReference;
import javax.sound.sampled.AudioInputStream;
public class Step3_ivw_thread extends Thread{
public void run() {
//登录参数
String lgi_param = "appid = 替换你的appid, work_dir = ./res";
String ssb_param = "ivw_threshold=0:1450,sst=wakeup,ivw_shot_word=1,ivw_res_path =fo|res/ivw/wakeupresource.jet";
int ret = Step1_ivw_dll.INSTANCE.MSPLogin(null, null, lgi_param);
if (ret != 0) {//登录成功标志ret为0
System.out.println("登录失败...请检查");
System.exit(1);
} else {
System.out.println("请注意,唤醒语音需要根据控制台定义的唤醒词来进行唤醒...");
}
//开启会话
IntByReference intByReference = new IntByReference(-100);
String sessionId = Step1_ivw_dll.INSTANCE.QIVWSessionBegin(null, ssb_param, intByReference);
if (intByReference.getValue() == 0) {//只有返回0为函数调用成功
System.out.println("本次会话开启成功...,会话id为:" + sessionId);
}
int frameSize = 6400; //一帧的大小为6400B,其他的可能导致无法长时间唤醒
int audioStatus = 1;//用来告知MSC音频发送是否完成
//注册回调
Step2_ivw_ntf_handler msgProcCb = new Step2_ivw_ntf_handler();//回调函数实例
ret = Step1_ivw_dll.INSTANCE.QIVWRegisterNotify(sessionId, msgProcCb, null);
if (ret == 0) {//返回为0则代表成功
System.out.println("注册回调函数成功...");
} else {
System.out.println("注册函数返回的错误码" + ret);
}
//反复调用QIVWAudioWrite写音频方法,直到音频写完为止
//long startTime=System.currentTimeMillis();
try {
while (true) {
byte[] audioDataByteArray = new byte[frameSize];
OfflineIvwMain.targetDataLine.open(OfflineIvwMain.audioFormat);
OfflineIvwMain.targetDataLine.start();
int len = new AudioInputStream(OfflineIvwMain.targetDataLine).read(audioDataByteArray);
//long endTime=System.currentTimeMillis();
if (len == -1) {//调用麦克风时候,这段将不会被执行...
//||(endTime-startTime>60*1000)
audioStatus = 4;//最后帧
ret = Step1_ivw_dll.INSTANCE.QIVWAudioWrite(sessionId, "".getBytes(), 0, audioStatus);
System.out.println("最后一帧返回的错误码:" + ret + ",即将执行退出...");
break; //文件读完,跳出循环
} else {
//反复调用
ret = Step1_ivw_dll.INSTANCE.QIVWAudioWrite(sessionId, audioDataByteArray, len, audioStatus);
}
audioStatus = 2;//中间帧
if (ret != 0) {
System.err.println("出错了:" + ret);
}
Thread.sleep(200); //模拟人说话时间间隙
}
} catch (Exception e) {
e.printStackTrace();
}
//终止会话
ret = Step1_ivw_dll.INSTANCE.QIVWSessionEnd(sessionId, "正常终止");
if (ret == 0) {
System.out.println("本次会话正常终止...");
}
//执行退出
ret = Step1_ivw_dll.INSTANCE.MSPLogout();
if (ret == 0) {
System.out.println("正常退出...");
}else{
System.out.println("异常退出...");
}
}
}
# 5、 录音生成16K、16BIT单声道的音频流
package com.iflytek.util;
import javax.sound.sampled.AudioFormat;
public class Step4_audioFormat {
//构造线程参数
//16k采样率的16bit音频,一帧的大小为640B, 时长20ms
/**
sampleRate - 每秒样品数
sampleSizeInBits - 每个样本中的位数
channels - 通道数(1为mono,2为立体声等)
signed - 表示数据是签名还是无符号
bigEndian - 指示单个样本的数据是否以大字节顺序存储( false表示小端)
*/
public static AudioFormat getAudioFormat(AudioFormat audioFormat) {
audioFormat=new AudioFormat(16000F, 16, 1,true,false);
// true,false 指示是以 big-endian 顺序还是以 little-endian 顺序存储音频数据。
return audioFormat;//构造具有线性 PCM 编码和给定参数的 AudioFormat。
}
}
# 6、放置讯飞开放平台下载Linux/Windows中所带的资源 图-1
# 7、 Linux代码基本类似,注意加载SO库就行和放置资源
package com.iflytek.service;
import com.iflytek.OfflineIvwMain;
import com.iflytek.util.Step2_ivw_ntf_handler;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
public interface Step1_ivw_so extends Library {
/**
* 重点:
* 1.char * 对应 String
* 2.int * 对应 IntByReference
* 3.void * 对应 Pointer
* 4.int 对应 int
* 5.无参 对应 无参
* 6.回调函数 对应 根据文档自定义回调函数,实现接口Callback
* 7.Linux设置程序共享库位置,否则会话时会报错25000
* export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/root/SoIvw/res
* 注意:每个版本都有两个so库,libmsc.so、libw_ivw.so
*/
//加载so动态库并实例化,从而使用其内部的方法
Step1_ivw_so INSTANCE = (Step1_ivw_so) Native.loadLibrary
(OfflineIvwMain.userPath+"res/libmsc.so", Step1_ivw_so.class);
//定义登录方法 MSPLogin(const char *usr, const char *pwd, const char *params)
public int MSPLogin(String usr, String pwd, String params);
//定义开始方法 QIVWSessionbegin(const char *grammarList, const char *params, int *errorCode)
public String QIVWSessionBegin(String grammarList, String params, IntByReference errorCode);
//定义写音频方法 QIVWAudioWrite(const char *sessionID, const void *audioData, unsigned int audioLen, int audioStatus)
public int QIVWAudioWrite(String sessionID, byte[] audioData, int audioLen, int audioStatus);
//定义获取结果方法 QIVWRegisterNotify(const char *sessionID, ivw_ntf_handler msgProcCb, void *userData)
public int QIVWRegisterNotify(String sessionID, Step2_ivw_ntf_handler msgProcCb, Pointer userData);
//定义结束方法 QIVWSessionEnd(const char *sessionID, const char *hints)
public int QIVWSessionEnd(String sessionID, String hints);
//定义退出方法 MSPLogout()
public int MSPLogout();
}
4、讯飞离线命令词识别Windows/Linux
# 1、离线命令词识别调用主函数
package com.iflytek;
import com.iflytek.util.Step2_asr_thread;
import com.iflytek.util.Step3_audioFormat;
import java.util.Scanner;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
/**
* 请注意!!!
* 1.首先到控制台https://console.xfyun.cn/services/mlcsb下载离线命令词识别的Windows MSC。
* 2.下载的离线命令词识别Windows MSC解压后,把bin目录下msc文件夹、dll文件、call.bnf文件拷贝到res目录下。
* 3.最后请替换Step2_ars_thread中的appid值,appid值在下载页面控制台可以看到。
*/
public class OfflineAsrMain {
//录音相关参数
public static AudioFormat audioFormat;
public static TargetDataLine targetDataLine;
public static void main(String[] args) throws Exception {
System.out.println("y开始体验离线命令词识别,n结束离线命令词识别");
Scanner input = new Scanner(System.in);
String inputContent = input.next();
long startTime = System.currentTimeMillis();
if(inputContent.equals("y")){
audioFormat = Step3_audioFormat.getAudioFormat(audioFormat);//构造具有线性 PCM 编码和给定参数的 AudioFormat。
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
Step2_asr_thread myThread=new Step2_asr_thread();
myThread.start();
Scanner input_2 = new Scanner(System.in);
String inputContent_2 = input_2.next();
if(inputContent_2.equals("n")){
targetDataLine.stop();
targetDataLine.close();
}
System.out.println("离线命令词识别操作持续:"+(System.currentTimeMillis()-startTime)/1000+"秒!");
System.exit(0);
}
}
}
# 2、根据MSC文档用JAVA重写方法
package com.iflytek.service;
import com.iflytek.util.Step1_grammar_callback;
import com.iflytek.util.Step1_lexicon_callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
public interface Step1_asr_dll extends Library {
/**
* 重点:
* 1.char * 对应 String
* 2.int * 对应 IntByReference
* 3.void * 对应 byte[],回调函数里此类型也可以用String来对应。
* 4.int 对应 int
* 5.无参 对应 void
* 6.回调函数 对应 根据文档自定义回调函数,实现接口Callback
*/
//加载dll动态库并实例化,从而使用其内部的方法
Step1_asr_dll INSTANCE = (Step1_asr_dll) Native.loadLibrary
("res/msc_x64.dll", Step1_asr_dll.class);
//定义登录方法
public int MSPLogin(String usr, String pwd, String params);
//开始一次语音识别。
public String QISRSessionBegin(String grammarList,String params,IntByReference errorCode);
//写入本次识别的音频
public int QISRAudioWrite(String sessionID, byte[] byteArrayAudioData,int waveLen,int audioStatus, IntByReference epStatus,IntByReference recogStatus);
//获取识别结果。
public String QISRGetResult(String sessionID,IntByReference rsltStatus,int waitTime, IntByReference errorCode);
//结束本次语音识别。
public int QISRSessionEnd(String sessionID,String hints);
//获取当次语音识别信息,如上行流量、下行流量等
public int QISRGetParam(String sessionID,String paramName,String paramValue, IntByReference valueLen);
//构建语法,生成语法ID。有回调
public int QISRBuildGrammar(String grammarType, String grammarContent, int grammarLength, String params, Step1_grammar_callback grammarCallBack, byte[] userData);
//更新本地语法词典。有回调
public int QISRUpdateLexicon(String lexiconName, String lexiconContent, int lexiconLength, String params, Step1_lexicon_callback lexiconCallback, byte[] userData);
//定义退出方法
public int MSPLogout();
}
# 3、 构建语法回调函数
package com.iflytek.util;
import com.sun.jna.Callback;
public class Step1_grammar_callback implements Callback {
//根据文档写回调方法
public int build_grm_cb(int errorCode,String info, String userData) {
System.out.println("构建语法返回的ID信息...:"+info+"错误码"+errorCode+"\n");
return 0;
}
}
# 4、更新词典回调函数
package com.iflytek.util;
import com.sun.jna.Callback;
public class Step1_lexicon_callback implements Callback {
//根据文档写回调方法
public int LexiconCallBack(int errorCode, String info, String userData) {
System.out.println("更新词典返回的信息...:"+info+"错误码:"+errorCode+"\n");
return 0;
}
}
# 5、把调用步骤依次写进线程类
package com.iflytek.util;
import com.iflytek.OfflineAsrMain;
import com.iflytek.service.Step1_asr_dll;
import com.sun.jna.ptr.IntByReference;
import javax.sound.sampled.AudioInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
public class Step2_asr_thread extends Thread{
public void run() {
while (true) {
//登录参数
String login_config = "appid = 替换你的appid, work_dir = ./res";
//第一个参数为用户名,第二个参数为密码,传null即可
int ret = Step1_asr_dll.INSTANCE.MSPLogin(null, null, login_config);
if (ret != 0) {//登录成功标志ret为0
System.out.println("登录失败...请检查"+ret);
System.exit(1);
} else {
//System.out.println("请注意,离线命令词call.bnf可以根据语法自定义...");
}
//读取call.bnf的内容
FileInputStream fileInputStream = null;
String grammarContent = "";
try {
fileInputStream = new FileInputStream("./res/call.bnf");
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, "UTF-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String temp = "";
while ((temp = bufferedReader.readLine()) != null) {
grammarContent = grammarContent + temp + "\n";
}
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println("语法内容:\n" + grammarContent);
//构建语法
Step1_grammar_callback step2_grammar_callback = new Step1_grammar_callback();
String params = "engine_type = local,asr_res_path = fo|res/asr/common.jet,sample_rate = 16000,grm_build_path = res/asr/GrmBuilld_x64";
ret = Step1_asr_dll.INSTANCE.QISRBuildGrammar("bnf", grammarContent, grammarContent.getBytes().length, params, step2_grammar_callback, null);
if (ret == 0) {
//System.out.println("构建语法成功...");
} else {
System.out.println("构建语法失败:" + ret);
}
//更新本地语法词典
/*String lexiconName = "enter";
String lex_content = "登录软件";
String lexParams = "engine_type=local,asr_res_path = fo|res/asr/common.jet, sample_rate = 16000,grm_build_path =res/asr/GrmBuilld_x64, grammar_list =call";
Step3_lexicon_callback step3_lexicon_callback = new Step3_lexicon_callback();
ret = Step1_asr_dll.INSTANCE.QISRUpdateLexicon(lexiconName, lex_content, lex_content.getBytes().length, lexParams, step3_lexicon_callback, null);
if (ret == 0) {
//System.out.println("更新本地语法词典成功...");
} else {
System.out.println("更新本地语法词典失败:" + ret);
}*/
//开始一次语音识别
IntByReference errorCode = new IntByReference(-100);
params = "vad_bos =4000 ,vad_eos = 10000,engine_type = local,asr_res_path = fo|res/asr/common.jet, sample_rate = 16000,grm_build_path = res/asr/GrmBuilld_x64, local_grammar = call,result_type = json, result_encoding = UTF8";
String session_id = Step1_asr_dll.INSTANCE.QISRSessionBegin(null, params, errorCode);
if (errorCode.getValue() == 0) {//只有返回0为函数调用成功
System.out.println("本次会话开启成功...,会话id为:" + session_id);
} else {
System.out.println("本次会话开启失败:" + errorCode.getValue());
}
//写入识别的音频
try {
//给构建语法、更新本地语法词典点时间
Thread.sleep(100);
System.err.println("\033[43;34;4m" + "请开始说出命令..." + "\033[0m");
//间隔400ms给用户反映时间
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
IntByReference epStatus = new IntByReference(-100);
IntByReference recogStatus = new IntByReference(-100);
//16k采样率的16位音频,一帧的大小为640Byte(来自Windows SDK的说明)
int frameSize = 640;
//用来告知MSC音频发送是否完成
int audioStatus = 1;
//反复调用QISRAudioWrite写音频方法,直到音频写完为止
//long startTime=System.currentTimeMillis();
try {
final byte[] audioDataByteArray = new byte[frameSize];
while (true) {
OfflineAsrMain.targetDataLine.open(OfflineAsrMain.audioFormat);
OfflineAsrMain.targetDataLine.start();
int len = new AudioInputStream(OfflineAsrMain.targetDataLine).read(audioDataByteArray);
//long endTime=System.currentTimeMillis();//||endTime-startTime>60000
if (len == -1 || epStatus.getValue() == 3) {//调用麦克风时候,len == -1将不会被执行...
//||(endTime-startTime>60*1000)
audioStatus = 4;//最后帧
ret = Step1_asr_dll.INSTANCE.QISRAudioWrite(session_id, audioDataByteArray, len, audioStatus, epStatus, recogStatus);
if (ret == 0) {
//System.out.println("最后一帧音频写入正常");
} else {
System.out.println("最后一帧音频写入异常:" + ret);
}
Thread.sleep(1500);
break; //文件读完,跳出循环
} else {
//反复调用
ret = Step1_asr_dll.INSTANCE.QISRAudioWrite(session_id, audioDataByteArray, len, audioStatus, epStatus, recogStatus);
}
audioStatus = 2;//中间帧
if (ret == 0) {
//System.out.println("音频写入正常");
} else {
System.out.println("音频写入异常:" + ret);
}
// 如果是实时采集音频,可以省略此操作。5KB 大小的16KPCM 持续的时间是160 毫秒
//Thread.sleep(160);
}
} catch (Exception e) {
e.printStackTrace();
}
//获取识别结果
IntByReference rsltStatus = new IntByReference(-100);
String grammarResult = Step1_asr_dll.INSTANCE.QISRGetResult(session_id, rsltStatus, 0, errorCode);
if(grammarResult!=null){
System.out.println("命令词识别结果:" + grammarResult);
}
//终止会话
ret = Step1_asr_dll.INSTANCE.QISRSessionEnd(session_id, "正常终止");
if (ret == 0) {
//System.out.println("本次会话正常终止...");
} else {
System.out.println("本次会话异常终止...");
}
//执行退出
ret = Step1_asr_dll.INSTANCE.MSPLogout();
if (ret == 0) {
//System.out.println("正常退出...");
} else {
System.out.println("异常退出...");
}
}
}
}
# 6、录音生成16K、16BIT单声道的音频流
package com.iflytek.util;
import javax.sound.sampled.AudioFormat;
public class Step3_audioFormat {
//构造线程参数
//16k采样率的16bit音频,一帧的大小为640B, 时长20ms
/**
sampleRate - 每秒样品数
sampleSizeInBits - 每个样本中的位数
channels - 通道数(1为mono,2为立体声等)
signed - 表示数据是签名还是无符号
bigEndian - 指示单个样本的数据是否以大字节顺序存储( false表示小端)
*/
public static AudioFormat getAudioFormat(AudioFormat audioFormat) {
audioFormat=new AudioFormat(16000F, 16,
1,true,false);
// true,false 指示是以 big-endian 顺序还是以 little-endian 顺序存储音频数据。
return audioFormat;//构造具有线性 PCM 编码和给定参数的 AudioFormat。
}
}
# 7、放置讯飞开放平台下载Linux/Windows中所带的资源,图-1
# 8、Linux代码基本类似,注意加载SO库就行和放置资源
package com.iflytek.service;
import com.iflytek.OfflineAsrMain;
import com.iflytek.util.Step1_grammar_callback;
import com.iflytek.util.Step1_lexicon_callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.ptr.IntByReference;
public interface Step1_asr_so extends Library {
/**
* 重点:
* 1.char * 对应 String
* 2.int * 对应 IntByReference
* 3.void * 对应 byte[],回调函数里此类型需用String来对应。
* 4.int 对应 int
* 5.无参 对应 void
* 6.回调函数 对应 根据文档自定义回调函数,实现接口Callback
*/
//加载so动态库并实例化,从而使用其内部的方法
Step1_asr_so INSTANCE = (Step1_asr_so) Native.loadLibrary
(OfflineAsrMain.userPath+"res/libmsc.so", Step1_asr_so.class);
//定义登录方法
public int MSPLogin(String usr, String pwd, String params);
//开始一次语音识别。
public String QISRSessionBegin(String grammarList,String params,IntByReference errorCode);
//写入本次识别的音频
public int QISRAudioWrite(String sessionID, byte[] byteArrayAudioData,int waveLen,int audioStatus, IntByReference epStatus,IntByReference recogStatus);
//获取识别结果。
public String QISRGetResult(String sessionID,IntByReference rsltStatus,int waitTime, IntByReference errorCode);
//结束本次语音识别。
public int QISRSessionEnd(String sessionID,String hints);
//获取当次语音识别信息,如上行流量、下行流量等
public int QISRGetParam(String sessionID,String paramName,String paramValue, IntByReference valueLen);
//构建语法,生成语法ID。有回调
public int QISRBuildGrammar(String grammarType, String grammarContent, int grammarLength, String params, Step1_grammar_callback grammarCallBack, byte[] userData);
//更新本地语法词典。有回调
public int QISRUpdateLexicon(String lexiconName, String lexiconContent, int lexiconLength, String params, Step1_lexicon_callback lexiconCallback, byte[] userData);
//定义退出方法
public int MSPLogout();
}
5、顺丰运单查询
1、目的:批量调用顺丰运单路由接口,更新最新物流状态
【1】到顺丰开放平台-服务与支持-开放规范-API-SDK使用-直接下载SDK
【2】开通正式接口必须要完成沙盒的3次成功调用。
2、这里有个坑,沙盒环境也必须替换正式顾客编码和校验码
【1】如果不替换,永远也通不过测试,无法开通正式环境。
3、需要上传电子面单
【1】这里可以在帮助中心提问,跳过审核
4、成功调用后就需要对返回的json数据进行解析
【1】这里又有一个坑,就是json不太规范,需要写两个json解析类,分两次解析
【2】详析代码如下:
class JsonParse{
public String apiResultData;
}
class JsonParse2{
public MsgData msgData;
}
class MsgData{
List<RouteResps> routeResps;
}
class RouteResps{
public String mailNo;
List<Routes> routes;
}
class Routes{
public String acceptTime;
public String remark;
public int opCode;
}
JsonParse jsonParse=gson.fromJson(result,JsonParse.class);
System.out.println("===返回结果:" +jsonParse.apiResultData);
JsonParse2 jsonParse2=gson.fromJson(jsonParse.apiResultData,JsonParse2.class);
5、需要结合官方文档,来判断opCode的值对应的状态,图-1
6、接口调用限制每次传10个运单,但是我每次要查2000多个。
【1】所以用循环加逻辑判断来分批调用顺丰的接口
@RequestMapping("updateAhSfTableBySfInterface")
public ModelAndView updateAhSfTableBySfInterface(AhSfTable ahSfTable) throws Exception{
ModelAndView mv=new ModelAndView();
List<AhSfTable> tempAhSfTableList=bigMapper.selectAhSfTableByLastLogisticsStatus();
if(tempAhSfTableList.size()>0){
int needWorkTimes=0;
List<SfInterface> sfInterfaceList=new ArrayList<SfInterface>();
if(tempAhSfTableList.size()%10==0){
//说明刚好可以调整数次接口
needWorkTimes=tempAhSfTableList.size()/10;
//开始分批调用
for(int i=0;i<needWorkTimes;i++){
String tempJsonNeed="";
for(int j=10*i;j<10*(i+1);j++){
tempJsonNeed=tempJsonNeed+"\""+tempAhSfTableList.get(j).getBillCode()+"\",";
}
tempJsonNeed="["+tempJsonNeed.substring(0,tempJsonNeed.length()-1)+"]";
sfInterfaceList=CallExpressAPIUtil.doSfInterfaceQuery(sfInterfaceList,tempJsonNeed);
}
}else{
//说明有剩余,设置为+1次
needWorkTimes=tempAhSfTableList.size()/10+1;
//开始分批调用
for(int i=0;i<needWorkTimes;i++){
String tempJsonNeed="";
for(int j=10*i;j<10*(i+1);j++){
//最后一次会越界,所以加个判断
if(j<tempAhSfTableList.size()){
tempJsonNeed=tempJsonNeed+"\""+tempAhSfTableList.get(j).getBillCode()+"\",";
}
}
tempJsonNeed="["+tempJsonNeed.substring(0,tempJsonNeed.length()-1)+"]";
sfInterfaceList=CallExpressAPIUtil.doSfInterfaceQuery(sfInterfaceList,tempJsonNeed);
}
}
//System.out.println(sfInterfaceList.size());
bigMapper.updateAhSfTableLastLogisticsStatusByBillCode(sfInterfaceList);
/* for(int i=0;i<sfInterfaceList.size();i++){
System.out.println(sfInterfaceList.get(i).getLastLogisticsStatus());
}*/
}
ahSfTable.setOperateResultInfo("更新完成...");
mv.addObject("ahSfTable",ahSfTable);
return mv;
}