1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     snd_VoiceManager.cpp
4 
5   Copyright (C)2009-2011 Nintendo/HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain proprietary
8   information of Nintendo and/or its licensed developers and are protected by
9   national and international copyright laws. They may not be disclosed to third
10   parties or copied or duplicated in any form, in whole or in part, without the
11   prior written consent of Nintendo.
12 
13   The content herein is highly confidential and should be handled accordingly.
14 
15   $Revision: 31311 $
16  *---------------------------------------------------------------------------*/
17 
18 #include "precompiled.h"
19 
20 #include <nw/snd/snd_VoiceManager.h>
21 #include <nw/snd/snd_DisposeCallbackManager.h>
22 
23 namespace nw {
24 namespace snd {
25 namespace internal {
26 namespace driver {
27 
28 /*---------------------------------------------------------------------------*
29     Name:           GetInstance
30 
31     Description:    シングルトンのインスタンスを取得します
32 
33     Arguments:      なし
34 
35     Returns:        なし
36  *---------------------------------------------------------------------------*/
GetInstance()37 VoiceManager& VoiceManager::GetInstance()
38 {
39     static VoiceManager instance;
40     return instance;
41 }
42 
43 
VoiceManager()44 VoiceManager::VoiceManager()
45 : m_Initialized( false )
46 {
47 }
48 
GetRequiredMemSize(int voiceCount)49 size_t VoiceManager::GetRequiredMemSize( int voiceCount )
50 {
51     return sizeof( Voice ) * voiceCount;
52 }
53 
Initialize(void * mem,size_t memSize)54 void VoiceManager::Initialize( void* mem, size_t memSize )
55 {
56     if ( m_Initialized ) return;
57 
58     int voiceCount = memSize / sizeof( Voice );
59 
60     u8* ptr = reinterpret_cast<u8*>( mem );
61     // ボイスをリスト管理
62     for ( int i = 0; i < voiceCount; i++ )
63     {
64         m_FreeVoiceList.PushBack( new( ptr )Voice() );
65         ptr += sizeof( Voice );
66     }
67     NW_ASSERT( ptr <= reinterpret_cast<u8*>( mem ) + memSize );
68 
69     m_Initialized = true;
70 }
71 
Finalize()72 void VoiceManager::Finalize()
73 {
74     if ( !m_Initialized ) return;
75 
76     StopAllVoices();
77 
78     // ボイスをリストから削除
79     while ( ! m_FreeVoiceList.IsEmpty() )
80     {
81         Voice& voice = m_FreeVoiceList.GetFront();
82         m_FreeVoiceList.PopFront();
83         voice.~Voice(); // デストラクタ呼び出し
84     }
85 
86     m_Initialized = false;
87 }
88 
StopAllVoices()89 void VoiceManager::StopAllVoices()
90 {
91     // 再生中のボイスを全てFree
92     while ( ! m_PrioVoiceList.IsEmpty() )
93     {
94         Voice& voice = m_PrioVoiceList.GetFront();
95         voice.Stop();
96         if ( voice.m_Callback != NULL )
97         {
98             voice.m_Callback(
99                 &voice,
100                 Voice::CALLBACK_STATUS_CANCEL,
101                 voice.m_pCallbackData
102             );
103         }
104         voice.Free();
105     }
106 }
107 
AllocVoice(int voiceChannelCount,int priority,Voice::VoiceCallback callback,void * callbackData)108 Voice* VoiceManager::AllocVoice(
109     int voiceChannelCount,
110     int priority,
111     Voice::VoiceCallback callback,
112     void* callbackData
113 )
114 {
115     // ボイスが不足している場合にプライオリティが低いボイスを停止させる
116     if ( m_FreeVoiceList.IsEmpty() )
117     {
118         if ( DropLowestPriorityVoice( priority ) == 0 ) return NULL;
119     }
120 
121     // フリーリストから空きボイスを取得
122     Voice& voice = m_FreeVoiceList.GetFront();
123 
124     if ( ! voice.Alloc( voiceChannelCount, priority, callback, callbackData ) )
125     {
126         return NULL;
127     }
128 
129     // プライオリティを設定してボイスリストに追加
130     voice.m_Priority = static_cast<u8>( priority );
131     AppendVoiceList( &voice );
132 
133     return &voice;
134 }
135 
FreeVoice(Voice * voice)136 void VoiceManager::FreeVoice( Voice* voice )
137 {
138     NW_NULL_ASSERT( voice );
139 
140     RemoveVoiceList( voice );
141 }
142 
UpdateAllVoices()143 void VoiceManager::UpdateAllVoices()
144 {
145     // 波形の再生が完了したボイスを停止させる
146     for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter();
147           itr != m_PrioVoiceList.GetEndIter();
148         )
149     {
150         VoiceList::Iterator curItr = itr++;
151         curItr->StopFinished();
152     }
153 
154     // すべてのボイスのパラメータ計算
155     for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter();
156           itr != m_PrioVoiceList.GetEndIter();
157         )
158     {
159         VoiceList::Iterator curItr = itr++;
160         curItr->Calc();
161     }
162 
163     // すべてのボイスを更新
164     for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter();
165           itr != m_PrioVoiceList.GetEndIter();
166         )
167     {
168         VoiceList::Iterator curItr = itr++;
169         curItr->Update();
170     }
171 }
172 
AppendVoiceList(Voice * voice)173 void VoiceManager::AppendVoiceList( Voice* voice )
174 {
175     m_FreeVoiceList.Erase( voice );
176 
177     // プライオリティリストに挿入、同プライオリティは後着の音を後ろに繋ぐ
178     // 逆順に探索したほうが、同プライオリティの場合に速い。
179     VoiceList::ReverseIterator itr = m_PrioVoiceList.GetBeginReverseIter();
180     while ( itr != m_PrioVoiceList.GetEndReverseIter() )
181     {
182         if ( itr->m_Priority <= voice->m_Priority )
183         {
184             break;
185         }
186         (void)++itr;
187     }
188 
189     m_PrioVoiceList.Insert( itr.GetBase(), voice );
190 }
RemoveVoiceList(Voice * voice)191 void VoiceManager::RemoveVoiceList( Voice* voice )
192 {
193     m_PrioVoiceList.Erase( voice );
194     m_FreeVoiceList.PushBack( voice );
195 }
196 
ChangeVoicePriority(Voice * voice)197 void VoiceManager::ChangeVoicePriority( Voice* voice )
198 {
199     RemoveVoiceList( voice );
200     AppendVoiceList( voice );
201     UpdateEachVoicePriority(
202         m_PrioVoiceList.GetIteratorFromPointer( voice ),
203         m_PrioVoiceList.GetEndIter()
204     );
205 }
206 
UpdateEachVoicePriority(const VoiceList::Iterator & beginItr,const VoiceList::Iterator & endItr)207 void VoiceManager::UpdateEachVoicePriority(
208     const VoiceList::Iterator& beginItr,
209     const VoiceList::Iterator& endItr
210 )
211 {
212     // プライオリティリストの更新
213     for ( VoiceList::Iterator itr = beginItr;
214           itr != endItr;
215           (void)++itr
216         )
217     {
218         // RELEASEよりプライオリティが低いものは再設定しない。
219         if ( itr->GetPriority() <= Voice::PRIORITY_RELEASE ) return;
220 
221         // NODROPは変更しない
222         if ( itr->GetPriority() == Voice::PRIORITY_NODROP ) continue;
223 
224         itr->UpdateVoicesPriority();
225     }
226 }
227 
UpdateAllVoicesSync(u32 syncFlag)228 void VoiceManager::UpdateAllVoicesSync( u32 syncFlag )
229 {
230     for ( VoiceList::Iterator itr = m_PrioVoiceList.GetBeginIter();
231           itr != m_PrioVoiceList.GetEndIter();
232         )
233     {
234         VoiceList::Iterator curItr = itr++;
235         if ( curItr->m_IsActive ) curItr->m_SyncFlag |= syncFlag;
236     }
237 }
238 
DropLowestPriorityVoice(int priority)239 int VoiceManager::DropLowestPriorityVoice( int priority )
240 {
241     if ( m_PrioVoiceList.IsEmpty() ) return 0;
242 
243     Voice* dropVoice = &m_PrioVoiceList.GetFront();
244     if ( dropVoice->GetPriority() > priority ) return 0;
245 
246     // もっとも優先度の低いボイスをDrop
247     int dropCount = dropVoice->GetPhysicalVoiceCount();
248 
249     Voice::VoiceCallback callbackFunc = dropVoice->m_Callback;
250     void* callbackData = dropVoice->m_pCallbackData;
251 
252     dropVoice->Stop();
253     dropVoice->Free();
254 
255     if ( callbackFunc != NULL )
256     {
257         callbackFunc(
258             dropVoice,
259             Voice::CALLBACK_STATUS_DROP_VOICE,
260             callbackData
261         );
262     }
263 
264     return dropCount;
265 }
266 
GetVoiceCount() const267 int VoiceManager::GetVoiceCount() const
268 {
269     SoundThreadLock lock;
270 
271     int voiceCount = 0;
272     for (
273         VoiceList::ConstIterator itr = m_PrioVoiceList.GetBeginIter();
274         itr != m_PrioVoiceList.GetEndIter();
275         (void)++itr
276     )
277     {
278         voiceCount += itr->GetPhysicalVoiceCount();
279     }
280 
281     return voiceCount;
282 }
283 
GetActiveCount() const284 unsigned long VoiceManager::GetActiveCount() const
285 {
286     SoundThreadLock lock;
287 
288     return m_PrioVoiceList.GetSize();
289 }
290 
GetFreeCount() const291 unsigned long VoiceManager::GetFreeCount() const
292 {
293     SoundThreadLock lock;
294 
295     return m_FreeVoiceList.GetSize();
296 }
297 
GetVoiceList() const298 const VoiceManager::VoiceList& VoiceManager::GetVoiceList() const
299 {
300     return m_PrioVoiceList;
301 }
302 
303 } // namespace nw::snd::internal::driver
304 } // namespace nw::snd::internal
305 } // namespace nw::snd
306 } // namespace nw
307 
308