cocos2dx 音频模块分析(1):背景音乐

cocos2dx 音频模块分析(1):背景音乐,第1张

概述cocos2dx音效的实现分析: 上面那个图是cocos2dx音效部分的实现结构图,总体的思想是: 使用同一个SimpleAudioEngine.h头文件,然后在不同平台下对应不同 的实现文件,不同平台编译不同的实现文件,这就要求这个类的函数定义 成员在各个平台下统一。其实这也可以理解为一种跨平台的实现方式。 这里分析android部分: 1、预加载背景音乐void SimpleAudioEng

cocos2dx音效的实现分析:


上面那个图是cocos2dx音效部分的实现结构图,总体的思想是:
使用同一个SimpleAudioEngine.h头文件,然后在不同平台下对应不同
的实现文件,不同平台编译不同的实现文件,这就要求这个类的函数定义
成员在各个平台下统一。其实这也可以理解为一种跨平台的实现方式。


这里分析androID部分:

1、预加载背景音乐voID SimpleAudioEngine::preloadBackgroundMusic(const char* pszfilePath){    std::string fullPath = getFullPathWithoutAssetsPrefix(pszfilePath);    preloadBackgroundMusicJNI(fullPath.c_str());}-->>//得到音乐文件的路径,如果是包里的文件,则去掉assets/前缀static std::string getFullPathWithoutAssetsPrefix(const char* pszfilename){    // Changing file path to full path    //获取文件的路径,以后有时间会分析下CCfileUtils这个类的实现    std::string fullPath = CCfileUtils::sharedfileUtils()->fullPathForfilename(pszfilename);    // Removing `assets` since it isn't needed for the API of playing sound.    size_t pos = fullPath.find("assets/");    if (pos == 0)    {        fullPath = fullPath.substr(strlen("assets/"));    }    return fullPath;}-->>preloadBackgroundMusicJNI://这里其实就是调用jni的方法:voID preloadBackgroundMusicJNI(const char *path)    {        // voID playBackgroundMusic(String,boolean)        JniMethodInfo methodInfo;                if (! getStaticmethodInfo(methodInfo,"preloadBackgroundMusic","(Ljava/lang/String;)V"))        {                        return;        }                Jstring stringArg = methodInfo.env->NewStringUTF(path);        methodInfo.env->CallStaticVoIDMethod(methodInfo.classID,methodInfo.methodID,stringArg);        methodInfo.env->DeleteLocalRef(stringArg);        methodInfo.env->DeleteLocalRef(methodInfo.classID);    }-->>java端的代码:public static voID preloadBackgroundMusic(final String pPath) {		Cocos2dxHelper.sCocos2dMusic.preloadBackgroundMusic(pPath);	}-->>public voID preloadBackgroundMusic(final String pPath) {		if ((this.mCurrentPath == null) || (!this.mCurrentPath.equals(pPath))) {			// preload new background music			// release old resource and create a new one			if (this.mBackgroundMediaPlayer != null) {				this.mBackgroundMediaPlayer.release();			}			this.mBackgroundMediaPlayer = this.createMediaplayer(pPath);			// record the path			this.mCurrentPath = pPath;		}	}-->>	private MediaPlayer createMediaplayer(final String pPath) {	//其实就是创建了一个androID下的MediaPlayer媒体播放实例,后面的播放,暂停之类	//的,都是这个实例去控制。		MediaPlayer mediaPlayer = new MediaPlayer(); 		try {			if (pPath.startsWith("/")) {				final fileinputStream fis = new fileinputStream(pPath);				mediaPlayer.setDataSource(fis.getFD());				fis.close();			} else {				final AssetfileDescriptor assetfileDescritor = this.mContext.getAssets().openFd(pPath);				mediaPlayer.setDataSource(assetfileDescritor.getfileDescriptor(),assetfileDescritor.getStartOffset(),assetfileDescritor.getLength());			}			mediaPlayer.prepare();			mediaPlayer.setVolume(this.mleftVolume,this.mRightVolume);		} catch (final Exception e) {			mediaPlayer = null;			Log.e(Cocos2dxMusic.TAG,"error: " + e.getMessage(),e);		}		return mediaPlayer;	}-->>播放背景音乐,这里可以不用预先加载,就直接播放。voID SimpleAudioEngine::playBackgroundMusic(const char* pszfilePath,bool bLoop){    std::string fullPath = getFullPathWithoutAssetsPrefix(pszfilePath);    playBackgroundMusicJNI(fullPath.c_str(),bLoop);}

我在(1)已经分析了一些东西,这里接着分析,这一篇我们主要分析背景音乐文件的播放,还是基于androID平台:1、这里只是背景音乐的预加载,为什么要进行预加载呢?主要是加载音乐文件是比较耗时的,如果我们没有预加载就直接播放也是可以的,但是会有一定的延时,因为如果没有预加载,就直接播放,也是会先进行加载音乐文件,然后进行播放。voID SimpleAudioEngine::preloadBackgroundMusic(const char* pszfilePath){    std::string fullPath = getFullPathWithoutAssetsPrefix(pszfilePath);    preloadBackgroundMusicJNI(fullPath.c_str());}其实加载背景音乐最终调用androID端:public voID preloadBackgroundMusic(final String pPath) {		if ((this.mCurrentPath == null) || (!this.mCurrentPath.equals(pPath))) {			// preload new background music			// release old resource and create a new one			// 如果我们播放的是一个新的背景音乐文件,那么我们需要先释放旧的播放器,然后创建一个新的			// Releases resources associated with this MediaPlayer object.			if (this.mBackgroundMediaPlayer != null) {				this.mBackgroundMediaPlayer.release();			}                        //创建一个播放器即MediaPlayer类的实例			this.mBackgroundMediaPlayer = this.createMediaplayer(pPath);			// record the path			// 记录当前播放的背景音乐文件,因为下次如果播放的是同一个音乐			// 文件,那么我们就可以直接进行播放了,不用再重新创建MediaPlayer类的实例			this.mCurrentPath = pPath; 		}	}----->>>	/**	 * create mediaplayer for music	 * 	 * @param pPath	 *            the pPath relative to assets	 * @return	 */	private MediaPlayer createMediaplayer(final String pPath) {		MediaPlayer mediaPlayer = new MediaPlayer();		try {		        //对绝对路径和包里的路径进行区分处理,当最终的目的就是设置播放源			if (pPath.startsWith("/")) {				final fileinputStream fis = new fileinputStream(pPath);				mediaPlayer.setDataSource(fis.getFD());				fis.close();			} else {				final AssetfileDescriptor assetfileDescritor = this.mContext.getAssets().openFd(pPath);				mediaPlayer.setDataSource(assetfileDescritor.getfileDescriptor(),assetfileDescritor.getLength());			}                        //播放器前需要做些准备工作,这个只是androID的API,不明白的话,查下文档。			/**		     * Prepares the player for playback,synchronously.		     *		     * After setting the datasource and the display surface,you need to either		     * call prepare() or prepareAsync(). For files,it is OK to call prepare(),* which blocks until MediaPlayer is ready for playback.		     *		     * @throws IllegalStateException if it is called in an invalID state		     */			mediaPlayer.prepare();                        			//设置声音音量			mediaPlayer.setVolume(this.mleftVolume,e);		}		return mediaPlayer;	}2、音乐播放函数//pszfilePath: 音乐文件名//bLoop: 是否循环播放,音乐文件我们一般设置为循环播放,看具体情况voID SimpleAudioEngine::playBackgroundMusic(const char* pszfilePath,bLoop);}--->>> 最终都会调用到androID端的playBackgroundMusic函数,并把文件路径,是否循环播放传进来	public voID playBackgroundMusic(final String path,final boolean isLoop) {		if (mCurrentPath == null) {			// it is the first time to play background music or end() was called			// 如果以前没有播放过音乐文件,那么重新创建一个,上面的英文注释很清楚			mBackgroundMediaPlayer = createMediaplayer(path);			mCurrentPath = path;		} else {			if (!mCurrentPath.equals(path)) {				// play new background music                                 				//如果这次播放的音乐文件和上次的不同,即是一个新的音乐文件,				//那么就需要先释放掉旧的,然后创建一个新的。				// release old resource and create a new one				if (mBackgroundMediaPlayer != null) {					mBackgroundMediaPlayer.release();				}				mBackgroundMediaPlayer = createMediaplayer(path);				// record the path				mCurrentPath = path;			}		}		if (mBackgroundMediaPlayer == null) {			Log.e(Cocos2dxMusic.TAG,"playBackgroundMusic: background media player is null");		} else {			try {				// if the music is playing or paused,stop it				// 对playing or paused,stop三种情况进行分别处理				if (mPaused) {				        //如果是暂停状态,那么就把播放进度设置到0,然后开始。					//这就意味着,如果我们调用了暂停,然后又调用play,那么					//音乐将会从头开始播放,而不是从暂停的地方接着播放。					    /**					     * Seeks to specifIEd time position.					     *					     * @param msec the offset in milliseconds from the start to seek to					     * @throws IllegalStateException if the internal player engine has not been					     * initialized					     */					mBackgroundMediaPlayer.seekTo(0);					/**				     * Starts(开始) or resumes playback(恢复播放). If playback had prevIoUsly been paused,* playback will continue from where it was paused. If playback had				     * been stopped,or never started before,playback will start at the				     * beginning.				     * start函数两个功能,一个是开始播放,一个是恢复播放				     * 1、如果stopped或者never started before(第一次开始),那么就从头开始播放				     * 2、如果paused即暂停,那么将会从暂停的地方接着播放。				     */					mBackgroundMediaPlayer.start();				} else if (mBackgroundMediaPlayer.isPlaying()) {				        //如果处于播放状态,则回到开始,从头播放					mBackgroundMediaPlayer.seekTo(0);				} else {				        //如果处于stop状态,则从新播放,上面已经说明了start函数的两个作用					mBackgroundMediaPlayer.start();				}                                /*				总结:其实对上面三种情况分别处理,最终达到的效果都是一样的,				那就是从头开始播放背景音乐文件。				*/				//设置是否循环播放				mBackgroundMediaPlayer.setLooPing(isLoop);				//mPaused 表示设为false,表示不处于暂停状态				mPaused = false;				//是否循环播放记录				mIsLoop = isLoop;			} catch (final Exception e) {				Log.e(Cocos2dxMusic.TAG,"playBackgroundMusic: error state");			}		}	}3、总结:从上面的分析我们可以知道,如果预先进行加载即先创建一个MediaPlayer,那么我们播放时可以直接进行播放,如果我们我们没有提前进行预加载,而是直接调用playBackgroundMusic函数,也可以进行播放,只不过会有一个创建MediaPlayer的过程,会有一些时间上的延时。

我在(2)已经分析了背景音乐文件的预加载preloadBackgroundMusic和播放playBackgroundMusic两个函数,这里接着分析,还是基于androID平台:1、//暂停函数,用于音乐的暂停voID SimpleAudioEngine::pauseBackgroundMusic(){    //在SimpleAudioEngineJni.cpp源文件中定义    pauseBackgroundMusicJNI();}//--pauseBackgroundMusicJNI--->>>voID pauseBackgroundMusicJNI()    {        // voID pauseBackgroundMusic()                JniMethodInfo methodInfo;                if (! getStaticmethodInfo(methodInfo,"pauseBackgroundMusic","()V"))        {            return;        }        //通过jni调用java端的函数,调用的是Cocos2dxHelper类中的	/*	public static voID pauseBackgroundMusic() {		Cocos2dxHelper.sCocos2dMusic.pauseBackgroundMusic();	}	*/        methodInfo.env->CallStaticVoIDMethod(methodInfo.classID,methodInfo.methodID);        methodInfo.env->DeleteLocalRef(methodInfo.classID);    }    最终调用的是Cocos2dxMusic类中的    public voID pauseBackgroundMusic() {                //mBackgroundMediaPlayer在(2)中有分析过,创建的MediaPlayer实例		if (this.mBackgroundMediaPlayer != null && this.mBackgroundMediaPlayer.isPlaying()) {			this.mBackgroundMediaPlayer.pause();			this.mPaused = true;  //是否暂停标志		}	}2、恢复播放voID SimpleAudioEngine::resumeBackgroundMusic(){    resumeBackgroundMusicJNI();}其实和上面暂停的调用过程是一样的,就不分析了,直接进入java端看最终调用的函数,public voID resumeBackgroundMusic() {                //这里只有处于暂停状态时即mPaused变量为true时,才会接着		//上次播放的位置开始播放		if (this.mBackgroundMediaPlayer != null && this.mPaused) {			this.mBackgroundMediaPlayer.start();			this.mPaused = false; //把暂停标志位设置false		}	}3、从头开始播放音乐文件voID rewindBackgroundMusicJNI()    {        // voID rewindBackgroundMusic()                JniMethodInfo methodInfo;                if (! getStaticmethodInfo(methodInfo,"rewindBackgroundMusic","()V"))        {            return;        }                methodInfo.env->CallStaticVoIDMethod(methodInfo.classID,methodInfo.methodID);        methodInfo.env->DeleteLocalRef(methodInfo.classID);    }    --->>//java端函数    //这个函数最终也是会调用playBackgroundMusic函数,但是和playBackgroundMusic有一点不同,    //playBackgroundMusic需要传入音乐文件名,可以播放和上一次一样的音乐也可以和是上一次不一样的    //音乐,但是rewindBackgroundMusic函数只有在mBackgroundMediaPlayer不为null时才执行,    //也就是必须播放过音乐,且播放的是上次播放的音乐,只不过这次是从头开始播放    public voID rewindBackgroundMusic() {		if (this.mBackgroundMediaPlayer != null) {			playBackgroundMusic(mCurrentPath,mIsLoop);		}	}4、停止播放音乐文件voID stopBackgroundMusicJNI()    {        // voID stopBackgroundMusic()                JniMethodInfo methodInfo;                if (! getStaticmethodInfo(methodInfo,"stopBackgroundMusic",methodInfo.methodID);        methodInfo.env->DeleteLocalRef(methodInfo.classID);    }    --->>>//java端函数:    public voID stopBackgroundMusic() {		if (this.mBackgroundMediaPlayer != null) {			mBackgroundMediaPlayer.release();			//不太明白这里为什么有从新创建了MediaPlayer实例			//可能是一些特殊情况下会出现问题?        	mBackgroundMediaPlayer = createMediaplayer(mCurrentPath);			// should set the state,if not,the following sequence will be error			// play -> pause -> stop -> resume			//为什么设置mPaused标志,直接看上面的英文注释			this.mPaused = false;		}	}5、返回是否处于播放状态 bool isBackgroundMusicPlayingJNI()    {        // boolean rewindBackgroundMusic()                JniMethodInfo methodInfo;        jboolean ret = false;                if (! getStaticmethodInfo(methodInfo,"isBackgroundMusicPlaying","()Z"))        {            return ret;        }                ret = methodInfo.env->CallStaticBooleanMethod(methodInfo.classID,methodInfo.methodID);        methodInfo.env->DeleteLocalRef(methodInfo.classID);                return ret;    }    --->>>//java端函数,没什么东西    	public boolean isBackgroundMusicPlaying() {		boolean ret = false;		if (this.mBackgroundMediaPlayer == null) {			ret = false;		} else {			ret = this.mBackgroundMediaPlayer.isPlaying();		}		return ret;	}6、获取播放声音音量值    float getBackgroundMusicVolumeJNI()    {        // float getBackgroundMusicVolume()                JniMethodInfo methodInfo;        jfloat ret = -1.0;                if (! getStaticmethodInfo(methodInfo,"getBackgroundMusicVolume","()F"))        {            return ret;        }                ret = methodInfo.env->CallStaticfloatMethod(methodInfo.classID,methodInfo.methodID);        methodInfo.env->DeleteLocalRef(methodInfo.classID);                return ret;    }------->>>//java    	public float getBackgroundVolume() {		if (this.mBackgroundMediaPlayer != null) {			return (this.mleftVolume + this.mRightVolume) / 2;		} else {			return 0.0f;		}	} 7、设置声音音量值    voID setBackgroundMusicVolumeJNI(float volume)    {        // voID setBackgroundMusicVolume()                JniMethodInfo methodInfo;                if (! getStaticmethodInfo(methodInfo,"setBackgroundMusicVolume","(F)V"))        {            return ;        }                methodInfo.env->CallStaticVoIDMethod(methodInfo.classID,volume);        methodInfo.env->DeleteLocalRef(methodInfo.classID);    }   ---->>>java   public voID setBackgroundVolume(float pVolume) {		if (pVolume < 0.0f) {			pVolume = 0.0f;		}		if (pVolume > 1.0f) {			pVolume = 1.0f;		}		this.mleftVolume = this.mRightVolume = pVolume;		if (this.mBackgroundMediaPlayer != null) {			this.mBackgroundMediaPlayer.setVolume(this.mleftVolume,this.mRightVolume);		}	}8、end函数,这个一般在退出游戏是调用,关掉所有的音乐和音效。 voID endJNI()    {        // voID end()                JniMethodInfo methodInfo;                if (! getStaticmethodInfo(methodInfo,"end","()V"))        {            return ;        }                methodInfo.env->CallStaticVoIDMethod(methodInfo.classID,methodInfo.methodID);        methodInfo.env->DeleteLocalRef(methodInfo.classID);    }    --->>>//java端函数    public static voID end() {		Cocos2dxHelper.sCocos2dMusic.end(); //背景音乐文件的处理		Cocos2dxHelper.sCocos2dSound.end();	}    ---->>>>/////背景音乐文件的处理	public voID end() {		if (this.mBackgroundMediaPlayer != null) {			this.mBackgroundMediaPlayer.release();		}                //把所有的变量恢复初始值		/*		private voID initData() {		this.mleftVolume = 0.5f;		this.mRightVolume = 0.5f;		this.mBackgroundMediaPlayer = null;		this.mPaused = false;		this.mCurrentPath = null;		}				*/		this.initData();	}    
总结

以上是内存溢出为你收集整理的cocos2dx 音频模块分析(1):背景音乐全部内容,希望文章能够帮你解决cocos2dx 音频模块分析(1):背景音乐所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1076614.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-26
下一篇 2022-05-26

发表评论

登录后才能评论

评论列表(0条)

保存