Cocos2d-x中SimpleAudioEngine播放多個(gè)背景音樂時(shí)被覆蓋的問題

前段時(shí)間,做小示例的時(shí)候偶然發(fā)現(xiàn)了個(gè)問題(也許是我沒仔細(xì)了解過SimpleAudioEngine):

  1. 使用SimpleAudioEngine來播放背景音樂A
  • 然后音樂開始播放了脯倒,使用preloadBackgroundMusic加載音樂B
  • 然后使用isBackgroundMusicPlaying來查詢時(shí)杨幼,返回值為false
  • 然后使用stopBackgroundMusic來關(guān)閉背景音樂時(shí),發(fā)現(xiàn)根本關(guān)不掉

從操作可以看出來,出問題的關(guān)鍵就在于,播放背景音樂時(shí),進(jìn)行了另外一個(gè)背景音樂的預(yù)加載采章,然而SimpleAudioEngine中,背景不能同時(shí)存在多個(gè)壶辜。


既然出現(xiàn)了問題悯舟,那就來看看源碼是怎么寫的(cocos2d-x 3.10)。

背景音樂預(yù)加載的代碼:

//audio/ios/SimpleAudioEngine.mm
void SimpleAudioEngine::preloadBackgroundMusic(const char* pszFilePath)
{
    // Changing file path to full path
    std::string fullPath = FileUtils::getInstance()->fullPathForFilename(pszFilePath);
    static_preloadBackgroundMusic(fullPath.c_str());
}

這個(gè)函數(shù)中砸民,重點(diǎn)是第二句抵怎,調(diào)用了一個(gè)靜態(tài)方法static_preloadBackgroundMusic來加載背景音樂文件。這個(gè)方法的源碼:

//audio/ios/SimpleAudioEngine.mm
static void static_preloadBackgroundMusic(const char* pszFilePath)
{
    [[SimpleAudioEngine sharedEngine] preloadBackgroundMusic: [NSString stringWithUTF8String: pszFilePath]];
}

看來是調(diào)用了ObjC封裝的接口岭参,繼續(xù)往下看:

//audio/ios/SimpleAudioEngine_objc.m
-(void) preloadBackgroundMusic:(NSString*) filePath {
    [am preloadBackgroundMusic:filePath];
}

這里看到反惕,在Objc的接口中調(diào)用到了一個(gè)對象am的preloadBackgroundMusic方法。這個(gè)am演侯,我們在這個(gè).m文件的頭部可以看到姿染,它是一個(gè)CDAudioManager對象。所以這個(gè)對象里面的預(yù)加載是如何實(shí)現(xiàn)的呢:

//audio/ios/CDAudioManager.m
//Load background music ready for playing
-(void) preloadBackgroundMusic:(NSString*) filePath
{
    [self.backgroundMusic load:filePath];    
} 

我們在這個(gè)文件中能夠找到很多backgroundMusic秒际,不能確定這里用到的是哪一個(gè)backgroundMusic悬赏。所以往上找,能看到它是屬于
CDAudioManager娄徊,然后在頭文件中可以看到:

@interface CDAudioManager : NSObject <CDLongAudioSourceDelegate, CDAudioInterruptProtocol, AVAudioSessionDelegate> {
    CDSoundEngine        *soundEngine;
    CDLongAudioSource    *backgroundMusic;
    NSMutableArray        *audioSourceChannels;
//....后面省略

其實(shí)闽颇,在這里我們就能看到問題了,這個(gè)CDAudioManager所持有的CDLongAudioSource引用只有一個(gè)嵌莉,也就是說进萄,表示我們的BackgroundMusic的資源對象只有一個(gè)。繼續(xù)看這個(gè)CDLongAudioSource的load函數(shù)是怎么實(shí)現(xiàn)的:

-(void) load:(NSString*) filePath {
    //We have already loaded a file previously, check if we are being asked to load the same file
    if (state == kLAS_Init || ![filePath isEqualToString:audioSourceFilePath]) {
        CDLOGINFO(@"Denshion::CDLongAudioSource - Loading new audio source %@",filePath);
        //New file
        if (state != kLAS_Init) {
            [audioSourceFilePath release];//Release old file path
            [audioSourcePlayer release];//Release old AVAudioPlayer, they can't be reused
        }
        audioSourceFilePath = [filePath copy];
        NSError *error = nil;
        NSString *path = [CDUtilities fullPathFromRelativePath:audioSourceFilePath];
        audioSourcePlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
        if (error == nil) {
            [audioSourcePlayer prepareToPlay];
            audioSourcePlayer.delegate = self;
            if (delegate && [delegate respondsToSelector:@selector(cdAudioSourceFileDidChange:)]) {
                //Tell our delegate the file has changed
                [delegate cdAudioSourceFileDidChange:self];
            }    
        } else {
            CDLOG(@"Denshion::CDLongAudioSource - Error initialising audio player: %@",error);
        }    
    } else {
        //Same file - just return it to a consistent state
        [self pause];
        [self rewind];
    }
    audioSourcePlayer.volume = volume;
    audioSourcePlayer.numberOfLoops = numberOfLoops;
    state = kLAS_Loaded;
}    

在load函數(shù)中锐峭,如果新的Path與本對象中的屬性audioSourceFilePath不同中鼠,而且之前已經(jīng)加載過一個(gè)背景音樂,state不再是kLAS_Init沿癞,此時(shí)會釋放并覆蓋舊的audioSourcePlayer和audioSourceFilePath援雇。

即使你先加載了音樂A,再加載音樂B椎扬,然后再重新加載一次音樂A惫搏,雖然Path與之前的A相同,但是控制的并不再是同一個(gè)對象蚕涤,因?yàn)椤芭f”的引用總是會被丟棄筐赔。

到這里并沒有結(jié)束,我們還需要確定一下揖铜,播放背景音樂的時(shí)候和這個(gè)audioSource有關(guān)茴丰。看看playBackgroundMusic的代碼:

//audio/ios/SimpleAudioEngine.mm
void SimpleAudioEngine::playBackgroundMusic(const char* pszFilePath, bool bLoop)
{
    // Changing file path to full path
    std::string fullPath = FileUtils::getInstance()->fullPathForFilename(pszFilePath);
    static_playBackgroundMusic(fullPath.c_str(), bLoop);
}

同樣,去找static_playBackgroundMusic方法:

static void static_playBackgroundMusic(const char* pszFilePath, bool bLoop)
{
    [[SimpleAudioEngine sharedEngine] playBackgroundMusic: [NSString stringWithUTF8String: pszFilePath] loop: bLoop];
}

然后找到Obj-c的接口:

-(void) playBackgroundMusic:(NSString*) filePath
{
    [am playBackgroundMusic:filePath loop:TRUE];
}
-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop
{
    [am playBackgroundMusic:filePath loop:loop];
}

這里可以看到進(jìn)行了一個(gè)重載贿肩,playBackgroundMusic方法的默認(rèn)loop屬性為true峦椰。然后繼續(xù)看am中的代碼:

-(void) playBackgroundMusic:(NSString*) filePath loop:(BOOL) loop
{
    [self.backgroundMusic load:filePath];

 if (loop) {
  [self.backgroundMusic setNumberOfLoops:-1];
 } else {
  [self.backgroundMusic setNumberOfLoops:0];
 }

 if (!willPlayBackgroundMusic || _mute) {
  CDLOGINFO(@"Denshion::CDAudioManager - play bgm aborted because audio is not exclusive or sound is muted");
  return;
 }

 [self.backgroundMusic play];
}

這里第一句就是調(diào)用CDLongAudioSource的load方法,也就是之前我們看到的preloadBakcgroundMusic函數(shù)中所做的操作汰规。中間一段是進(jìn)行l(wèi)oop設(shè)置汤功,以及錯(cuò)誤處理。最后調(diào)用了self.backgroundMusic(CDLongAudioSource)的play方法溜哮。


結(jié)論:
至始至終滔金,SimpleAudioEngine中只持有了一個(gè)關(guān)于背景音樂的引用。所以茂嗓,進(jìn)行連續(xù)的preloadBackgroundMusic或者playBackgroundMusic鹦蠕,我們看到的現(xiàn)象都是丟失了對原來的背景音樂的控制(引用)。

使用SimpleAudioEngine播放背景音樂時(shí)在抛,如果游戲中有多個(gè)場景,每個(gè)場景有不同的背景音樂萧恕,我們沒有必要去一次preload所有的BackgroundMusic文件刚梭,注意每次播放背景音樂前進(jìn)行判斷,確保舊的背景音樂被停止票唆。

關(guān)于SimpleAudioEngine的測試代碼朴读,從我的Github中可以看到。如有錯(cuò)誤與不完整指出走趋,還請小伙伴指出衅金!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市簿煌,隨后出現(xiàn)的幾起案子氮唯,更是在濱河造成了極大的恐慌,老刑警劉巖姨伟,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惩琉,死亡現(xiàn)場離奇詭異,居然都是意外死亡夺荒,警方通過查閱死者的電腦和手機(jī)瞒渠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來技扼,“玉大人伍玖,你說我怎么就攤上這事〗宋牵” “怎么了窍箍?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我仔燕,道長造垛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任晰搀,我火速辦了婚禮五辽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘外恕。我一直安慰自己杆逗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布鳞疲。 她就那樣靜靜地躺著罪郊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪尚洽。 梳的紋絲不亂的頭發(fā)上悔橄,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機(jī)與錄音腺毫,去河邊找鬼癣疟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛潮酒,可吹牛的內(nèi)容都是我干的睛挚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼急黎,長吁一口氣:“原來是場噩夢啊……” “哼扎狱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起勃教,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤淤击,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后荣回,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遭贸,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年心软,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了壕吹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡删铃,死狀恐怖耳贬,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猎唁,我是刑警寧澤咒劲,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響腐魂,放射性物質(zhì)發(fā)生泄漏帐偎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一蛔屹、第九天 我趴在偏房一處隱蔽的房頂上張望削樊。 院中可真熱鬧,春花似錦兔毒、人聲如沸漫贞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽迅脐。三九已至,卻和暖如春豪嗽,著一層夾襖步出監(jiān)牢的瞬間谴蔑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工龟梦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留树碱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓变秦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親框舔。 傳聞我的和親對象是個(gè)殘疾皇子蹦玫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容