【cocos creator与C++知识分享】 四.Creator接入呀呀语音SDK

插入一只广告: 欢迎成都ccc开发者入群 573289987

在接入之前请到http://www.17yaya.com下载云娃呀呀语音COCOS版本。并且联系客服申请appid(没有appid是无法正常使用功能的)。

下面进入正题,要在cocos creator接入呀呀语音SDK实现即时语音通信功能需要掌握以下知识:
1.NDK编译流程;
2.C++相关知识;
3.js手动绑定相关知识;
4.js相关知识。

前置条件:新建项目->已执行default模板构建和编译android项目

进入目录:
然后打开AndroidManifest.xml文件,修改以下处内容:


在权限部分增加以下权限:

完成以后关闭文件,然后打开build-cfg.json文件。

在此文件中增加jar包的复制命令。
然后进入jni目录,打开Android.mk文件,注意我红色圈中部分需要修改。

进入目录
打开编辑AppActivity.java文件



注意红色抹掉的部分是我项目里需要用的不用管,重点在我用红色框中的部分是必须要加的。下面的if…建议加上。
进入目录
添加以下jar包

此jar包在下面的zip中有。

然后进入目录
在此目录中增加IM_SDK目录,此目录在下载的压缩包中。

加入后
进入classes目录,将压缩包中classes目录中YunVaSDK目录复制到此目录

在此目录中打开AppDelegate.h和AppDelegate.cpp文件。
AppDelegate.h文件内容如下:
//
// GCTestAppDelegate.h
// GCTest
//
// Created by Rohan Kuruvilla on 06/08/2012.
// Copyright MyCompanyName 2012. All rights reserved.
//

#ifndef  _APP_DELEGATE_H_
#define  _APP_DELEGATE_H_

#include "platform/CCApplication.h"
#include "cocos2d.h"
#include "YunVaSDK/YVTool.h"

using namespace YVSDK;
class DispatchMsgNode;
/**
 @brief    The cocos2d Application.
 
 The reason for implement as private inheritance is to hide some interface call by Director.
 */
class  AppDelegate : private cocos2d::Application
{
public:
    AppDelegate();
    virtual ~AppDelegate();
    
    void initGLContextAttrs() override;
    
    /**
     @brief    Implement Director and Scene init code here.
     @return true    Initialize success, app continue.
     @return false   Initialize failed, app terminate.
     */
    virtual bool applicationDidFinishLaunching();
    
    /**
     @brief  The function be called when the application enter background
     @param  the pointer of the application
     */
    virtual void applicationDidEnterBackground();
    
    /**
     @brief  The function be called when the application enter foreground
     @param  the pointer of the application
     */
    virtual void applicationWillEnterForeground();
private:
	DispatchMsgNode* m_dispatchMsgNode;
};

#endif // _APP_DELEGATE_H_

AppDelegate.cpp文件内容如下:

#include "AppDelegate.h"

#include "platform/CCGLView.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
#include "platform/ios/CCGLViewImpl-ios.h"
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_IOS
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include "platform/android/CCGLViewImpl-android.h"
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include "platform/desktop/CCGLViewImpl-desktop.h"
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#if (CC_TARGET_PLATFORM == CC_PLATFORM_MAC)
#include "platform/desktop/CCGLViewImpl-desktop.h"
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_MAC

#include "base/CCDirector.h"
#include "base/CCEventDispatcher.h"

#include "js_module_register.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) && PACKAGE_AS
#include "SDKManager.h"
#include "jsb_anysdk_protocols_auto.hpp"
#include "manualanysdkbindings.hpp"
using namespace anysdk::framework;
#endif

USING_NS_CC;

class DispatchMsgNode : public cocos2d::Node
{
public:
	bool init()
	{
		isschedule = false;
		return  Node::init();
	}
	CREATE_FUNC(DispatchMsgNode);
	void startDispatch()
	{
		if (isschedule) return;
		isschedule = true;
		Director::getInstance()->getScheduler()->scheduleUpdate(this, 0, false);
	}
	void stopDispatch()
	{
		if (!isschedule) return;
		isschedule = false;
		Director::getInstance()->getScheduler()->unscheduleUpdate(this);
	}
	void update(float dt)
	{
		YVTool::getInstance()->dispatchMsg(dt);
	}
private:
	bool isschedule;

};


AppDelegate::AppDelegate()
{
	m_dispatchMsgNode = NULL;
}

AppDelegate::~AppDelegate()
{
    ScriptEngineManager::destroyInstance();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) && PACKAGE_AS
    SDKManager::getInstance()->purge();
#endif
	if (m_dispatchMsgNode != NULL)
	{
		m_dispatchMsgNode->stopDispatch();
		m_dispatchMsgNode->release();
		m_dispatchMsgNode = NULL;
	}
}

void AppDelegate::initGLContextAttrs()
{
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};
    
    GLView::setGLContextAttrs(glContextAttrs);
}

bool AppDelegate::applicationDidFinishLaunching()
{
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS && PACKAGE_AS
    SDKManager::getInstance()->loadAllPlugins();
#endif
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
#if(CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
        glview = GLViewImpl::create("mahjong");
#else
        glview = GLViewImpl::createWithRect("mahjong", Rect(0,0,900,640));
#endif
        director->setOpenGLView(glview);
    }
    
    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0 / 60);
    
    js_module_register();
    
    ScriptingCore* sc = ScriptingCore::getInstance();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS) && PACKAGE_AS    
    sc->addRegisterCallback(register_all_anysdk_framework);
    sc->addRegisterCallback(register_all_anysdk_manual);
#endif
    sc->start();
    sc->runScript("script/jsb_boot.js");
#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    sc->enableDebugger();
#endif
    ScriptEngineManager::getInstance()->setScriptEngine(sc);
    ScriptingCore::getInstance()->runScript("main.js");
    if (m_dispatchMsgNode == NULL)
	{
		m_dispatchMsgNode = DispatchMsgNode::create();
		m_dispatchMsgNode->retain();
		m_dispatchMsgNode->startDispatch();
	}
    return true;
}

// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground()
{
    auto director = Director::getInstance();
    director->stopAnimation();
    director->getEventDispatcher()->dispatchCustomEvent("game_on_hide");
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground()
{
    auto director = Director::getInstance();
    director->startAnimation();
    director->getEventDispatcher()->dispatchCustomEvent("game_on_show");
}

完成之后,android接入的部分就完成了。

进入目录
增加目录audio-yvsdk,新建以下两个文件

IMDispatchMsgNode.h
#ifndef IMDispatchMsgNode_h
#define IMDispatchMsgNode_h
#include
#include <stdio.h>
#include “cocos2d.h”
#include “YunVaSDK/YVTool.h”

using namespace YVSDK;
class IMDispatchMsgNode: public cocos2d::Node,
public YVListern::YVUpLoadFileListern,
public YVListern::YVFinishPlayListern,
public YVListern::YVStopRecordListern,
public YVListern::YVLoginListern,
public YVListern::YVDownLoadFileListern,
public YVListern::YVRecordVoiceListern

{
public:
    IMDispatchMsgNode();
    ~IMDispatchMsgNode();
	
	class Delegate{
	public:
		virtual ~Delegate() {}
		virtual void onMessage(IMDispatchMsgNode* node, const std::string& data) = 0;
	};
    bool init(const Delegate& delegate);
    void addListern();
	
	void initSDK(unsigned long appid,unsigned int length);
	void cpLogin(std::string nickname,std::string uid);
	void destroy();
    bool startRecord();
    void stopRecord();
    bool playRecord();
    void playFromUrl(std::string url);
    void stopPlay();
    void upLoadFile(std::string path);
    
    void onLoginListern(CPLoginResponce* r);
    void onStopRecordListern(RecordStopNotify* r);
    void onFinishPlayListern(StartPlayVoiceRespond* r);
    void onUpLoadFileListern(UpLoadFileRespond* r);
    void onDownLoadFileListern(DownLoadFileRespond* r);
    void onRecordVoiceListern(RecordVoiceNotify*);
	// static IMDispatchMsgNode getInstance();
    
private:
    bool _sdk_inited;
    static int time;
	unsigned int m_length;
	Delegate* _delegate;
	// static IMDispatchMsgNode m_instance;
};

#endif /* IMDispatchMsgNode_h */

IMDispatchMsgNode.cpp
#include “IMDispatchMsgNode.h”
#include
#include “base/CCEventDispatcher.h”
#include “base/CCEventListenerCustom.h”
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <unistd.h>
#endif

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#include <stdlib.h>
#include <string.h>
#endif
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
#define RECODEPATH ("C:/imsdkk/path/test.amr")
#define DOWNPATH ("C:/imsdkk/path/http.amr")

#else

#define RECODEPATH (FileUtils::getInstance()->getWritablePath() +"test.amr")
#define DOWNPATH (FileUtils::getInstance()->getWritablePath()+"dwtest.amr")

#endif
USING_NS_CC;
// using namespace CocosDenshion;
using namespace std;
using namespace cocos2d;
#define BUFFER_SIZE  8192
#define MAX_FILENAME 512

int IMDispatchMsgNode::time = 0;
IMDispatchMsgNode::IMDispatchMsgNode()
:_delegate(nullptr)
,_sdk_inited(false)
{
}
IMDispatchMsgNode::~IMDispatchMsgNode(){
}
bool IMDispatchMsgNode::init(const Delegate& delegate) {
	_delegate = const_cast<Delegate*>(&delegate);
	if(!Node::init()){
		return false;
	}
	addListern();
	return true;
}
void IMDispatchMsgNode::addListern() {
	YVTool::getInstance()->addRecordVoiceListern(this);
	YVTool::getInstance()->addDownLoadFileListern(this);
	YVTool::getInstance()->addFinishPlayListern(this);
	YVTool::getInstance()->addLoginListern(this);
	YVTool::getInstance()->addStopRecordListern(this);
	YVTool::getInstance()->addUpLoadFileListern(this);
	
}

void IMDispatchMsgNode::destroy(){
	YVTool::getInstance()->delRecordVoiceListern(this);
	YVTool::getInstance()->delDownLoadFileListern(this);
	YVTool::getInstance()->delFinishPlayListern(this);
	YVTool::getInstance()->delLoginListern(this);
	YVTool::getInstance()->delStopRecordListern(this);
	YVTool::getInstance()->delUpLoadFileListern(this);
	
	YVTool::getInstance()->cpLogout();
	YVTool::getInstance()->releaseSDK();
	Director::getInstance()->end();
} 

void IMDispatchMsgNode::initSDK(unsigned long appid,unsigned int length){
	auto director = Director::getInstance();
	if(_sdk_inited)return; _sdk_inited=true;
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
	std::string path = "C:/imsdkk/path/";
#else
	std::string path = FileUtils::getInstance()->getWritablePath();
#endif
	YVTool::getInstance()->initSDK(appid, path, false, false);
	//m_length = length;
}

void IMDispatchMsgNode::cpLogin(std::string nickname,std::string uid){
	YVTool::getInstance()->cpLogin(nickname, uid);
}
bool IMDispatchMsgNode::startRecord(){
	YVTool::getInstance()->startRecord(RECODEPATH,2);//2:边录音边上传
}
void IMDispatchMsgNode::stopRecord(){
    YVTool::getInstance()->stopRecord();
}
bool IMDispatchMsgNode::playRecord(){
	YVTool::getInstance()->playRecord("", RECODEPATH, "");
}
void IMDispatchMsgNode::playFromUrl(std::string url){
// #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
    // if (FileUtils::getInstance()->isFileExist(DOWNPATH))
    // {
        // rmdir(DOWNPATH.c_str());
    // }
// #else
    // if (isFileExist(DOWNPATH))
    // {
        // remove(DOWNPATH.c_str());
    // }
// #endif
	
	YVTool::getInstance()->playRecord(url,"");   //只云播放
}

void IMDispatchMsgNode::stopPlay(){
    YVTool::getInstance()->stopPlay();
}
void IMDispatchMsgNode::upLoadFile(std::string path){
    YVTool::getInstance()->upLoadFile(RECODEPATH);
}

void IMDispatchMsgNode::onLoginListern(CPLoginResponce* r){
    //auto  event = new EventCustom("YVSDK_LOGIN_COMPLETED");
    if (r->result == 0) {
        cocos2d::log("语音模块登录成功");
		YVTool::getInstance()->setRecord(10, true);
		_delegate->onMessage(this, "{\"name\":\"YVSDK_LOGIN_COMPLETED\",\"result\":0}");
    }else{
		
		cocos2d::log("登录失败");
	_delegate->onMessage(this, "{\"name\":\"YVSDK_LOGIN_COMPLETED\",\"result\":1}");
    }
}
void IMDispatchMsgNode::onStopRecordListern(RecordStopNotify* r){
    cocos2d::log("yvsdk onStopRecordListern");
    cocos2d::log("time:%d,path:%s",r->time,r->strfilepath.c_str());
    char ttStr1[500] = { 0 };
    const char* ttFormat = "{\"name\":\"YVSDK_STOP_RECORD\",\"time\":%d,\"path\":\"%s\"}";
    IMDispatchMsgNode::time = r->time;
    sprintf(ttStr1, ttFormat, r->time, r->strfilepath.c_str());
	_delegate->onMessage(this, ttStr1);
}
void IMDispatchMsgNode::onFinishPlayListern(StartPlayVoiceRespond* r){
	cocos2d::log("播放完成");
	_delegate->onMessage(this, "{\"name\":\"YVSDK_PLAY_COMPLETED\",\"result\":0}");
}
void IMDispatchMsgNode::onUpLoadFileListern(UpLoadFileRespond* r){
	char ttStr1[200] = { 0 };
    if(r->result == 0){
        cocos2d::log("yvsdk upfile successful:%s",r->fileurl.c_str());
        const char* ttFormat = "{\"name\":\"YVSDK_UPLOAD_COMPLETED\",\"result\":%d,\"url\":\"%s\"}";
        sprintf(ttStr1, ttFormat, r->result, r->fileurl.c_str());
        //event->setUserData(ttStr1);
    }else{
        cocos2d::log("yvsdk upfile fail:%s",r->msg.c_str());
        //char ttStr1[200] = { 0 };
        const char* ttFormat = "{\"name\":\"YVSDK_UPLOAD_COMPLETED\",\"result\":%d,\"url\":\"%s\"}";
        sprintf(ttStr1, ttFormat, r->result, r->msg.c_str());
        //event->setUserData(ttStr1);
    }
    //Director::getInstance()->getEventDispatcher()->dispatchEvent(event);
	_delegate->onMessage(this, ttStr1);
}
void IMDispatchMsgNode::onDownLoadFileListern(DownLoadFileRespond* r){
	//auto  event = new EventCustom("YVSDK_DOWNLOAD");
	char ttStr1[200] = { 0 };
    const char* ttFormat = "{\"name\":\"YVSDK_DOWNLOAD\",\"result\":%s,\"localpath\":\"%s\",\"percent\":%s}";
	if(r->result == 0){
		cocos2d::log("yvsdk DownLoadFile success");
	}
	else{
		cocos2d::log("yvsdk DownLoadFile failed");
	}
    sprintf(ttStr1, ttFormat, r->result, r->filename.c_str(),r->percent);
	_delegate->onMessage(this, ttStr1);
}
void IMDispatchMsgNode::onRecordVoiceListern(RecordVoiceNotify* r)
{
	cocos2d::log("音量变化回调");
	char ttStr1[200] = { 0 };
	const char* ttFormat = "{\"name\":\"YVSDK_VOICE_CHANGE\",\"volume\":%d}";
	sprintf(ttStr1, ttFormat, r->volume);
	_delegate->onMessage(this, ttStr1);
}

完成之后回到cocos目录将刚才的YunVaSDK目录再添加到此处并且打开android.mk文件。


进入目录:

新建audio-yvsdk目录

添加以上两个文件。
jsb_IMDispatchMsgNode.h

jsb_IMDispatchMsgNode.cpp
因本文字数限制和商业版本版权限制,此文件不予公布。

回到目录manual,打开js_module_register.cpp文件


OK,完成,现在JS里就可以调用了。

下面cplogin后面传id,注意登录的id不能重复,前面的账号可以重复。
对了还要把资源包放到以下目录

本文完毕…
欢迎加入群:573289987 交流学习

14赞

C姐出来,你可以做一个连续报道了!

不懂!!

感谢分享,学习了!

感谢分享!

好样,有用了!

这个语音不是很好,不如用GVoice或者声网的。还有应该不是C姐

兄弟我们用这个语音SDK还不错啊。没有出现什么问题呢? 你们用的时候有什么无法使用情况吗?

有没有ios版的,分享下。我们要多试几个。你有的话,我就抄了,不研究了!:grin:

1赞

目前用的这几个,声网的效果是最好的。不过也是最贵的。gvoice一般般。接个呀呀的试试效果

这是通用版本

manualyunvasdkbindings.zip (2.4 KB)
感谢‘欧阳锋’的分享,感谢cocos。
分享上面代码缺失的部分。

世人迷惘,世界上最远的距离莫过于楼上和楼下,请看你头上

@7437280 IMDispatchMsgNode::init 这个函数好像没有调用,然后我在js:
var node = new IMDispatchMsgNode();
IMDispatchMsgNode.prototype.init.call(node, null);
结果
登录回调的时候_delegate = NULL;

这个方法是在手动绑定代码里去调用的,也就是我没有提供的部分

嗯嗯,我已经解决,好的谢谢!

anysdk admob广告这几天无法显示了 怎么回事啊

楼主,就是我在creator中接其他SDK前面的步骤基本都完成了,但是后面在Classes中的AppDelegate.cpp和AppDelegate.h需要做哪些改动呢?

看看他们官方给的示例吧

没搞明白。服务端不用接入吗?客户端是怎么个逻辑接收语音消息的。