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