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

转自:http://blog.csdn.net/honghaier/article/details/62494089


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



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


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

                                       



           那么这是怎么做到的呢?

           

           首先这个过程分为三步: 

           

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

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


[cpp]  view plain  copy
  1.         void startSoundRecord()  
  2.     {  
  3.         std::string kFileName = utility::toString(time(NULL),".wav");  
  4.         s_kRecordFileName = cocos2d::FileUtils::getInstance()->getWritablePath()+kFileName;  
  5. #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID  
  6.         JniMethodInfo minfo;    
  7.         bool isHave = JniHelper::getStaticMethodInfo(minfo,JAVA_CLASSNAME, "startSoundRecord""(Ljava/lang/String;)V");  
  8.         if (isHave)    
  9.         {    
  10.             jstring jurl = minfo.env->NewStringUTF(kFileName.c_str());  
  11.             minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID,jurl);   
  12.             cocos2d::log("JniFun call startSoundRecord over!");  
  13.   
  14.             minfo.env->DeleteLocalRef(minfo.classID);    
  15.         }    
  16.         else  
  17.         {  
  18.             cocos2d::log("JniFun call startSoundRecord error!");  
  19.         }  
  20. #endif  
  21. #if CC_TARGET_PLATFORM == CC_PLATFORM_IOS  
  22.         IosHelper::beginRecord(s_kRecordFileName.c_str());  
  23. #endif  
  24.     }  
  25.   
  26.   
  27.     const char* stopSoundRecord()  
  28.     {  
  29. #if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID  
  30.         std::string str;  
  31.         JniMethodInfo minfo;    
  32.         bool isHave = JniHelper::getStaticMethodInfo(minfo,JAVA_CLASSNAME, "stopSoundRecord""()Ljava/lang/String;");  
  33.         if (isHave)    
  34.         {    
  35.             jstring jFileName = (jstring)minfo.env->CallStaticObjectMethod(minfo.classID, minfo.methodID);   
  36.             const char *newStr = minfo.env->GetStringUTFChars(jFileName, 0);  
  37.             str = newStr;  
  38.             cocos2d::log("JniFun call stopSoundRecord over :");  
  39.             cocos2d::log("%s",str.c_str());  
  40.             minfo.env->ReleaseStringUTFChars(jFileName, newStr);  
  41.             minfo.env->DeleteLocalRef(minfo.classID);   
  42.         }    
  43.         else  
  44.         {  
  45.             cocos2d::log("JniFun call stopSoundRecord error!");  
  46.         }  
  47.         return str.c_str();  
  48. #endif  
  49. #if CC_TARGET_PLATFORM == CC_PLATFORM_IOS  
  50.         IosHelper::endRecord();  
  51.         return s_kRecordFileName.c_str();  
  52. #endif  
  53.         return "";  
  54.     }  




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


     

[java]  view plain  copy
  1.   //开始录音  
  2.        public static void startSoundRecord( String SoundFileName)  
  3. {  
  4.  String SoundFilePath= Environment.getExternalStorageDirectory().getAbsolutePath();    
  5.   
  6. if (filePath != null)  
  7. {  
  8.     File file = new File(filePath);  
  9.     if (file!= null && file.exists())  
  10.     {  
  11.         file.delete();  
  12.     }  
  13.  }  
  14. filePath = SoundFilePath+"/"+SoundFileName;  
  15. recorder = new MediaRecorder();  
  16.               //从麦克风中录音  
  17. recorder.setAudioSource(MediaRecorder.AudioSource.MIC);    
  18.               //设置编码格式为AMR  
  19. recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);    
  20. recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);    
  21. recorder.setOutputFile(SoundFilePath+"/"+SoundFileName);    
  22. try {    
  23.     recorder.prepare();//  
  24.     recorder.start();//  
  25. catch (IllegalStateException e) {    
  26.     e.printStackTrace();    
  27. catch (IOException e) {    
  28.     e.printStackTrace();    
  29. }    
  30. }  
  31.   
  32.       //结束录音  
  33. public static String stopSoundRecord()  
  34. {  
  35. recorder.stop();//   
  36.               recorder.release(); //   
  37.               recorder = null;    
  38. return filePath;  
  39. }  


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


 

[plain]  view plain  copy
  1. <uses-permission android:name="android.permission.RECORD_AUDIO" />    


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


[objc]  view plain  copy
  1.  AVAudioRecorder *recorder = NULL;  
  2. void IosHelper::beginRecord(const charchar *_fileName)  
  3. {  
  4.       if (recorder == nil)  
  5.       {  
  6.         //设置文件名和录音路径  
  7.         NSString *recordFilePath = [NSString stringWithCString:_fileName encoding:NSUTF8StringEncoding];  
  8.           
  9.         NSDictionary *recordSetting = [[NSDictionary alloc] initWithObjectsAndKeys:  
  10.                                        [NSNumber numberWithFloat8000.0],AVSampleRateKey, //采样率  
  11.                                        [NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,  
  12.                                        [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,//采样位数 默认 16  
  13.                                        [NSNumber numberWithInt: 1], AVNumberOfChannelsKey,//通道的数目  
  14.                                        nil nil];  
  15.         //初始化录音  
  16.         NSError *error = nil;  
  17.         recorder = [[ AVAudioRecorder alloc] initWithURL:[NSURL URLWithString:recordFilePath] settings:recordSetting error:&error];  
  18.       }  
  19.       recorder.meteringEnabled = YES;  
  20.       [recorder prepareToRecord];  
  21.       //开始录音  
  22.       UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord;  
  23.       AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory), &sessionCategory);  
  24.       
  25.       // 扬声器播放  
  26.       UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;  
  27.       AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);  
  28.       [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];  
  29.       [[AVAudioSession sharedInstance] setActive:YES error:nil];  
  30.       [recorder record];  
  31. }  
  32.   
  33. const charchar * IosHelper::endRecord()  
  34. {  
  35.         if (recorder == nil)  
  36.         return "";  
  37.         if (recorder.isRecording)  
  38.         [recorder stop];  
  39.     return "";  
  40. }  


                                           
            


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


[cpp]  view plain  copy
  1. std::string kFileName = JniFun::stopSoundRecord();  
  2. sendTalkFile(m_pLocal->GetChairID(),kFileName);  



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

        

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

          

[cpp]  view plain  copy
  1. bool GameBase::RevTalk_File(CMD_GR_C_TableTalk* pNetInfo)  
  2. {  
  3.     if (pNetInfo->strTalkSize == 0)  
  4.     {  
  5.         return true;  
  6.     }  
  7.     static int iIdex = 0;  
  8.     iIdex ++;  
  9.     std::string kFile = utility::toString(cocos2d::CCFileUtils::sharedFileUtils()->getWritablePath(),"TableTalk",iIdex,".arm");  
  10.     FILE *fp = fopen(kFile.c_str(), "wb");  
  11.   
  12.     fseek(fp,0,SEEK_END);  
  13.     fseek(fp,0,SEEK_SET);  
  14.     fwrite(&pNetInfo->strTalkData,sizeof(unsigned char), pNetInfo->strTalkSize,fp);  
  15.     fclose(fp);  
  16.     int iAddTime = pNetInfo->strTalkSize/1200+2.0f;  
  17.     if (iAddTime > 10)  
  18.     {  
  19.         iAddTime = 10;  
  20.     }  
  21.     std::string kDestFile = kFile;  
  22.     utility::StringReplace(kDestFile,"arm","wav");  
  23.         //这里需要做一个解压转换,将ARM转换成WAV  
  24.     ArmFun::ArmToWav(kFile.c_str(),kDestFile.c_str());  
  25.         //为了防止游戏音乐干扰,先静音游戏音乐  
  26.     SoundFun::Instance().PaseBackMusic();  
  27.     SoundFun::Instance().ResumeBackMusic(iAddTime);  
  28.     SoundFun::Instance().PaseEffectMusic();  
  29.     SoundFun::Instance().ResumeEffectMusic(iAddTime);  
  30.         //播放接收到的声音文件  
  31.     SoundFun::Instance().playEffectDirect(kDestFile);  
  32.         //指定玩家显示播放语音的动画图标  
  33.     GamePlayer* pPlayer = getBasePlayerByChairID(pNetInfo->cbChairID);  
  34.     if (pPlayer)  
  35.     {  
  36.         pPlayer->showTalkState(pNetInfo);  
  37.     }  
  38.           
  39.     return true;  
  40. }  


          

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


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

                             

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值