小程序 silk php,小程序、录音、TP5、转码、silk

本篇文章主要介绍一下以下功能:

用小程序实现录音功能,在本地播放,提交服务端,服务端播放。

中间遇到了一些坑,找到了一些解决方法,如果有更优的解决方案希望你能在评论留言,一起加油。

1.小程序端

AAffA0nNPuCLAAAAAElFTkSuQmCC

前端展示页面,小程序采用数据绑定方式,这里正在录音和正在播放的状态切换,用两张图根据录音状态和播放状态切换,这里就不多说了。

长按开始录音,触发录音事件,松手执行录音结束,将录音结果保存在本地。

/**

* 开始录音

*/

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);

}

就这吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值