前言:
语音评测(SpeechEvaluator):
通过智能语音技术自动对发音水平进行评价、发音错误、缺陷进行定位和问题分析。目前评音评测提供汉语、英语两种语言的评测,支持单字(汉语专有)、词语 和句子朗读三种题型。
public class IseActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "IseActivity";
private EditText mEvaTextEditText;
private EditText mResultEditText;
private Button mIseStartButton;
private Button iseLanguages, iseTopic, iseResultLevel;
private EditText iseStartTime, iseEndTime, iseTime;
private String mLastResult;
private SpeechEvaluator mIse;
//切换(汉语、句子、等级)
private String languageType = "zh_cn", topicType = "read_sentence", resultLevelType = "complete";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ise);
mIse = SpeechEvaluator.createEvaluator(this, null);
initUI();
setEvaText();
}
private void initUI() {
mEvaTextEditText = this.findViewById(R.id.ise_eva_text);
mResultEditText = this.findViewById(R.id.ise_result_text);
mIseStartButton = this.findViewById(R.id.ise_start);
iseLanguages = this.findViewById(R.id.ise_languages);
iseTopic = this.findViewById(R.id.ise_topic);
iseResultLevel = this.findViewById(R.id.ise_result_level);
iseStartTime = this.findViewById(R.id.ise_start_time);
iseEndTime = this.findViewById(R.id.ise_end_time);
iseTime = this.findViewById(R.id.ise_time);
mIseStartButton.setOnClickListener(this);
iseLanguages.setOnClickListener(this);
iseTopic.setOnClickListener(this);
iseResultLevel.setOnClickListener(this);
findViewById(R.id.ise_parse).setOnClickListener(this);
findViewById(R.id.ise_stop).setOnClickListener(this);
findViewById(R.id.ise_cancel).setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (null == mIse) {
// 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
showToast("创建对象失败,请确认 libmsc.so 放置正确,且有调用 createUtility 进行初始化");
return;
}
switch (view.getId()) {
case R.id.ise_start:
if (mIse == null) {
return;
}
String evaText = mEvaTextEditText.getText().toString();
mLastResult = null;
mResultEditText.setText("");
mResultEditText.setHint("请朗读以上内容");
mIseStartButton.setEnabled(false);
setParams();
int ret = mIse.startEvaluating(evaText, null, mEvaluatorListener);
//以下方法为通过直接写入音频的方式进行评测业务
/*if (ret != ErrorCode.SUCCESS) {
showTip("识别失败,错误码:" + ret);
} else {
showTip(getString(R.string.text_begin_ise));
byte[] audioData = FucUtil.readAudioFile(IseDemo.this,"isetest.wav");
if(audioData != null) {
//防止写入音频过早导致失败
try{
new Thread().sleep(100);
}catch (InterruptedException e) {
Log.d(TAG,"InterruptedException :"+e);
}
mIse.writeAudio(audioData,0,audioData.length);
mIse.stopEvaluating();
}
}*/
break;
case R.id.ise_parse:
// 解析最终结果
if (!TextUtils.isEmpty(mLastResult)) {
XmlResultParser resultParser = new XmlResultParser();
Result result = resultParser.parse(mLastResult);
if (null != result) {
mResultEditText.setText(result.toString());
} else {
showToast("解析结果为空");
}
}
break;
case R.id.ise_stop:
if (mIse.isEvaluating()) {
mResultEditText.setHint("评测已停止,等待结果中...");
mIse.stopEvaluating();
}
break;
case R.id.ise_cancel:
mIse.cancel();
mIseStartButton.setEnabled(true);
mResultEditText.setText("");
mResultEditText.setHint("请点击“开始评测”按钮");
mLastResult = null;
break;
case R.id.ise_languages:
if (languageType.equals("zh_cn")) {
languageType = "en_us";
iseLanguages.setText("语种:英语");
} else {
languageType = "zh_cn";
iseLanguages.setText("语种:汉语");
}
setEvaText();
break;
case R.id.ise_topic:
if (topicType.equals("read_sentence")) {
topicType = "read_word";
iseTopic.setText("题型:词语");
} else if (topicType.equals("read_word")) {
topicType = "read_syllable";
iseTopic.setText("题型:单词");
} else {
topicType = "read_sentence";
iseTopic.setText("题型:句子");
}
setEvaText();
break;
case R.id.ise_result_level:
if (resultLevelType.equals("complete")) {
resultLevelType = "plain";
iseResultLevel.setText("等级:plain");
} else {
resultLevelType = "complete";
iseResultLevel.setText("等级:complete");
}
setEvaText();
break;
default:
}
}
// 设置评测试题
private void setEvaText() {
String text = "";
if ("en_us".equals(languageType)) {
if ("read_word".equals(topicType)) {
text = getString(R.string.text_en_word);
} else if ("read_sentence".equals(topicType)) {
text = getString(R.string.text_en_sentence);
}
} else {
// 中文评测
if ("read_syllable".equals(topicType)) {
text = getString(R.string.text_cn_syllable);
} else if ("read_word".equals(topicType)) {
text = getString(R.string.text_cn_word);
} else if ("read_sentence".equals(topicType)) {
text = getString(R.string.text_cn_sentence);
}
}
mEvaTextEditText.setText(text);
mResultEditText.setText("");
mLastResult = null;
mResultEditText.setHint("请点击“开始评测”按钮");
}
private void setParams() {
// 设置评测语言
mIse.setParameter(SpeechConstant.LANGUAGE, languageType);
// 设置需要评测的类型
mIse.setParameter(SpeechConstant.ISE_CATEGORY, topicType);
// 设置结果等级(中文仅支持complete)
mIse.setParameter(SpeechConstant.RESULT_LEVEL, resultLevelType);
mIse.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
// 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
mIse.setParameter(SpeechConstant.VAD_BOS, iseStartTime.getText().toString());
// 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
mIse.setParameter(SpeechConstant.VAD_EOS, iseEndTime.getText().toString());
// 语音输入超时时间,即用户最多可以连续说多长时间;(-1无超时)
mIse.setParameter(SpeechConstant.KEY_SPEECH_TIMEOUT, iseTime.getText().toString());
mIse.setParameter(SpeechConstant.AUDIO_FORMAT_AUE, "opus");
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
mIse.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
mIse.setParameter(SpeechConstant.ISE_AUDIO_PATH, Environment.getExternalStorageDirectory().getAbsolutePath() + "/msc/helloword_ise.wav");
//通过writeaudio方式直接写入音频时才需要此设置
//mIse.setParameter(SpeechConstant.AUDIO_SOURCE,"-1");
}
// 评测监听接口
private EvaluatorListener mEvaluatorListener = new EvaluatorListener() {
@Override
public void onResult(EvaluatorResult result, boolean isLast) {
Log.e(TAG, "evaluator result :" + isLast);
if (isLast) {
StringBuilder builder = new StringBuilder();
builder.append(result.getResultString());
if (!TextUtils.isEmpty(builder)) {
mResultEditText.setText(builder.toString());
}
mIseStartButton.setEnabled(true);
mLastResult = builder.toString();
showToast("评测结束");
}
}
@Override
public void onError(SpeechError error) {
mIseStartButton.setEnabled(true);
if (error != null) {
showToast("error:" + error.getErrorCode() + "," + error.getErrorDescription());
mResultEditText.setText("");
mResultEditText.setHint("请点击“开始评测”按钮");
} else {
Log.e(TAG, "evaluator over");
}
}
@Override
public void onBeginOfSpeech() {
// 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
Log.e(TAG, "onBeginOfSpeech: evaluator begin");
}
@Override
public void onEndOfSpeech() {
// 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
Log.e(TAG, "onEndOfSpeech: evaluator stoped");
}
@Override
public void onVolumeChanged(int volume, byte[] data) {
showToast("当前音量:" + volume);
Log.e(TAG, "onVolumeChanged: 返回音频数据" + data.length);
}
@Override
public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
// 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
// if (SpeechEvent.EVENT_SESSION_ID == eventType) {
// String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
// Log.d(TAG, "session id =" + sid);
// }
}
};
/**
* 展示吐司
*/
private void showToast(final String str) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
@Override
protected void onResume() {
// 开放统计 移动数据统计分析
//FlowerCollector.onResume(IseDemo.this);
//FlowerCollector.onPageStart(TAG);
super.onResume();
}
@Override
protected void onPause() {
// 开放统计 移动数据统计分析
//FlowerCollector.onPageEnd(TAG);
//FlowerCollector.onPause(IseDemo.this);
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (null != mIse) {
mIse.destroy();
mIse = null;
}
}
}
布局文件activity_ise.xml:
2、工具类XmlResultParser.java(Demo中也有):
public class XmlResultParser {
public Result parse(String xml) {
if (TextUtils.isEmpty(xml)) {
return null;
}
XmlPullParser pullParser = Xml.newPullParser();
try {
InputStream ins = new ByteArrayInputStream(xml.getBytes());
pullParser.setInput(ins, "utf-8");
FinalResult finalResult = null;
int eventType = pullParser.getEventType();
while (XmlPullParser.END_DOCUMENT != eventType) {
switch (eventType) {
case XmlPullParser.START_TAG:
if ("FinalResult".equals(pullParser.getName())) {
// 只有一个总分的结果
finalResult = new FinalResult();
} else if ("ret".equals(pullParser.getName())) {
finalResult.ret = getInt(pullParser, "value");
} else if ("total_score".equals(pullParser.getName())) {
finalResult.total_score = getFloat(pullParser, "value");
} else if ("xml_result".equals(pullParser.getName())) {
// 详细结果
return parseResult(pullParser);
}
break;
case XmlPullParser.END_TAG:
if ("FinalResult".equals(pullParser.getName())) {
return finalResult;
}
break;
default:
break;
}
eventType = pullParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private Result parseResult(XmlPullParser pullParser) {
Result result = null;
// 标签是否已扫描到
boolean rec_paperPassed = false;
Sentence sentence = null;
Word word = null;
Syll syll = null;
Phone phone = null;
int eventType;
try {
eventType = pullParser.getEventType();
while (XmlPullParser.END_DOCUMENT != eventType) {
switch (eventType) {
case XmlPullParser.START_TAG:
if ("rec_paper".equals(pullParser.getName())) {
rec_paperPassed = true;
} else if ("read_syllable".equals(pullParser.getName())) {
if (!rec_paperPassed) {
result = new ReadSyllableResult();
} else {
readTotalResult(result, pullParser);
}
} else if ("read_word".equals(pullParser.getName())) {
if (!rec_paperPassed) {
result = new ReadWordResult();
String lan = getLanguage(pullParser);
result.language = (null == lan) ? "cn" : lan;
} else {
readTotalResult(result, pullParser);
}
} else if ("read_sentence".equals(pullParser.getName()) ||
"read_chapter".equals(pullParser.getName())) {
if (!rec_paperPassed) {
result = new ReadSentenceResult();
String lan = getLanguage(pullParser);
result.language = (null == lan) ? "cn" : lan;
} else {
readTotalResult(result, pullParser);
}
} else if ("sentence".equals(pullParser.getName())) {
if (null == result.sentences) {
result.sentences = new ArrayList();
}
sentence = createSentence(pullParser);
} else if ("word".equals(pullParser.getName())) {
if (null != sentence && null == sentence.words) {
sentence.words = new ArrayList();
}
word = createWord(pullParser);
} else if ("syll".equals(pullParser.getName())) {
if (null != word && null == word.sylls) {
word.sylls = new ArrayList();
}
syll = createSyll(pullParser);
} else if ("phone".equals(pullParser.getName())) {
if (null != syll && null == syll.phones) {
syll.phones = new ArrayList();
}
phone = createPhone(pullParser);
}
break;
case XmlPullParser.END_TAG:
if ("phone".equals(pullParser.getName())) {
syll.phones.add(phone);
} else if ("syll".equals(pullParser.getName())) {
word.sylls.add(syll);
} else if ("word".equals(pullParser.getName())) {
sentence.words.add(word);
} else if ("sentence".equals(pullParser.getName())) {
result.sentences.add(sentence);
} else if ("read_syllable".equals(pullParser.getName())
|| "read_word".equals(pullParser.getName())
|| "read_sentence".equals(pullParser.getName())) {
return result;
}
break;
default:
break;
}
eventType = pullParser.next();
}
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private void readTotalResult(Result result, XmlPullParser pullParser) {
result.beg_pos = getInt(pullParser, "beg_pos");
result.end_pos = getInt(pullParser, "end_pos");
result.content = getContent(pullParser);
result.total_score = getFloat(pullParser, "total_score");
result.time_len = getInt(pullParser, "time_len");
result.except_info = getExceptInfo(pullParser);
result.is_rejected = getIsRejected(pullParser);
}
private Phone createPhone(XmlPullParser pullParser) {
Phone phone;
phone = new Phone();
phone.beg_pos = getInt(pullParser, "beg_pos");
phone.end_pos = getInt(pullParser, "end_pos");
phone.content = getContent(pullParser);
phone.dp_message = getInt(pullParser, "dp_message");
phone.time_len = getInt(pullParser, "time_len");
return phone;
}
private Syll createSyll(XmlPullParser pullParser) {
Syll syll;
syll = new Syll();
syll.beg_pos = getInt(pullParser, "beg_pos");
syll.end_pos = getInt(pullParser, "end_pos");
syll.content = getContent(pullParser);
syll.symbol = getSymbol(pullParser);
syll.dp_message = getInt(pullParser, "dp_message");
syll.time_len = getInt(pullParser, "time_len");
return syll;
}
private Word createWord(XmlPullParser pullParser) {
Word word;
word = new Word();
word.beg_pos = getInt(pullParser, "beg_pos");
word.end_pos = getInt(pullParser, "end_pos");
word.content = getContent(pullParser);
word.symbol = getSymbol(pullParser);
word.time_len = getInt(pullParser, "time_len");
word.dp_message = getInt(pullParser, "dp_message");
word.total_score = getFloat(pullParser, "total_score");
word.global_index = getInt(pullParser, "global_index");
word.index = getInt(pullParser, "index");
return word;
}
private Sentence createSentence(XmlPullParser pullParser) {
Sentence sentence;
sentence = new Sentence();
sentence.beg_pos = getInt(pullParser, "beg_pos");
sentence.end_pos = getInt(pullParser, "end_pos");
sentence.content = getContent(pullParser);
sentence.time_len = getInt(pullParser, "time_len");
sentence.index = getInt(pullParser, "index");
sentence.word_count = getInt(pullParser, "word_count");
return sentence;
}
private String getLanguage(XmlPullParser pullParser) {
return pullParser.getAttributeValue(null, "lan");
}
private String getExceptInfo(XmlPullParser pullParser) {
return pullParser.getAttributeValue(null, "except_info");
}
private boolean getIsRejected(XmlPullParser pullParser) {
String isRejected = pullParser.getAttributeValue(null, "is_rejected");
if (null == isRejected) {
return false;
}
return Boolean.parseBoolean(isRejected);
}
private String getSymbol(XmlPullParser pullParser) {
return pullParser.getAttributeValue(null, "symbol");
}
private float getFloat(XmlPullParser pullParser, String attrName) {
String val = pullParser.getAttributeValue(null, attrName);
if (null == val) {
return 0f;
}
return Float.parseFloat(val);
}
private String getContent(XmlPullParser pullParser) {
return pullParser.getAttributeValue(null, "content");
}
private int getInt(XmlPullParser pullParser, String attrName) {
String val = pullParser.getAttributeValue(null, attrName);
if (null == val) {
return 0;
}
return Integer.parseInt(val);
}
}
3、使用到的strings.xml:
"[word]\napple\nbanana\norange"
"The quick brown fox jumps over the lazy dog."
"知,痴,是"
"磁铁,率领,脆弱,动手,古筝"
"一座座雪峰插入云霄,峰顶银光闪闪,大大小小的湖泊,像颗颗宝石镶嵌在彩带般的沟谷中。"