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

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

我在(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.getStartOffset(), 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, this.mRightVolume);
		} catch (final Exception e) {
			mediaPlayer = null;
			Log.e(Cocos2dxMusic.TAG, "error: " + e.getMessage(), e);
		}

		return mediaPlayer;
	}

2、
音乐播放函数
//pszFilePath: 音乐文件名
//bLoop: 是否循环播放,音乐文件我们一般设置为循环播放,看具体情况
void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop)
{
    std::string fullPath = getFullPathWithoutAssetsPrefix(pszFilePath);
    playBackgroundMusicJNI(fullPath.c_str(), 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的过程,会有一些时间上的延时。
时间: 2024-08-29 05:36:35

cocos2dx 音频模块分析(2):背景音乐的相关文章

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

cocos2dx 音频模块分析(3):背景音乐部分 我在(2)已经分析了背景音乐文件的预加载preloadBackgroundMusic和播放playBackgroundMusic两个函数, 这里接着分析,还是基于android平台: 1. //暂停函数,用于音乐的暂停 void SimpleAudioEngine::pauseBackgroundMusic() { //在SimpleAudioEngineJni.cpp源文件中定义 pauseBackgroundMusicJNI(); } //

cocos2dx 音频模块分析(4): 音效部分

我们上面几篇分析了cocos2dx音频模块的音乐部分,从这篇开始, 我们分析下音效部分: 1. //预加载音效文件:pszFilePath 音效文件名 void SimpleAudioEngine::preloadEffect(const char* pszFilePath) { //获取音效文件的全路径,如果是apk包里的路径,则不包含assets/ std::string fullPath = getFullPathWithoutAssetsPrefix(pszFilePath); prel

cocos2dx 音频模块分析(5): 音效部分

我们上一篇中分析的音效部分的预加载和播放函数,这一篇来分析下其他函数: 1. 暂停某个播放中的音效 //注意这里的nSoundId不是java端的soundID,而是streamID //不要被参数的名字迷惑了. void SimpleAudioEngine::pauseEffect(unsigned int nSoundId) { pauseEffectJNI(nSoundId); } -->> void pauseEffectJNI(unsigned int nSoundId) { //

WebRTC源码分析:音频模块结构分析

一.概要介绍WebRTC的音频处理流程,见下图: webRTC将音频会话抽象为一个通道Channel,譬如A与B进行音频通话,则A需要建立一个Channel与B进行音频数据传输.上图中有三个Channel,每个Channel包含编解码和RTP/RTCP发送功能. 以一个Channel而言,应用程序中将包含三个活动线程,录音线程,音频接收线程和播放线程. 1)录音线程:负责麦克风音频的采集,见图中红色路径,采集到音频后,缓存到一定长度,进行音频处理,主要包括EC,AGC和NS等.然后送到Chann

css扁平化博客学习总结(一)模块分析

一.模块分析 1.每开发一个项目之前,首先要对项目进行一个大致规划,它到底要做什么功能,它有什么具体需求. 2.所以需要进行模块化分析,把这些东西具象化,把一个问题模块化,对需求有一个宏观的了解. 3.有一个模块的概念后,把模块进行细分,细分成更小的模块,然后逐一击破. 不进行模块分析的恶劣影响: 不做模块分析,一上手就想到哪儿做到哪儿,很容易一叶障目,不能看到全貌,你会发现做着做着就失控了.因为有些地方出现了矛盾,你会发现要修改的成本很高,还不如推倒重写. 新手很容易着眼于细节,首先不应该从细

学生信息管理系统(四)——模块分析

学生信息管理系统已经敲完了,也进行了第一次验收,结果不是太理想.之前的总结也没有及时发表.现在重新复习一遍,把它发表. 从今天开始,我们就进入了代码分析阶段.现在我们就来分析一下模块中的几个函数. Public Function ExecuteSQL(ByVal SQL As String, MsgString As String) As ADODB.Recordset 'executes SQL and returns Recordset Dim cnn As ADODB.Connection

cocos2d-x 源码分析 : control 源码分析 ( 控制类组件 controlButton)

源码版本来自3.1rc 转载请注明 cocos2d-x源码分析总目录 http://blog.csdn.net/u011225840/article/details/31743129 1.继承结构 control的设计整体感觉挺美的,在父类control定义了整个控制事件的基础以及管理,虽然其继承了Layer,但其本身和UI组件的实现并没有关联.在子类(controlButton,controlSwitch,controlStepper等中实现不同的UI组件).下面通过源码来分析control与

ABP之模块分析

ABP之模块分析 本篇作为我ABP介绍的第三篇文章,这次想讲下模块的,ABP文档已经有模块这方面的介绍,但是它只讲到如何使用模块,我想详细讲解下它模块的设计思路. ABP 框架提供了创建和组装模块的基础,一个模块能够依赖于另一个模块.在通常情况 下,一个程序集就可以看成是一个模块.在 ABP 框架中,一个模块通过一个类来定义,而这 个类要继承自 AbpModule. 其实它的设计思路很简单: 1.加载bin目录下的所有dll 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

cocos2D-X源码分析之从cocos2D-X学习OpenGL(3)----BATCH_COMMAND

个人原创,欢迎转载,转载请注明原文地址http://blog.csdn.net/bill_man 上一篇介绍了QUAD_COMMAND渲染命令,顺带介绍了VAO和VBO,这一篇介绍批处理渲染命令BatchCommand,批处理命令的处理在Render中比较简单 else if(commandType == RenderCommand::Type:: BATCH_COMMAND) { //将之前缓存的绘制 flush(); auto cmd = static_cast<BatchCommand*>