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

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



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


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

                                       



           那么这是怎么做到的呢?

           

           首先这个过程分为三步: 

           

           一。录制声音并压缩成数据包:这个过程一般是当玩家点击按钮,开始录音,松开按钮,停止录音并生成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~


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

                             


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
网传资源,如有侵权请联系/留言,资源过大上传乃是下载链接的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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

火云洞红孩儿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值