- 根据不同平台分别调用
Android
和 iOS
平台的录音函数:
5如果录音时长小于 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 initialization
init: 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 initialization
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;
});
}
},
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 callback
update: 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;
}
}
},
});
好的本篇文章先到这