小程序、录音、TP5、转码、silk
本篇文章主要介绍一下以下功能:
用小程序实现录音功能,在本地播放,提交服务端,服务端播放。
中间遇到了一些坑,找到了一些解决方法,如果有更优的解决方案希望你能在评论留言,一起加油。
1.小程序端
前端展示页面,小程序采用数据绑定方式,这里正在录音和正在播放的状态切换,用两张图根据录音状态和播放状态切换,这里就不多说了。
长按开始录音,触发录音事件,松手执行录音结束,将录音结果保存在本地。
/**
* 开始录音
*/
startRecord: function () {
var that = this
that.setData({
isRecording: true,
});
whisper.startRecord({
success(result) {
that.setData({
isRecording: false,
});
console.log('录音成功:', result);
console.log('录音成功:', result.tempFilePath);
//提交录音
var src = result.tempFilePath;
var name = 'record'
repair.uploadRecord(src, name, (res) => {
that.data.recordObj.record = res;
});
},
fail(error) {
console.log('录音失败:', error);
},
process() {
that.setData({
duration: whisper.getRecordDuration(),
});
},
compelete() {
},
});
},
/**
* 停止录音
*/
stopRecord: function () {
var that = this;
whisper.stopRecord({
success(result) {
that.setData({
isRecording: false,
});
console.log('停止录音:', result);
},
fail(error) {
console.log('停止录音失败:', error);
}
});
},
/**
* 播放录音
*/
play: function () {
var that = this
that.setData({
isPlaying: true,
});
whisper.playRecord({
src: whisper.getRecordSrc(),
success(result) {
that.setData({
isPlaying: false,
});
console.log('播放/暂停成功:', result);
},
fail(error) {
that.setData({
isPlaying: false,
});
console.log('播放/暂停失败:', error);
}
});
},
在录音结束后,执行success的回调函数,上传录音文件到服务器,注意微信小程序的录音文件是silk格式,这是一大坑,在服务端需要处理一下,后面再说。
具体的上传在model层。
//上传录音
uploadRecord(src, name, callback){
var params = {
url:'repair/record',
filePath: src,
name: name,
formData: {
'name': name
},
sCallback: function (data) {
callback && callback(data);
}
};
this.upload(params);
}
录音类也贴一下
var noop = function noop() { };
var recorder = {
};
/***
* @class
* 表示请求过程中发生的异常
*/
var RecordError = (function () {
function RecordError(message) {
Error.call(this, message);
this.message = message;
}
RecordError.prototype = new Error();
RecordError.prototype.constructor = RecordError;
return RecordError;
})();
function startRecord(options) {
if (typeof options !== 'object') {
var message = '请求传参应为 object 类型,但实际传了 ' + (typeof options) + ' 类型';
throw new RecordError(message);
}
var process = options.process;
var success = options.success || noop;
var fail = options.fail || noop;
var complete = options.complete || noop;
if (typeof process !== 'function') {
var message = '刷新Ui函数不存在';
throw new RecordError(message);
}
// 成功回调
var callSuccess = function () {
success.apply(null, arguments);
complete.apply(null, arguments);
};
// 失败回调
var callFail = function (error) {
fail.call(null, error);
complete.call(null, error);
};
// 初始化录音器
initRecorder();
// 实际进行请求的方法
doRecord();
// 实际进行请求的方法
function doRecord() {
console.log("开始录音")
recorder.timer = setInterval(function () {
recorder.duration += 1;
process();
if (recorder.duration >= recorder.maxDuration && recorder.timer) {
clearInterval(recorder.timer);
}
}, 1000);
wx.startRecord({
success: function (res) {
if (res.tempFilePath) {
recorder.src = res.tempFilePath;
callSuccess.apply(null, arguments);
return ;
} else {
message = '录音文件保存失败';
var error = new RecordError(message);
options.fail(error);
}
callFail(error);
},
fail: callFail,
complete: complete,
});
};
// 初始化录音器
function initRecorder() {
recorder = {
maxDuration: 60,
duration: 0,
src: null,
timer: null,
};
}
};
function stopRecord(options) {
if (typeof options !== 'object') {
var message = '请求传参应为 object 类型,但实际传了 ' + (typeof options) + ' 类型';
throw new RecordError(message);
}
var success = options.success || noop;
var fail = options.fail || noop;
var complete = options.complete || noop;
// 成功回调
var callSuccess = function () {
success.apply(null, arguments);
complete.apply(null, arguments);
};
// 失败回调
var callFail = function (error) {
fail.call(null, error);
complete.call(null, error);
};
doStopRecord();
// 实际进行请求的方法
function doStopRecord() {
wx.stopRecord({
success: function (res) {
if (recorder.timer) {
clearInterval(recorder.timer);
}
callSuccess.apply(null, arguments);
},
fail: callFail,
complete: complete,
});
};
};
function getRecordDuration() {
return recorder.duration || 0;
}
function getRecordSrc() {
return recorder.src || null;
}
module.exports = {
RecordError: RecordError,
startRecord: startRecord,
stopRecord: stopRecord,
getRecordDuration: getRecordDuration,
getRecordSrc: getRecordSrc,
};
播放类
var noop = function noop() { };
var player = {
src: null,
};
/***
* @class
* 表示请求过程中发生的异常
*/
var PlayError = (function () {
function PlayError(message) {
Error.call(this, message);
this.message = message;
}
PlayError.prototype = new Error();
PlayError.prototype.constructor = PlayError;
return PlayError;
})();
function play(options) {
if (typeof options !== 'object') {
var message = '请求传参应为 object 类型,但实际传了 ' + (typeof options) + ' 类型';
throw new PlayError(message);
}
if (!options.src) {
var message = '无资源';
var error = new PlayError(message);
options.fail(error);
return;
}
var process = options.process || noop;
var success = options.success || noop;
var fail = options.fail || noop;
var complete = options.complete || noop;
// 成功回调
var callSuccess = function () {
success.apply(null, arguments);
complete.apply(null, arguments);
};
// 失败回调
var callFail = function (error) {
fail.call(null, error);
complete.call(null, error);
};
if (!player.src || player.src != options.src) {
if (player.src) {
doStop(false);
}
doPlay();
} else {
doStop(true);
}
// 实际进行请求的方法
function doPlay() {
player.src = options.src;
console.log("开始播放" + player.src);
wx.playVoice({
filePath: player.src,
success: function (res) {
// success
console.log("播放结束" + player.src);
player.src = null,
callSuccess.apply(null, arguments);
},
fail: callFail,
complete: complete,
})
};
// 实际进行请求的方法
function doStop(isCallbackOn) {
wx.stopVoice({
success: function () {
console.log("停止播放" + player.src);
player.src = null;
isCallbackOn ? callSuccess.apply(null, arguments) : noop();
},
fail: isCallbackOn ? callFail : noop,
complete: isCallbackOn ? complete : noop,
});
};
};
module.exports = {
PlayError: PlayError,
playRecord: play,
};
ok,前端完成,转向服务端
2.服务端
后端用的是PHP的TP5框架,接收到的silk文件想要在浏览器上播放,我刚开始是想的转码MP3,在网上找了些资料,基本都指向了kn007大神的博客,写了个demo,Silk v3编码格式转码MP3终于实现了,结果发现小程序的silk不是silk V3,哭晕在厕所,这里可能有我没理解的,有人实现了希望可以留言。
然后,我一同事提醒我webm格式的可以直接播放的,囧。那就没的说了,直接base64解码成webm格式。
/**
* 报修录音
* @url /repair/record
* @http post
*/
public function uploadRecord()
{
$name = input('post.name');
if($_FILES[$name]["error"] > 0){
throw new Exception("Error: " . $_FILES[$name]["error"]);
}else{
$file = request()->file($name);
$info = $file->move(ROOT_PATH . 'public' . DS . 'record/'.$this->mainID);
$path = $info->getSaveName();
$base = ROOT_PATH . 'public' . DS;
$silk = $base . 'record/' . $this->mainID . '/' . $path;
$webm = str_replace("silk","webm",$silk);
$returnWebm = str_replace("silk","webm",$path);
$content = file_get_contents($silk);
$baseSilk = base64_decode(str_replace("data:audio/webm;base64,", '', $content));
file_put_contents($webm,$baseSilk);
}
return $returnWebm;
}
也贴一下wins环境下,用silk_v3_decoder.exe转码MP3的测试代码吧,具体的可以看下kn007大神的博客。
public function test()
{
$base = ROOT_PATH . 'public' . DS;
$file = $base.'record\\song1.silk';
$type = $base.'record\\song.pcm';
$res = $base.'record\\song.mp3';
$cmd = "{$base}silk\\windows\\silk_v3_decoder.exe $file $type" ;
exec($cmd, $out);
$cmd1 = "{$base}silk\\windows\\ffmpeg.exe -y -f s16le -ar 24000 -ac 1 -i $type $res";
exec($cmd1,$out1);
}
就这吧。
点击查看更多内容
7人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦