达达麻将语音聊天源码分析

达达麻将版图

语音聊天

1:语音聊天只支持Native平台,iOS与android;

2: 语音聊天的音频格式为amr;

3: native平台实现了语音的录制和播放,可以移植到其他项目使用,代码放在native文件夹下;

录制流程

1:按钮在代码里面监听START与Ended事件开始和结束录音;  Voice.js

2: VoiceMgr.js: 封装了android/IOS native平台的调用;

3: cc.vv.voiceMgr.prepare("record.amr");开始录制;

1>暂停所有的游戏音效与音乐;

2>清理掉原来录制的文件;

3>调用native 平台的开始录制函数;

4:Ended事件响应:

1>判断录音是否超过10S,没有超过取消;

2>cc.vv.voiceMgr.cancel() --> 调用到native平台 cancel;

3>cc.vv.voiceMgr.realease() --> 调用native平台,录制完成;

4>getVoiceData: 将录制好的二进制文件转换成文本编码;

5>发送音频文本数据给游戏服务器: voice_msg事件;

播放流程

1:服务器收到 voice_msg事件;

2: 将语音数据在房间广播:voice_msg_push事件;

3: GameNetMgr.js-->收到voice_msg_push事件,然后派发voice_msg;

4: MJRoom.js-->playVoice;

5: cc.vv.voiceMgr.writeVoice(msgfile,msgInfo.msg); 将受到的数据写入声音文件;

6: 播放声音文件cc.vv.voiceMgr.play(msgfile),调用本地接口;

7: update函数里面检车播放时间是否到,调用onPlayerOver到了以后继续恢复原来的游戏音效;

8:优点: native平台下的录音和播放代码是直接可以使用的;

有带改进的地方:音频数据比较大,建议不要走游戏的长连接,阻塞游戏数据通道(广播),可以改成独立的http上传和下载,然后房间里面只广播xxxx说了话;

在Voice.js中发现按钮

监听的事件:

btnVoice.on(cc.Node.EventType.TOUCH_START,function(){

btnVoice.on(cc.Node.EventType.TOUCH_END,function(){

btnVoice.on(cc.Node.EventType.TOUCH_CANCEL,function(){

onLoad: function () {this._voice = cc.find("Canvas/voice");this._voice.active = false;this._voice_failed = cc.find("Canvas/voice/voice_failed");this._voice_failed.active = false;this._timeBar = cc.find("Canvas/voice/time");this._timeBar.scaleX = 0.0;this._volume = cc.find("Canvas/voice/volume");for(var i = 1; i < this._volume.children.length; ++i){this._volume.children[i].active = false;}var btnVoice = cc.find("Canvas/voice/voice_failed/btn_ok");if(btnVoice){cc.vv.utils.addClickEvent(btnVoice,this.node,"Voice","onBtnOKClicked");}var self = this;var btnVoice = cc.find("Canvas/btn_voice");if(btnVoice){btnVoice.on(cc.Node.EventType.TOUCH_START,function(){console.log("cc.Node.EventType.TOUCH_START");cc.vv.voiceMgr.prepare("record.amr");self._lastTouchTime = Date.now();self._voice.active = true;self._voice_failed.active = false;});btnVoice.on(cc.Node.EventType.TOUCH_MOVE,function(){console.log("cc.Node.EventType.TOUCH_MOVE");});btnVoice.on(cc.Node.EventType.TOUCH_END,function(){console.log("cc.Node.EventType.TOUCH_END");if(Date.now() - self._lastTouchTime < 1000){self._voice_failed.active = true;cc.vv.voiceMgr.cancel();}else{self.onVoiceOK();}self._lastTouchTime = null;});btnVoice.on(cc.Node.EventType.TOUCH_CANCEL,function(){console.log("cc.Node.EventType.TOUCH_CANCEL");cc.vv.voiceMgr.cancel();self._lastTouchTime = null;self._voice.active = false;});}},

分别调用Android和IOS平台的函数;

录音小于1秒或者是在按钮外部弹起来,都是取消录音;

//---------------------VoiceMgr.js----------------------------
var radix = 12;
var base = 128 - radix;
function crypto(value){value -= base;var h = Math.floor(value/radix) + base;var l = value%radix + base;return String.fromCharCode(h) + String.fromCharCode(l);
}var encodermap = {}
var decodermap = {}
for(var i = 0; i < 256; ++i){var code = null;var v = i + 1;if(v >= base){code = crypto(v);}else{code = String.fromCharCode(v);    }encodermap[i] = code;decodermap[code] = i;
}function encode(data){var content = "";var len = data.length;var a = (len >> 24) & 0xff;var b = (len >> 16) & 0xff;var c = (len >> 8) & 0xff;var d = len & 0xff;content += encodermap[a];content += encodermap[b];content += encodermap[c];content += encodermap[d];for(var i = 0; i < data.length; ++i){content += encodermap[data[i]];}return content;
}function getCode(content,index){var c = content.charCodeAt(index);if(c >= base){c = content.charAt(index) + content.charAt(index + 1);}else{c = content.charAt(index);}return c;
}
function decode(content){var index = 0;var len = 0;for(var i = 0; i < 4; ++i){var c = getCode(content,index);index += c.length;var v = decodermap[c];len |= v << (3-i)*8;}var newData = new Uint8Array(len);var cnt = 0;while(index < content.length){var c = getCode(content,index);index += c.length;newData[cnt] = decodermap[c];cnt++;}return newData;
}cc.Class({extends: cc.Component,properties: {// foo: {//    default: null,      // The default value will be used only when the component attaching//                           to a node for the first time//    url: cc.Texture2D,  // optional, default is typeof default//    serializable: true, // optional, default is true//    visible: true,      // optional, default is true//    displayName: 'Foo', // optional//    readonly: false,    // optional, default is false// },// ...onPlayCallback:null,_voiceMediaPath:null,},// use this for initializationinit: function () {/*var url = cc.url.raw("resources/test.amr");var fileData = jsb.fileUtils.getDataFromFile(url);var content = "";var sep = "";for(var i = 0; i < fileData.length; ++i){content += sep + fileData[i];sep = ",";}var url = cc.url.raw("resources/test.txt");jsb.fileUtils.writeStringToFile(content,url);var url = cc.url.raw("resources/test2.amrs");var content = encode(fileData);jsb.fileUtils.writeStringToFile(content,url);var url = cc.url.raw("resources/test2.amr");jsb.fileUtils.writeDataToFile(decode(content),url);*/if(cc.sys.isNative){this._voiceMediaPath = jsb.fileUtils.getWritablePath() + "/voicemsgs/";this.setStorageDir(this._voiceMediaPath);}},prepare:function(filename){if(!cc.sys.isNative){return;}cc.vv.audioMgr.pauseAll();this.clearCache(filename);if(cc.sys.os == cc.sys.OS_ANDROID){jsb.reflection.callStaticMethod("com/babykylin/VoiceRecorder", "prepare", "(Ljava/lang/String;)V",filename);}else if(cc.sys.os == cc.sys.OS_IOS){jsb.reflection.callStaticMethod("VoiceSDK", "prepareRecord:",filename);}},release:function(){if(!cc.sys.isNative){return;}cc.vv.audioMgr.resumeAll();if(cc.sys.os == cc.sys.OS_ANDROID){jsb.reflection.callStaticMethod("com/babykylin/VoiceRecorder", "release", "()V");}else if(cc.sys.os == cc.sys.OS_IOS){jsb.reflection.callStaticMethod("VoiceSDK", "finishRecord");}},cancel:function(){if(!cc.sys.isNative){return;}cc.vv.audioMgr.resumeAll();if(cc.sys.os == cc.sys.OS_ANDROID){jsb.reflection.callStaticMethod("com/babykylin/VoiceRecorder", "cancel", "()V");}else if(cc.sys.os == cc.sys.OS_IOS){jsb.reflection.callStaticMethod("VoiceSDK", "cancelRecord");}},writeVoice:function(filename,voiceData){if(!cc.sys.isNative){return;}if(voiceData && voiceData.length > 0){var fileData = decode(voiceData);var url = this._voiceMediaPath + filename;this.clearCache(filename);jsb.fileUtils.writeDataToFile(fileData,url); }},clearCache:function(filename){if(cc.sys.isNative){var url = this._voiceMediaPath + filename;//console.log("check file:" + url);if(jsb.fileUtils.isFileExist(url)){//console.log("remove:" + url);jsb.fileUtils.removeFile(url);}if(jsb.fileUtils.isFileExist(url + ".wav")){//console.log("remove:" + url + ".wav");jsb.fileUtils.removeFile(url + ".wav");}   }},play:function(filename){if(!cc.sys.isNative){return;}cc.vv.audioMgr.pauseAll();if(cc.sys.os == cc.sys.OS_ANDROID){jsb.reflection.callStaticMethod("com/babykylin/VoicePlayer", "play", "(Ljava/lang/String;)V",filename); }else if(cc.sys.os == cc.sys.OS_IOS){jsb.reflection.callStaticMethod("VoiceSDK", "play:",filename);}else{}},stop:function(){if(!cc.sys.isNative){return;}cc.vv.audioMgr.resumeAll();if(cc.sys.os == cc.sys.OS_ANDROID){jsb.reflection.callStaticMethod("com/babykylin/VoicePlayer", "stop", "()V"); }else if(cc.sys.os == cc.sys.OS_IOS){jsb.reflection.callStaticMethod("VoiceSDK", "stopPlay");}else{}},getVoiceLevel:function(maxLevel){return Math.floor(Math.random() * maxLevel + 1);if(cc.sys.os == cc.sys.OS_ANDROID){ return jsb.reflection.callStaticMethod("com/babykylin/VoiceRecorder", "getVoiceLevel", "(I)I",maxLevel);}else if(cc.sys.os == cc.sys.OS_IOS){}else{return Math.floor(Math.random() * maxLevel + 1);}},getVoiceData:function(filename){if(cc.sys.isNative){var url = this._voiceMediaPath + filename;console.log("getVoiceData:" + url);var fileData = jsb.fileUtils.getDataFromFile(url);if(fileData){var content = encode(fileData);return content;}}return "";},download:function(){},// called every frame, uncomment this function to activate update callback// update: function (dt) {// },setStorageDir:function(dir){if(!cc.sys.isNative){return;}if(cc.sys.os == cc.sys.OS_ANDROID){ jsb.reflection.callStaticMethod("com/babykylin/VoiceRecorder", "setStorageDir", "(Ljava/lang/String;)V",dir);    }else if(cc.sys.os == cc.sys.OS_IOS){jsb.reflection.callStaticMethod("VoiceSDK", "setStorageDir:",dir);if(!jsb.fileUtils.isDirectoryExist(dir)){jsb.fileUtils.createDirectory(dir);}}}
});

声音数据转换成文本信息,发送服务器来广播:

    onVoiceOK:function(){if(this._lastTouchTime != null){cc.vv.voiceMgr.release();var time = Date.now() - this._lastTouchTime;var msg = cc.vv.voiceMgr.getVoiceData("record.amr");cc.vv.net.send("voice_msg",{msg:msg,time:time});}this._voice.active = false;},

服务器的语音是长连接:

收到消息之后进行广播:

userMgr.broacastInRoom('voice_msg_push',{sender:socket.userId,content:data},socket.userId,true);

客户端收到之后,分发消息:

MJRoom.js收到消息之后,吧数据缓存队列里面,然后播放playVoice();

播放playVoice:function(){

把文档声音数据再转换成二进制文件,然后播放;

cc.vv.voiceMgr.writeVoice(msgfile,msgInfo.msg);
cc.vv.voiceMgr.play(msgfile);

播放的时候play:function(filename){

先是暂停所有的音乐cc.vv.audioMgr.pauseAll();

停止之后stop:function(){

再恢复cc.vv.audioMgr.resumeAll();所有的音乐

//------------------------Voice.js------------------------------
cc.Class({extends: cc.Component,properties: {// foo: {//    default: null,      // The default value will be used only when the component attaching//                           to a node for the first time//    url: cc.Texture2D,  // optional, default is typeof default//    serializable: true, // optional, default is true//    visible: true,      // optional, default is true//    displayName: 'Foo', // optional//    readonly: false,    // optional, default is false// },// ..._lastTouchTime:null,_voice:null,_volume:null,_voice_failed:null,_lastCheckTime:-1,_timeBar:null,MAX_TIME:15000,},// use this for initializationonLoad: function () {this._voice = cc.find("Canvas/voice");this._voice.active = false;this._voice_failed = cc.find("Canvas/voice/voice_failed");this._voice_failed.active = false;this._timeBar = cc.find("Canvas/voice/time");this._timeBar.scaleX = 0.0;this._volume = cc.find("Canvas/voice/volume");for(var i = 1; i < this._volume.children.length; ++i){this._volume.children[i].active = false;}var btnVoice = cc.find("Canvas/voice/voice_failed/btn_ok");if(btnVoice){cc.vv.utils.addClickEvent(btnVoice,this.node,"Voice","onBtnOKClicked");}var self = this;var btnVoice = cc.find("Canvas/btn_voice");if(btnVoice){btnVoice.on(cc.Node.EventType.TOUCH_START,function(){console.log("cc.Node.EventType.TOUCH_START");cc.vv.voiceMgr.prepare("record.amr");self._lastTouchTime = Date.now();self._voice.active = true;self._voice_failed.active = false;});btnVoice.on(cc.Node.EventType.TOUCH_MOVE,function(){console.log("cc.Node.EventType.TOUCH_MOVE");});btnVoice.on(cc.Node.EventType.TOUCH_END,function(){console.log("cc.Node.EventType.TOUCH_END");if(Date.now() - self._lastTouchTime < 1000){self._voice_failed.active = true;cc.vv.voiceMgr.cancel();}else{self.onVoiceOK();}self._lastTouchTime = null;});btnVoice.on(cc.Node.EventType.TOUCH_CANCEL,function(){console.log("cc.Node.EventType.TOUCH_CANCEL");cc.vv.voiceMgr.cancel();self._lastTouchTime = null;self._voice.active = false;});}},onVoiceOK:function(){if(this._lastTouchTime != null){cc.vv.voiceMgr.release();var time = Date.now() - this._lastTouchTime;var msg = cc.vv.voiceMgr.getVoiceData("record.amr");cc.vv.net.send("voice_msg",{msg:msg,time:time});}this._voice.active = false;},onBtnOKClicked:function(){this._voice.active = false;},// called every frame, uncomment this function to activate update callbackupdate: function (dt) {if(this._voice.active == true && this._voice_failed.active == false){if(Date.now() - this._lastCheckTime > 300){for(var i = 0; i < this._volume.children.length; ++i){this._volume.children[i].active = false;}var v = cc.vv.voiceMgr.getVoiceLevel(7);if(v >= 1 && v <= 7){this._volume.children[v-1].active = true;   }this._lastCheckTime = Date.now();}}if(this._lastTouchTime){var time = Date.now() - this._lastTouchTime;if(time >= this.MAX_TIME){this.onVoiceOK();this._lastTouchTime = null;}else{var percent = time / this.MAX_TIME;this._timeBar.scaleX = 1 - percent;}}},
});

下一篇:幼麟棋牌代码讲解

cocos creator麻将教程系列(八)—— 达达麻将语音聊天源码分析相关推荐

  1. cocos creator休闲游戏甜品幻想H5+安卓+IOS三端源码开发脚本为javaScript

    cocos creator休闲游戏甜品幻想H5+安卓+IOS三端源码,开发脚本为javaScript方便扩展和阅读,支持cocos creator2.X版本,完整的源码可拿来运营学习研究二次开发. 1 ...

  2. 一步步实现windows版ijkplayer系列文章之二——Ijkplayer播放器源码分析之音视频输出——视频篇...

    一步步实现windows版ijkplayer系列文章之一--Windows10平台编译ffmpeg 4.0.2,生成ffplay 一步步实现windows版ijkplayer系列文章之二--Ijkpl ...

  3. 从零开始实现自己的Kalimba——Cocos Creator新手教程系列(一)使用瓦片图Tiledmap设计游戏地图

    瓦片图Tiledmap可能是很多2d游戏开发者的偏爱,本节就Cocos Creator如何使用瓦片图进行详细的讲解. Tiled地图编辑器的下载安装不再赘述.下面介绍如何使用地图编辑器. 创建新地图. ...

  4. 在参考了众多博客之后,我写出了多达三万字的HashMap源码分析,比我本科毕业论文都要精彩

    HashMap源码分析 以下代码都是基于java8的版本 HashMap简介 源码: public class HashMap<K,V> extends AbstractMap<K, ...

  5. java观察者模式类图_设计模式(十八)——观察者模式(JDK Observable源码分析)...

    1 天气预报项目需求,具体要求以下: 1) 气象站能够将天天测量到的温度,湿度,气压等等以公告的形式发布出去(好比发布到本身的网站或第三方).java 2) 须要设计开放型 API,便于其余第三方也能 ...

  6. 决策树(八)--随机森林及OpenCV源码分析

    原文: http://blog.csdn.net/zhaocj/article/details/51580092 一.原理 随机森林(Random Forest)的思想最早是由Ho于1995年首次提出 ...

  7. 动态代理最全详解系列[2]-Proxy生成代理类对象源码分析

      之前我们通过JDK中的Proxy实现了动态代理,Proxy用起来是比较简便的,但理解起来不是那么清晰,是因为我们并没有看见代理类是怎么生成的,代理类怎么调用的被代理类方法,所以下面我们进入源码看一 ...

  8. cocos creator麻将教程系列(七)—— 达达麻将打包与发布

    达达麻将打包与发布 达达麻将版图 打包注意事项 1:打包之前仔细学习creator的android 与h5的打包; 2: 达达麻将打包两个注意点: (1) 录音的代码; (2)微信的登陆代码; 3: ...

  9. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

最新文章

  1. linux怎么安装java环境变量_如何简单的在linux上安装jdk并配置环境变量
  2. Zabbix的snmp监控
  3. 【Ubuntu】 Ubuntu18.04修改主机名
  4. #时间预测算法_【时间序列】时序预测竞赛之异常检测算法综述
  5. 哪三级分类java_技术汇总:第五章:使用angularjs做首页三级分类
  6. [AutoSar]RTE运行逻辑、通信逻辑与接口实现
  7. 阶段5 3.微服务项目【学成在线】_day04 页面静态化_14-页面静态化-数据模型-远程请求接口...
  8. Java 引用jar包
  9. 【财富空间】UpHonest Capital:美国人工智能投资分析报告(精华版)
  10. wifi握手包自动跑包
  11. 如何烧写Bubblegum-96 board
  12. 直播、网红和社交网络的Social Media化
  13. Numpy学习之——numpy.mean中axis参数用法
  14. iveiw:Switch开关用法
  15. 自由浏览器 android,百度浏览器发布安卓6.1版 趣味视频弹幕吐槽不停
  16. 数据库逻辑设计之三大范式通俗理解
  17. Rider去除警告波浪线设置
  18. Javascript实现cuid唯一标识号
  19. 用友U8 V13安装教程
  20. 面试重点——promise原理以及实现

热门文章

  1. 武林传奇之七剑下天山java游戏开发商_武林传奇2之七剑下天山的配方
  2. C++ lazy evaluation(延迟计算或惰性求值)介绍
  3. R语言按照人口比例绘制甘肃地图
  4. php获取七牛上传token失效,上传文件到七牛时,bad token了怎么处理?
  5. linux内核修改电流,为AM335x移植Linux内核主线代码(43)USB HUB和鼠标
  6. 玩转【斗鱼直播APP】系列之界面分析
  7. gitee码云安装和使用教程
  8. DOS命令之诊断网络
  9. GeeM2传奇引擎进入游戏出现白屏的解决办法
  10. C#远程启动、终止进程