/*---------------------------------------------------------------------------* Project: NintendoWare File: snd_VoiceManager.cpp Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo and/or its licensed developers and are protected by national and international copyright laws. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. The content herein is highly confidential and should be handled accordingly. $Revision: 31311 $ *---------------------------------------------------------------------------*/ #include "precompiled.h" #include #include namespace nw { namespace snd { namespace internal { namespace driver { /*---------------------------------------------------------------------------* Name: GetInstance Description: シングルトンのインスタンスを取得します Arguments: なし Returns: なし *---------------------------------------------------------------------------*/ VoiceManager& VoiceManager::GetInstance() { static VoiceManager instance; return instance; } VoiceManager::VoiceManager() : m_Initialized( false ) { } size_t VoiceManager::GetRequiredMemSize( int voiceCount ) { return sizeof( Voice ) * voiceCount; } void VoiceManager::Initialize( void* mem, size_t memSize ) { if ( m_Initialized ) return; int voiceCount = memSize / sizeof( Voice ); u8* ptr = reinterpret_cast( mem ); // ボイスをリスト管理 for ( int i = 0; i < voiceCount; i++ ) { m_FreeVoiceList.PushBack( new( ptr )Voice() ); ptr += sizeof( Voice ); } NW_ASSERT( ptr <= reinterpret_cast( mem ) + memSize ); m_Initialized = true; } void VoiceManager::Finalize() { if ( !m_Initialized ) return; StopAllVoices(); // ボイスをリストから削除 while ( ! m_FreeVoiceList.IsEmpty() ) { Voice& voice = m_FreeVoiceList.GetFront(); m_FreeVoiceList.PopFront(); voice.~Voice(); // デストラクタ呼び出し } m_Initialized = false; } void VoiceManager::StopAllVoices() { // 再生中のボイスを全てFree while ( ! m_PrioVoiceList.IsEmpty() ) { Voice& voice = m_PrioVoiceList.GetFront(); voice.Stop(); if ( voice.m_Callback != NULL ) { voice.m_Callback( &voice, Voice::CALLBACK_STATUS_CANCEL, voice.m_pCallbackData ); } voice.Free(); } } Voice* VoiceManager::AllocVoice( int voiceChannelCount, int priority, Voice::VoiceCallback callback, void* callbackData ) { // ボイスが不足している場合にプライオリティが低いボイスを停止させる if ( m_FreeVoiceList.IsEmpty() ) { if ( DropLowestPriorityVoice( priority ) == 0 ) return NULL; } // フリーリストから空きボイスを取得 Voice& voice = m_FreeVoiceList.GetFront(); if ( ! voice.Alloc( voiceChannelCount, priority, callback, callbackData ) ) { return NULL; } // プライオリティを設定してボイスリストに追加 voice.m_Priority = static_cast( priority ); AppendVoiceList( &voice ); return &voice; } void VoiceManager::FreeVoice( Voice* voice ) { NW_NULL_ASSERT( voice ); RemoveVoiceList( voice ); } void VoiceManager::UpdateAllVoices() { // 波形の再生が完了したボイスを停止させる for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter(); itr != m_PrioVoiceList.GetEndIter(); ) { VoiceList::Iterator curItr = itr++; curItr->StopFinished(); } // すべてのボイスのパラメータ計算 for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter(); itr != m_PrioVoiceList.GetEndIter(); ) { VoiceList::Iterator curItr = itr++; curItr->Calc(); } // すべてのボイスを更新 for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter(); itr != m_PrioVoiceList.GetEndIter(); ) { VoiceList::Iterator curItr = itr++; curItr->Update(); } } void VoiceManager::AppendVoiceList( Voice* voice ) { m_FreeVoiceList.Erase( voice ); // プライオリティリストに挿入、同プライオリティは後着の音を後ろに繋ぐ // 逆順に探索したほうが、同プライオリティの場合に速い。 VoiceList::ReverseIterator itr = m_PrioVoiceList.GetBeginReverseIter(); while ( itr != m_PrioVoiceList.GetEndReverseIter() ) { if ( itr->m_Priority <= voice->m_Priority ) { break; } (void)++itr; } m_PrioVoiceList.Insert( itr.GetBase(), voice ); } void VoiceManager::RemoveVoiceList( Voice* voice ) { m_PrioVoiceList.Erase( voice ); m_FreeVoiceList.PushBack( voice ); } void VoiceManager::ChangeVoicePriority( Voice* voice ) { RemoveVoiceList( voice ); AppendVoiceList( voice ); UpdateEachVoicePriority( m_PrioVoiceList.GetIteratorFromPointer( voice ), m_PrioVoiceList.GetEndIter() ); } void VoiceManager::UpdateEachVoicePriority( const VoiceList::Iterator& beginItr, const VoiceList::Iterator& endItr ) { // プライオリティリストの更新 for ( VoiceList::Iterator itr = beginItr; itr != endItr; (void)++itr ) { // RELEASEよりプライオリティが低いものは再設定しない。 if ( itr->GetPriority() <= Voice::PRIORITY_RELEASE ) return; // NODROPは変更しない if ( itr->GetPriority() == Voice::PRIORITY_NODROP ) continue; itr->UpdateVoicesPriority(); } } void VoiceManager::UpdateAllVoicesSync( u32 syncFlag ) { for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter(); itr != m_PrioVoiceList.GetEndIter(); ) { VoiceList::Iterator curItr = itr++; if ( curItr->m_IsActive ) curItr->m_SyncFlag |= syncFlag; } } int VoiceManager::DropLowestPriorityVoice( int priority ) { if ( m_PrioVoiceList.IsEmpty() ) return 0; Voice* dropVoice = &m_PrioVoiceList.GetFront(); if ( dropVoice->GetPriority() > priority ) return 0; // もっとも優先度の低いボイスをDrop int dropCount = dropVoice->GetPhysicalVoiceCount(); Voice::VoiceCallback callbackFunc = dropVoice->m_Callback; void* callbackData = dropVoice->m_pCallbackData; dropVoice->Stop(); dropVoice->Free(); if ( callbackFunc != NULL ) { callbackFunc( dropVoice, Voice::CALLBACK_STATUS_DROP_VOICE, callbackData ); } return dropCount; } int VoiceManager::GetVoiceCount() const { SoundThreadLock lock; int voiceCount = 0; for ( VoiceList::ConstIterator itr = m_PrioVoiceList.GetBeginIter(); itr != m_PrioVoiceList.GetEndIter(); (void)++itr ) { voiceCount += itr->GetPhysicalVoiceCount(); } return voiceCount; } unsigned long VoiceManager::GetActiveCount() const { SoundThreadLock lock; return m_PrioVoiceList.GetSize(); } unsigned long VoiceManager::GetFreeCount() const { SoundThreadLock lock; return m_FreeVoiceList.GetSize(); } const VoiceManager::VoiceList& VoiceManager::GetVoiceList() const { return m_PrioVoiceList; } } // namespace nw::snd::internal::driver } // namespace nw::snd::internal } // namespace nw::snd } // namespace nw