JS SDK开发文档

更新时间:

快速接入

1、SDK说明

jssdk通过使用Web Audio API完成在浏览器上的录音。然后将录制的原始音频流进行wav格式封装。在封装之后进行speex压缩以及ogg格式的重新封装,最终将ogg格式的采样率为16000的音频通过WebSocket流式传输到引擎,进行测评。

2、引入方式

2.1 引入js

需要在页面初始化时通过script引入engine.js.

engine.js文件,引入根目录,例如可放入 sdk 文件夹下。

<script type="text/javascript" src="sdk/engine.js"></script>

engine.jsEngineEvaluat是挂载在window对象上的。

2.2 完整引入代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title></title>
    <meta name="Description" content="" />
    <link href="static/css/bootstrap.min.css" rel="stylesheet" type="text/css">
</head>
<body>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
<div>

</div>

<script type="text/javascript" src="sdk/engine.js"></script>

</body>
</html>

3、快速测评流程

3.1 完整调用代码示例

需要改成为改成自己的app id,userId,以及下面的warrantId 鉴权url

<!-- 引入下方的js代码 -->
<script type="text/javascript" src="main.js"></script>

main.js源码

// TODO : 改成自己的 applicationId,userId 以及 warrantId
const applicationId = '';
const userId = '';
let warrantId = '';

var myRecord = new EngineEvaluat({

	applicationId: applicationId,

	userId : userId,	

	micAllowCallback: function() {
		console.log('麦克风已经允许');
	},
	micForbidCallback: function() {
		console.log('麦克风没有被允许');
	},
	playAudioComplete: function(data) {
		console.log('播放完毕');
	},
	playAudioError:function(data){
		console.log('播放出错');
	},
	engineBackResultDone:function(msg){
        console.log('评测成功');
		myRecordResult(msg);
	},
	engineBackResultFail:function(msg){
		console.error('评测失败');
		console.log(msg);
	},
	engineFirstInitDone:function(){
		console.log('初始化成功');
	},
	micVolumeCallback: function(data) {
		console.log('录音音量大小为:' + data);
	},
	JSSDKNotSupport: function() {
		alert('jssdk  not support');
	},
	noNetwork: function() {
                console.warn('暂无网络');
	},
	
	audioDataCallback: function(data, isLast){
                //需要在开始测评参数配置  saveAudio : 1 ,此回调才会响应
                //如果在开始测评参数里配置 compress : 'raw',则会返回pcm格式的录音实时数据。否则会ogg格式的音频实时数据
		console.log(data);
	},

	 

});


function defaultTime(refText) {
	return 2000 + refText.trim().split(' ').length * 600;
}
console.warn('wwwwwww');
getWarrantId();

function getWarrantId(){
	var request = $.ajax({
    // TODO: 改成自己的鉴权url,user_id、applicationId
          url: "",
          type: "POST",
          data: {"user_id": "","applicationId": ""},
          success:function(data){
            console.log(data);
            warrantId = data.data.warrant_id;
          }
        });

}


$(document).ready(function(){
	var setTimeoutLoop = null,
		paused = false;
    //跟读句子
	$("#follow").bind("click", function() {
		recordInit();
		var coreType = getCoreType(),
			//times = evalTime(),
			params = {};
		// 评测类型;
		params.coreType = coreType;
		params.refText = getEvalText();
		params.warrantId = warrantId;

		if (params.refText !== '') {
			myRecord.startRecord(params);
			$("#score").text('录音中...');
			setTimeoutLoop = setTimeout(function(){
				$("#score").text('评测中...');
			},defaultTime(params.refText) + 1000)
		} else {
			alert('请填写评测内容');
		}
	});

    //停止录音
    $("#stop").bind("click", function(){
    	console.log('停止录音');
    	clearTimeout(setTimeoutLoop);
    	$("#score").text('评测中...');
    	myRecord.stopRecord();
    });

    //拖拽文件上传评测
    // $('#upload').on('change', function(evt) {
    // 	var params = {};
    // 		params.refText = getEvalText();
    // 		params.coreType = getCoreType();
    // 	myRecord.wholeFileUpload(evt, params);
    // });

    //播放音乐的事件
    $('#play').on('click', function() {
    	paused = false;
    	$('#pauseMusic').find('span').text('暂停');
    	myRecord.loadAudio(musicUrl);
    	myRecord.playAudio();    	
    });
    //获取音乐长度
    // $('#musicDuration').on('click', function() {
    // 	$('#duration').text(myRecord.getDuration());
    // });
    //暂停音乐播放
	$('#pauseMusic').on('click', function() {
		if (!paused) {
			paused = true;
			$(this).find('span').text('继续播放');
			myRecord.pauseAudio();
		} else {
			paused = false;
			$(this).find('span').text('暂停');
			myRecord.playAudio();
		}
	});
    // $('#continueMusic').on('click',function(){
    // 	myRecord.playAudio();
    // })
    //停止音乐的播放
    $('#stopMusic').on('click', function() {
    	myRecord.stopAudio();
    });

  //   $('#musicCurrentTime').on('click',function(){
		// $('#currentTime').text(myRecord.getCurrentTime());
  //   })

});
var musicUrl;
//录音结果详情等初始化方法
function recordInit(){
	$("#point").text('');
	$("#totalPoint").text('');
	$("#result").text('');
	$('#audioUrl').text('');
	$('#grade').html('');
}

// 获取评测文本;
function getEvalText() {
	return $.trim($('#eng-txt').val());
}

//获取评测时间
function evalTime(){
	var evalTimeChecked = $('#evalTime').find('input[name="evalTime"]:checked');
	if(evalTimeChecked.length > 0){
		var _val = evalTimeChecked.val();
		if(_val == 'null'){
			return null;
		}else{
			return parseInt(evalTimeChecked.val(), 10) * 1000;
		}
	}else{
		return false;
	}
}
//获取评测类型;
function getCoreType(){
	return $('#coreType').find('input[name="coreType"]:checked').val();
}

// // 评测成功回调;
// function engineSuc(msg) {
// 	myRecordResult(msg);
// }

// // 评测失败回调;
// function engineErr(msg) {
// 	console.log(msg);
// }

//获取评测数据的结果分析
var myRecordResult = function(result) {
	var recordResult = JSON.parse(result);
	var gradeInfo = '';
	var coreType = getCoreType();
	$("#point").text(recordResult.result.overall);
	$("#totalPoint").text(recordResult.result.rank);
	$("#result").text(JSON.stringify(recordResult));
	var wordPoint = recordResult.result.details;
	var url = 'https://files.cloud.ssapi.cn/' + recordResult.applicationId + '/' + recordResult.recordId;
	musicUrl = url + '.mp3';
	//myRecord.loadAudio(musicUrl);
	if(coreType == "en.word.score" || coreType == "en.sent.score"){
		wordPoint.forEach(function(item) {
			gradeInfo += wordLevel(item.score, item.char);
		})
	}else if(coreType == "en.pred.score"){
		wordPoint.forEach(function(item) {
			gradeInfo += wordLevel(item.score, item.char);
		})
	}
	else{
		wordPoint.forEach(function(item) {
			gradeInfo += wordLevel(item.score, item.char);
		})
	}

	$('#audioUrl').text(url);
	$('#grade').html(gradeInfo);
	$("#score").text('测评完毕');
}

var wordLevel = function(point, word){
	var coreType = getCoreType();
	var ref = $("#eng-txt").val();
	var exce = 85;
	var good = 75;
	var fine = 55;
	var exceColor = '<font color="#OOECOO">';
	var goodColor = '<font color="#OOE3EE">';
	var fineColor = '<font color="#5B5B5B">';
	var badColor = '<font color="#FF5151">';
	if(coreType == "en.word.score" || coreType == "en.sent.score" || coreType == "en.pred.score"){
		if (point >= exce){
			var level = ' ' + exceColor + word +'</font>';
		} else if(good <= point && point < exce){
			var level = ' ' + goodColor + word +'</font>';
		} else if(fine <= point && point < good){
			var level = ' ' + fineColor + word +'</font>';
		} else{
			var level = ' ' + badColor + word +'</font>';
		}
	}else{
		if (point >= exce){
			var level = exceColor + word +'</font>';
		} else if(good <= point && point < exce){
			var level = goodColor + word +'</font>';
		} else if(fine <= point && point < good){
			var level = fineColor + word +'</font>';
		} else{
			var level = badColor + word +'</font>';
		}
	}
	return level;
}

详细文档说明

1、JSSDK使用说明

1.1 简介

jssdk通过使用Web Audio Api完成在浏览器上的录音。然后将录制的原始音频流进行wav格式封装。在封装之后进行speex压缩以及ogg格式的重新封装,最终将ogg格式的采样率为16000的音频通过WebSocket流式传输到引擎,进行测评。

1.2 引入

需要在页面初始化时通过script引入engine.js.

1.3 浏览器兼容测试

操作系统

支持的浏览器最低版本

360

chrome

Firefox

猎豹

搜狗

QQ

win7

8.0.1.222

41.0.2272.76

46

5.9.109

7

9.3.1

winXP

8.0.1.222

43.0.2357.81

46

5.9.109

7

9.3.1

win10

8.0.1.222

43.0.2357.81

46

5.9.109

7

9.3.1

mac

MAC

43.0.2357.81

46

MAC

MAC

MAC

2、详细方法调用及参数配置

方案切换

如果想切换评测的方案,可以销毁 引擎对象,重新创建一个

测评流程

初始化引擎对象 -> 收到初始化成功回调 -> 执行开始评测方法 -> 执行停止评测方法 -> 等待测评结果回调

image.jpeg

2.1 初始化方法

const params = {
  // 应用id
  applicationId: "",
  //  用户id
  userId: "",

  warrantId: "",

  // SDK 回调
  micAllowCallback: function () {
    console.log("麦克风已经允许");
  },
  micForbidCallback: function () {
    console.log("麦克风没有被允许");
  },
  playAudioComplete: function (data) {
    console.log("播放完毕");
  },
  playAudioError: function (data) {
    console.log("播放出错");
  },
  engineBackResultDone: function (msg) {
    console.log("测评成功");
    myRecordResult(msg);
  },
  engineBackResultFail: function (msg) {
    console.error("测评失败");
    console.log(msg);
  },
  engineFirstInitDone: function () {
    console.log("初始化成功");
  },
  micVolumeCallback: function (data) {
    console.log("录音音量大小为:" + data);
  },
  JSSDKNotSupport: function () {
    alert("jssdk  not support");
  },
  noNetwork: function () {
    console.warn("暂无网络");
  },

  audioDataCallback: function (data, isLast) {
    //需要在开始测评参数配置  saveAudio : 1 ,此回调才会响应
    //如果在开始测评参数里配置 compress : 'raw',则会返回pcm格式的录音实时数据。否则会ogg格式的音频实时数据
    console.log(data);
  },
}
var myRecord = new window.EngineEvaluat(params)

params参数释义

名称

类型

说明

默认值

是否必填

applicationId

String

商户id(appkey)

customRecord

Boolean

是否要自定义录音

false

userId

String

用户ID

any

coreType

String

默认测评类型

en.sent.score

warrantId

String

测评鉴权ID

logIsOpen

Boolean

日志是否开启

true

coreTimeout

Number

连接服务器超时时间

10000

serverTimeout

Number

返回测评结果超时时间

10000

intermission

Number

最大空闲时间

10000

engineLinksAddress

Array

链接地址

['api.cloud.ssapi.cn', 'gate-01.api.cloud.ssapi.cn', 'gate-02.api.cloud.ssapi.cn', 'gate-03.api.cloud.ssapi.cn']

engineFirstInitDone

Function

引擎初始化成功回调

engineBackResultDone

Function

引擎返回结果成功回调

engineBackResultFail

Function

引擎返回结果失败回调

engineRequestIdCallback

Function

引擎返回每次测评对应的RequestId回调

micAllowCallback

Function

麦克风授权允许回调方法

micForbidCallback

Function

麦克风授权拒绝回调方法,收到此回调则不能进行录音测评

micVolumeCallback

Function

麦克风录音音量大小回调

JSSDKNotSupport

Function

sdk不支持当前浏览器回调

noNetwork

Function

暂无网络回调

playAudioComplete

Function

音频播放完成回调

playAudioError

Function

音频加载或播放出错回调

logAccept

Function

日志收集回调

audioDataCallback

Function

录音数据实时回调

2.2 评测方案 (1):通过SDK默认录音功能进行评测

2.2.1 开始录音方法

// TODO: 根据自己的题型,生成相应的params,详细题型,可参考题型模块文档 https://open.singsound.com/doc/engine?type=engine-en-en.word.score&detail=false
var params = { coreType: 'en.word.score', refText:'word', precision:0.5, evalTime:6000 };
// 更新warrantId,warrantId默认时间是120分钟,可不需要每次都更新
params.warrantId = warrantId;
// 完整参数
myRecord.startRecord(params,  done,  fail);

名称

类型

说明

默认值

是否必填

params

object

引擎开始测评参数 object类型 eg: { coreType: 'en.word.score', refText:'word', precision:0.5, evalTime:6000 }; evalTime:1、不填。不会自动停止录音 2、填。值必须是number类型,会按照参数值自动结束录音获取测评结果;

done

Function

引擎测评成功回调

engineBackResultDone

fail

Function

引擎测评失败回调

engineBackResultFail

2.2.2 停止录音方法

选择一种即可

2.3.1 手动停止

针对没有设置 evalTime 的可手动调用停止方法

myRecord.stopRecord();
2.3.2 设置 evalTime 自动停止

myRecord.startRecord(params)的params设置evalTime。evalTime的时长可参考以下方法

function defaultTime(refText) {
  return 2000 + refText.trim().split(" ").length * 600 + 1000;
}

停止录音后,等待测评结果回调

2.3.3 录音结果分析
engineBackResultDone: function (msg) {
    console.log("测评成功");
    myRecordResult(msg);
},
engineBackResultFail: function (msg) {
    console.error("测评失败");
    console.log(msg);
},

//获取测评数据的结果分析
var myRecordResult = function (result) {
  var recordResult = JSON.parse(result);
  var gradeInfo = "";
  // TODO:当前测评的 coreType
  var coreType = getCoreType();
  $("#point").text(recordResult.result.overall);
  $("#totalPoint").text(recordResult.result.rank);
  $("#result").text(JSON.stringify(recordResult));
  var wordPoint = recordResult.result.details;
  var url =
    "https://files.cloud.ssapi.cn/" +
    recordResult.applicationId +
    "/" +
    recordResult.recordId;
  musicUrl = url + ".mp3";
  //myRecord.loadAudio(musicUrl);
  if (coreType == "en.word.score" || coreType == "en.sent.score") {
    wordPoint.forEach(function (item) {
      gradeInfo += wordLevel(item.score, item.char);
    });
  } else if (coreType == "en.pred.score") {
    wordPoint.forEach(function (item) {
      gradeInfo += wordLevel(item.score, item.char);
    });
  } else {
    wordPoint.forEach(function (item) {
      gradeInfo += wordLevel(item.score, item.char);
    });
  }

  $("#audioUrl").text(url);
  $("#grade").html(gradeInfo);
  $("#score").text("测评完毕");
};

var wordLevel = function (point, word) {
  var coreType = getCoreType();
  var ref = $("#eng-txt").val();
  var exce = 85;
  var good = 75;
  var fine = 55;
  var exceColor = '<font color="#OOECOO">';
  var goodColor = '<font color="#OOE3EE">';
  var fineColor = '<font color="#5B5B5B">';
  var badColor = '<font color="#FF5151">';
  if (
    coreType == "en.word.score" ||
    coreType == "en.sent.score" ||
    coreType == "en.pred.score"
  ) {
    if (point >= exce) {
      var level = " " + exceColor + word + "</font>";
    } else if (good <= point && point < exce) {
      var level = " " + goodColor + word + "</font>";
    } else if (fine <= point && point < good) {
      var level = " " + fineColor + word + "</font>";
    } else {
      var level = " " + badColor + word + "</font>";
    }
  } else {
    if (point >= exce) {
      var level = exceColor + word + "</font>";
    } else if (good <= point && point < exce) {
      var level = goodColor + word + "</font>";
    } else if (fine <= point && point < good) {
      var level = fineColor + word + "</font>";
    } else {
      var level = badColor + word + "</font>";
    }
  }
  return level;
};

2.2.3 取消录音方法

myRecord.cancelRecord();

执行取消录音后,不再返回本次测评的结果

2.3 评测方案 (2):自定义录音功能进行评测

2.3.1 开启自定义录音

在初始化SDK的时候设置 customRecord 为 true

参考代码:

var myRecord = new EngineEvaluat({
  customRecord: true,

  applicationId: appid,

  userId: userId,

  micAllowCallback: function () {
    console.log("麦克风已经允许");
  },
  micForbidCallback: function () {
    console.log("麦克风没有被允许");
  },
  playAudioComplete: function (data) {
    console.log("播放完毕");
  },
  playAudioError: function (data) {
    console.log("播放出错");
  },
  engineBackResultDone: function (msg) {
    console.log("评测成功");
    myRecordResult(msg);
  },
  engineBackResultFail: function (msg) {
    console.error("评测失败");
    console.log(msg);
  },
  engineFirstInitDone: function () {
    console.log("初始化成功");
  },
  micVolumeCallback: function (data) {
    console.log("录音音量大小为:" + data);
  },
  JSSDKNotSupport: function () {
    alert("jssdk  not support");
  },
  noNetwork: function () {
    console.warn("暂无网络");
  },

  audioDataCallback: function (data, isLast) {
    //需要在开始测评参数配置  saveAudio : 1 ,此回调才会响应
    //如果在开始测评参数里配置 compress : 'raw',则会返回pcm格式的录音实时数据。否则会ogg格式的音频实时数据
    console.log(data);
  },
});

2.3.2 设置start参数

var params = { coreType: 'en.word.score', refText:'word' };

// 鉴权参数,必填
params.warrantId = warrantId;

// 使用wav,非必须
params.audio = {
      audioType: "wav"
};

// 建议使用ogg格式,评测效果比较好,SDK兼容也是最好的。默认就是ogg的,不需要设置 
myRecord.start(params);

js中自定义录音的参考代码:

// 初始化音频录制
async function initAudio() {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    audioContext = new (window.AudioContext || window.webkitAudioContext)();
    audioInput = audioContext.createMediaStreamSource(stream);
    scriptProcessor = audioContext.createScriptProcessor(4096, 1, 1);

    audioInput.connect(scriptProcessor);
    scriptProcessor.connect(audioContext.destination);

    scriptProcessor.onaudioprocess = (audioProcessingEvent) => {
      if (!isRecording) return;

      const inputBuffer = audioProcessingEvent.inputBuffer;
      const inputData = inputBuffer.getChannelData(0);

      // 重新采样到 16000 Hz
      const resampledBuffer = resample(
        inputData,
        audioContext.sampleRate,
        16000
      );

      // 将 Float32Array 转换为 PCM
      const pcmData = convertFloat32ToPCM(resampledBuffer);

      // 将 PCM 转换为 Base64
      const base64String = arrayBufferToBase64(pcmData.buffer);
      console.log("Real-Time PCM Data (Base64):", base64String);

      // 这里可以把 base64String 传到服务端或
      myRecord.feed(base64String);
    };
  } catch (error) {
    console.error("Error accessing microphone:", error);
  }
}

// 重新采样函数
function resample(inputData, inputSampleRate, targetSampleRate) {
  const sampleRateRatio = inputSampleRate / targetSampleRate;
  const newLength = Math.round(inputData.length / sampleRateRatio);
  const resampledData = new Float32Array(newLength);

  for (let i = 0; i < newLength; i++) {
    const originalIndex = i * sampleRateRatio;
    const lowerIndex = Math.floor(originalIndex);
    const upperIndex = Math.ceil(originalIndex);
    const interpolation = originalIndex - lowerIndex;

    resampledData[i] =
      (1 - interpolation) * inputData[lowerIndex] +
      interpolation * inputData[upperIndex];
  }

  return resampledData;
}

// 将 Float32Array 转换为 PCM 16位整数
function convertFloat32ToPCM(float32Array) {
  let pcmArray = new Int16Array(float32Array.length);
  for (let i = 0; i < float32Array.length; i++) {
    let s = Math.max(-1, Math.min(1, float32Array[i]));
    pcmArray[i] = s < 0 ? s * 0x8000 : s * 0x7fff;
  }
  return pcmArray;
}

// 将 ArrayBuffer 转换为 Base64 编码
function arrayBufferToBase64(buffer) {
  let binary = "";
  const bytes = new Uint8Array(buffer);
  for (const byte of bytes) {
    binary += String.fromCharCode(byte);
  }
  return window.btoa(binary);
}

// 开始录音
function startRecording() {
  if (!audioContext || !scriptProcessor) {
    console.error("Audio context or script processor not initialized");
    return;
  }

  isRecording = true;
  console.log("Recording started");
}

// 停止录音
function stopRecording() {
  if (isRecording) {
    isRecording = false;
    console.log("Recording stopped");
  }
}

2.3.3 调用feed方法

// 将你们的录音流转成ogg格式 ArrayBuffer Base64 encode 后的字符串 
myRecord.feed(base64String);

2.3.4 调用stop方法

myRecord.stop();

回调engineBackResultDone方法回调结果

2.4.1 文件整体上传测评

var params = { coreType: 'en.word.score', refText:'word' };

// 鉴权参数,必填
params.warrantId = warrantId;
    // 建议使用pcm格式的文件,评测效果比较好,SDK兼容也是最好的。默认就是ogg的,不需要设置 
myRecord.wholeFileUpload(evt, params);

名称

类型

说明

默认值

是否必填

evt

Object

上传文件压缩后对象

params

Object

引擎测评参数{coreType: “en.word.score”, refText:”word”}

<input type="file" id="fileInput" accept="audio/*">
document.getElementById('fileInput').addEventListener('change', function(event) {
    engine.wholeFileUpload(event, { coreType: 'en.sent.score', refText: 'word' });
});

2.4.2 结束评测

自动结束,不需要额外设置,等待回调即可。

2.4 销毁引擎

destroyEngine(): 销毁引擎,关闭 WebSocket。

2.5 常用功能设置

2.5.1 设置麦克风音量

myRecord.setMicVolume(num);

名称

类型

说明

默认值

是否必填

num

Number

设置麦克风音量

1.5

2.6 录音音频播放

2.6.1 初始化播放音频

var url =
    "https://files.cloud.ssapi.cn/" +
    recordResult.applicationId +
    "/" +
    recordResult.recordId;
var musicUrl = url + ".mp3";
myRecord.loadAudio(musicUrl);

参数:@url;

名称

类型

说明

默认值

是否必填

url

String

音频播放地址

2.6.2 播放音频

myRecord.playAudio();

2.6.3 暂停播放音频

myRecord.pauseAudio();

2.6.4 停止播放音频

myRecord.stopAudio();

2.6.5 获取音频时长

let duration = myRecord.getDuration();

2.6.6 获取音频播放进度

let currentTime = myRecord.getCurrentTime();

2.6.7 设置音频音量

myRecord.setAudioVolume(num);

名称

类型

说明

默认值

是否必填

num

Number

设置音频音量

0.8