cocos2dx 音频模块分析(2):背景音乐
发表于2015-12-02
我在(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的过程,会有一些时间上的延时。