房卡麻将分析系列之"千里传音"

 ”房卡“麻将研发技巧,尽在”红孩儿的游戏开发之路“,欢迎关注公众号!



               房卡麻将分析系列之"千里传音"


                   在房卡棋牌游戏中,因为要频繁的看牌,出牌。为了实时沟通打字聊天往往比较麻烦,通过语音交流,催牌可以很好的帮助玩家及时的表达情绪,增强游戏的气氛。

                                       



           那么这是怎么做到的呢?

           

           首先这个过程分为三步: 

           

           一。录制声音并压缩成数据包:这个过程一般是当玩家点击按钮,开始录音,松开按钮,停止录音并生成WAV文件,之后通过编码转换压缩为

在这里要根据安卓和苹果两个平台来做区分。


        void startSoundRecord()
	{
		std::string kFileName = utility::toString(time(NULL),".wav");
		s_kRecordFileName = cocos2d::FileUtils::getInstance()->getWritablePath()+kFileName;
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
		JniMethodInfo minfo;  
		bool isHave = JniHelper::getStaticMethodInfo(minfo,JAVA_CLASSNAME, "startSoundRecord", "(Ljava/lang/String;)V");
		if (isHave)  
		{  
			jstring jurl = minfo.env->NewStringUTF(kFileName.c_str());
			minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID,jurl); 
			cocos2d::log("JniFun call startSoundRecord over!");

			minfo.env->DeleteLocalRef(minfo.classID);  
		}  
		else
		{
			cocos2d::log("JniFun call startSoundRecord error!");
		}
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
		IosHelper::beginRecord(s_kRecordFileName.c_str());
#endif
	}


	const char* stopSoundRecord()
	{
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
		std::string str;
		JniMethodInfo minfo;  
		bool isHave = JniHelper::getStaticMethodInfo(minfo,JAVA_CLASSNAME, "stopSoundRecord", "()Ljava/lang/String;");
		if (isHave)  
		{  
			jstring jFileName = (jstring)minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID); 
			const char *newStr = minfo.env->GetStringUTFChars(jFileName, 0);
			str = newStr;
			cocos2d::log("JniFun call stopSoundRecord over :");
			cocos2d::log("%s",str.c_str());
			minfo.env->ReleaseStringUTFChars(jFileName, newStr);
			minfo.env->DeleteLocalRef(minfo.classID); 
		}  
		else
		{
			cocos2d::log("JniFun call stopSoundRecord error!");
		}
		return str.c_str();
#endif
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS
		IosHelper::endRecord();
		return s_kRecordFileName.c_str();
#endif
		return "";
	}




在Native.java中实现录音和结束:


     

    //开始录音
         public static void startSoundRecord( String SoundFileName)
	 {
		 String SoundFilePath= Environment.getExternalStorageDirectory().getAbsolutePath();  

		if (filePath != null)
		{
			File file = new File(filePath);
			if (file!= null && file.exists())
			{
				file.delete();
			}
		 }
		filePath = SoundFilePath+"/"+SoundFileName;
		recorder = new MediaRecorder();
                //从麦克风中录音
		recorder.setAudioSource(MediaRecorder.AudioSource.MIC);  
                //设置编码格式为AMR
		recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);  
		recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);  
		recorder.setOutputFile(SoundFilePath+"/"+SoundFileName);  
		try {  
			recorder.prepare();//
			recorder.start();//
		} catch (IllegalStateException e) {  
			e.printStackTrace();  
		} catch (IOException e) {  
			e.printStackTrace();  
		}  
	 }
	 
        //结束录音
	 public static String stopSoundRecord()
	 {
		recorder.stop();// 
                recorder.release(); // 
                recorder = null;  
		return filePath;
	 }


另外,要在AndroidMainfest.xml中注意开启录音权限:


 

<uses-permission android:name="android.permission.RECORD_AUDIO" />  


IOS版本处理:需要在mm文件中完成相应函数


 AVAudioRecorder *recorder = NULL;
void IosHelper::beginRecord(const char *_fileName)
{
      if (recorder == nil)
      {
        //设置文件名和录音路径
        NSString *recordFilePath = [NSString stringWithCString:_fileName encoding:NSUTF8StringEncoding];
        
        NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:
                                       [NSNumber numberWithFloat: 8000.0],AVSampleRateKey, //采样率
                                       [NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,
                                       [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,//采样位数 默认 16
                                       [NSNumber numberWithInt: 1], AVNumberOfChannelsKey,//通道的数目
                                       nil];
        //初始化录音
        NSError *error = nil;
        recorder = [[ AVAudioRecorder alloc] initWithURL:[NSURL URLWithString:recordFilePath] settings:recordSetting error:&error];
      }
      recorder.meteringEnabled = YES;
      [recorder prepareToRecord];
      //开始录音
      UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord;
      AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);
    
      // 扬声器播放
      UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
      AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
      [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];
      [[AVAudioSession sharedInstance] setActive:YES error:nil];
      [recorder record];
}

const char * IosHelper::endRecord()
{
        if (recorder == nil)
        return "";
        if (recorder.isRecording)
        [recorder stop];
	return "";
}


                                           
            


          二。发送声音数据到服务器:在结束录制声音并生成文件后,将文件发送出去。


	std::string kFileName = JniFun::stopSoundRecord();
	sendTalkFile(m_pLocal->GetChairID(),kFileName);



        这里就是将文件以数据包形式发送出去,不做详细表述。

        

        三。接收数据并解压,播放: 在接收到消息后,将数据写入文件并播放即可。

          

bool GameBase::RevTalk_File(CMD_GR_C_TableTalk* pNetInfo)
{
	if (pNetInfo->strTalkSize == 0)
	{
		return true;
	}
	static int iIdex = 0;
	iIdex ++;
	std::string kFile = utility::toString(cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath(),"TableTalk",iIdex,".arm");
	FILE *fp = fopen(kFile.c_str(), "wb");

	fseek(fp,0,SEEK_END);
	fseek(fp,0,SEEK_SET);
	fwrite(&pNetInfo->strTalkData,sizeof(unsigned char), pNetInfo->strTalkSize,fp);
	fclose(fp);
	int iAddTime = pNetInfo->strTalkSize/1200+2.0f;
	if (iAddTime > 10)
	{
		iAddTime = 10;
	}
	std::string kDestFile = kFile;
	utility::StringReplace(kDestFile,"arm","wav");
        //这里需要做一个解压转换,将ARM转换成WAV
	ArmFun::ArmToWav(kFile.c_str(),kDestFile.c_str());
        //为了防止游戏音乐干扰,先静音游戏音乐
	SoundFun::Instance().PaseBackMusic();
	SoundFun::Instance().ResumeBackMusic(iAddTime);
	SoundFun::Instance().PaseEffectMusic();
	SoundFun::Instance().ResumeEffectMusic(iAddTime);
        //播放接收到的声音文件
	SoundFun::Instance().playEffectDirect(kDestFile);
        //指定玩家显示播放语音的动画图标
	GamePlayer* pPlayer = getBasePlayerByChairID(pNetInfo->cbChairID);
	if (pPlayer)
	{
		pPlayer->showTalkState(pNetInfo);
	}
        
	return true;
}


          

        最终,房卡棋牌中的语音聊天就完整的实现出来了,当然,这种方式并不完美,如果能开启P2P的实时语音对话就更好了。另外,这套代码中会不断的产生声音文件,这是个问题,小伙伴们可以在发送完声音和播放完声音后删除生成的声音文件,以免造成空间增长的BUG~


 ”房卡“麻将研发技巧,尽在”红孩儿的游戏开发之路“,欢迎关注公众号!

                             


幼麟棋牌社区版,是幼麟棋牌的开源版本,用于提供给大家学习游戏服务器编程和客户端编程。 在此之前,幼麟棋牌早已开源在Cocos社区,造成了不小的影响力。 已有不下百个团队或者个人在使用本套代码开发作品。 使用幼麟棋牌框架开发出来的产品更是无法统计,抛开幼麟科技官方的数十款不算,第三方已上线产品不下百款。 幼麟棋牌框架被业内众多爱好者评为 业界清流。 它打破了早期的C++为主的棋牌格局。开创了新一代的纯Javascript脚本模式。 同时也是目前唯一能够一次开发,同时发布iOS,Android,H5三端的棋牌框架。由于其纯脚本特性,在发布iOS和Android时,天然支持热更新功能。 有了幼麟棋牌的动静,业内也有部分团队开始以棋牌平台为主打,建立开源社区,希望在开源社区找到新的盈利模式。 幼麟棋牌社区版包含功能如下: 完整的服务器客户端源代码,搭建后即可运行 完整的房卡棋牌玩法(加入,创建房间) 完整的四川麻将玩法(血战到底,血流成河两种模式) 房卡战绩记录 微信登陆,微信分享 断线重连 适合人群: 想通过完整案例,快速入手游戏开发 想通过完整源码,快速开发棋牌游戏 想通过完整源码,快速开发麻将游戏 想替换一下美术资源,就拥有一款四川麻将 想直接打个包,就拥有一款四川麻将
网传资源,如有侵权请联系/留言,资源过大上传乃是下载链接的ZIP文件。 目录: ├─1javascript程序设计9 T& b/ o% ?' h G! b' Y' X/ y │ 第001课初识node_js.rar │ 第002课JS基本数据_函数对象_表_数组_字符串_函数调用.rar* H x0 z) f4 B3 { │ 第003课JS运算表达式_条件判断_循环语句_垃圾回收.rar │ 第004课JSMath函数_数组_表_字符串_常用接口.rar │ 第005课JS模块_new_类_继承.rar1 f2 V3 A& R5 x3 Z │ ├─2creator客户端游戏开发 │ 第001课初识creator.rar+ P+ m" }! v( C& I │ 第002课cc.Node(一)场景树.rar │ 第003课cc.Node(二)事件响应.rar J. G) e" Z/ O( V% m9 i │ 第004课cc.Node(三)坐标空间的转换.rar │ 第005课cc.Node(四)Action的使用.rar │ 第006课cc.Component的使用详解.rar │ 第007课Sprite组件的使用详解.rar │ 第008课Button组件使用详解.rar │ 第009课Label组件使用详解.rar G5 [4 B8 `& ]/ b │ 第010课AudioSource组件的使用.rar' D' [8 n/ _) w% C1 v6 H │ 第011课动画编辑器的使用.rar$ N1 Y4 L$ J, g# L, c) x* T4 s* @ │ 第012课骨骼动画组件的使用.rar& G. _4 U0 u; U0 V: p% | │ 第013课mask_layout_scrollview组件的使用.rar {& g, [) W2 l0 b# t) M& V │ 第014课cc_loader代码加载和释放资源.rar │ 第015课cc.Widget与屏幕适配.rar0 D. n! ?4 l* K; c0 R │ 第023课creator_滚动列表动态加载数据.rar │ 第024课creator_h5打包发布优化技巧_android环境搭建与打包发布.rar │ 第025课creator_cc.director与资源加载策略.rar │ ) L& {! H9 r5 C7 x x2 q/ z2 t5 O: [ ├─3node.js游戏服务器开发& C7 h( i* c {* D! E │ 第006课node事件循环_process模块的基本使用.rar │ 第007课TCP网络传送的基本原理.rar- K7 a" O9 M* r0 h- q h6 U │ 第008课node.js使用Net模块搭建TCPserver_client.rar │ 第009课node.js二进制数据与Buffer模块.rar │ 第010课node.js_npm模块的安装和加载.rar! o O' O- ]0 G! t: R3 U │ 第011课node.js_websocket与ws模块使用.rar │ 第012课node.js_TCP通讯拆包与封包.rar7 L5 Y4 X, V$ v# H" c$ {0 Q │ 第013课node.js_二进制数据协议与JSON数据协议.rar │ 第014课node.js_http基础与express_webserver搭建.rar9 [5 x$ @3 Y" v: U7 e* Z │ 第015课node.js_http_server与http_client_get_post编码基本流程和实现.rar# Y/ a6 M$ d8 f │ 第016课node.js_fs模块的同步异步读写.rar4 R7 X5 @# a4 v │ 第017课node.js_Base64_MD5_SHA1_Timer模块.rar │ 第018课node.js_mysql数据库的基本使用.rar4 h5 v, n; ?' t7 ^ h# Z. x │ 第019课node.js使用mysql模块编程操作数据库.rar! D& Y* ]+ t: U: }; F │ 第020课redis的搭建和基本使用.rar' c G% x. A+ e0 d- H, ^9 D& v │ 第021课noderedis编程和使用.rar/ \2 o& J% @# k1 F │ 9 I4 R$ V( {: n- b. B* Z/ B B └─4麒麟棋牌达达麻将框架设计与源码分析 第001课麒麟棋牌_达达房卡麻将安装和导入使用注意事项.rar' B$ u+ j# Z) F7 N 第002课麒麟棋牌_达达麻将的底层通讯express框架与socket.io.rar 第003课麒麟棋牌_达达麻将开房间流程.rar 第004课达达麻将客户端初始化流程.rar- B% H! q% ]( _+ s8 |. B 第005课达达麻将开房间.rar 第006课达达麻将的游戏流程.rar! g- h. f3 m4 G 第007课达达麻将打包与发布.rar' e: p$ l* j1 K; Z1 R! ?: t: Y 第008课达达麻将语音聊天源码分析.rar 麒麟游戏达达麻将.zip
课程介绍: 01 第001课初识node_js 02 第002课JS基本数据_函数对象_表_数组_字符串_函数调用 【录播】JS基本数据_函数对象表数组字符串函数调用(97分钟) 03 第003课JS运算表达式_条件判断_循环语句_垃圾回收 【录播】JS运算表达式_条件判断_循环语句_垃圾回收(81分钟) 04 JSMath函数数组表字符串常用接口 【录播】JSMath函数数组表字符串常用接口(65分钟) 05 JS模块_new_类_继承 【录播】第005课JS模块_new_类_继承(111分钟) 06 第001课初识creator 【录播】第001课初识creator(71分钟) 07 第002课cc.Node(一)场景树 【录播】第002课cc.Node(一)场景树(73分钟) 08 第003课cc.Node(二)事件响应 【录播】第003课cc.Node(二)事件响应(68分钟) 09 第004课cc.Node(三)坐标空间的转换 【录播】第004课cc.Node(三)坐标空间的转换(54分钟) 10 第005课cc.Node(四)Action的使用 【录播】第005课cc.Node(四)Action的使用(53分钟) 11 第006课cc.Component的使用详解 【录播】第006课cc.Component的使用详解(58分钟) 12 第007课Sprite组件的使用详解 【录播】第007课Sprite组件的使用详解(59分钟) 13 第008课Button组件使用详解 【录播】第008课Button组件使用详解(46分钟) 14 第009课Label组件使用详解 【录播】第009课Label组件使用详解(34分钟) 15 第010课AudioSource组件的使用 【录播】第010课AudioSource组件的使用(34分钟) 16 第011课动画编辑器的使用 【录播】第011课动画编辑器的使用(55分钟) 17 第012课骨骼动画组件的使用 【录播】第012课骨骼动画组件的使用(35分钟) 18 creator开发课程其他 【录播】cc.Mask_滚动条使用(42分钟) 19 第006课node事件循环_process模块的基本使用 【录播】node事件循环_process模块的基本使用(56分钟) 20 第007课TCP网络传送的基本原理 【录播】第007课TCP网络传送的基本原理(49分钟) 21 第008课node.js使用Net模块搭建TCPserver_client 【录播】node.js使用Net模块搭建TCPserver_client(72分钟) 22 node.js二进制数据与Buffer模块 【录播】node.js二进制数据与Buffer模块(66分钟) 23 第010课node.js_npm模块的安装和加载 【录播】第010课node.js_npm模块的安装和加载(41分钟) 24 第011课node.js_websocket与ws模块使用 【录播】第011课node.js_websocket与ws模块使用(58分钟) 25 第012课node.js_TCP通讯拆包与封包 【录播】第012课node.js_TCP通讯拆包与封包(59分钟) 26 第013课node.js_二进制数据协议与JSON数据协议 【录播】node.js_二进制数据协议与JSON数据协议(73分钟) 27 第014课node.js_http基础与express_webserver搭建 【录播】node.js_http基础与express_webserver搭建(50分钟) 28 node.js_http_server与http_client_get_post编码基本流程和实现 【录播】node.jshttpserver与http_client_get_post(41分钟) 29 第016课node.js_fs模块的同步异步读写 【录播】第016课node.js_fs模块的同步异步读写(62分钟) 30 第017课node.js_Base64_MD5_SHA1_Timer模块 【录播】第017课node.js_Base64_MD5_SHA1_Timer模块(52分钟) 31 第018课node.js_mysql数据库的基本使用 【录播】第018课node.js_mysql数据库的基本使用(60分钟) 32 第0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火云洞红孩儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值