DVDCodecs: Amlogic: Handle conditions in which amcodec should be opened during Open()
[vuplus_xbmc] / xbmc / guilib / GUIAudioManager.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "system.h"
22 #include "GUIAudioManager.h"
23 #include "Key.h"
24 #include "input/ButtonTranslator.h"
25 #include "settings/lib/Setting.h"
26 #include "threads/SingleLock.h"
27 #include "utils/URIUtils.h"
28 #include "utils/XBMCTinyXML.h"
29 #include "addons/Skin.h"
30 #include "cores/AudioEngine/AEFactory.h"
31
32 using namespace std;
33
34 CGUIAudioManager g_audioManager;
35
36 CGUIAudioManager::CGUIAudioManager()
37 {
38   m_bEnabled = false;
39 }
40
41 CGUIAudioManager::~CGUIAudioManager()
42 {
43 }
44
45 void CGUIAudioManager::OnSettingChanged(const CSetting *setting)
46 {
47   if (setting == NULL)
48     return;
49
50   const std::string &settingId = setting->GetId();
51   if (settingId == "lookandfeel.soundskin")
52   {
53     Enable(true);
54     Load();
55   }
56 }
57
58 void CGUIAudioManager::Initialize()
59 {
60 }
61
62 void CGUIAudioManager::DeInitialize()
63 {
64   CSingleLock lock(m_cs);
65   UnLoad();
66 }
67
68 void CGUIAudioManager::Stop()
69 {
70   CSingleLock lock(m_cs);
71   for (windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
72   {
73     if (it->second.initSound  ) it->second.initSound  ->Stop();
74     if (it->second.deInitSound) it->second.deInitSound->Stop();
75   }
76
77   for (pythonSoundsMap::iterator it = m_pythonSounds.begin(); it != m_pythonSounds.end(); ++it)
78   {
79     IAESound* sound = it->second;
80     sound->Stop();
81   }
82 }
83
84 // \brief Play a sound associated with a CAction
85 void CGUIAudioManager::PlayActionSound(const CAction& action)
86 {
87   CSingleLock lock(m_cs);
88
89   // it's not possible to play gui sounds when passthrough is active
90   if (!m_bEnabled)
91     return;
92
93   actionSoundMap::iterator it = m_actionSoundMap.find(action.GetID());
94   if (it == m_actionSoundMap.end())
95     return;
96
97   if (it->second)
98     it->second->Play();
99 }
100
101 // \brief Play a sound associated with a window and its event
102 // Events: SOUND_INIT, SOUND_DEINIT
103 void CGUIAudioManager::PlayWindowSound(int id, WINDOW_SOUND event)
104 {
105   CSingleLock lock(m_cs);
106
107   // it's not possible to play gui sounds when passthrough is active
108   if (!m_bEnabled)
109     return;
110
111   windowSoundMap::iterator it=m_windowSoundMap.find(id);
112   if (it==m_windowSoundMap.end())
113     return;
114
115   CWindowSounds sounds=it->second;
116   IAESound *sound = NULL;
117   switch (event)
118   {
119   case SOUND_INIT:
120     sound = sounds.initSound;
121     break;
122   case SOUND_DEINIT:
123     sound = sounds.deInitSound;
124     break;
125   }
126
127   if (!sound)
128     return;
129
130   sound->Play();
131 }
132
133 // \brief Play a sound given by filename
134 void CGUIAudioManager::PlayPythonSound(const CStdString& strFileName)
135 {
136   CSingleLock lock(m_cs);
137
138   // it's not possible to play gui sounds when passthrough is active
139   if (!m_bEnabled)
140     return;
141
142   // If we already loaded the sound, just play it
143   pythonSoundsMap::iterator itsb=m_pythonSounds.find(strFileName);
144   if (itsb != m_pythonSounds.end())
145   {
146     IAESound* sound = itsb->second;
147     sound->Play();
148     return;
149   }
150
151   IAESound *sound = LoadSound(strFileName);
152   if (!sound)
153     return;
154
155   m_pythonSounds.insert(pair<const CStdString, IAESound*>(strFileName, sound));
156   sound->Play();
157 }
158
159 void CGUIAudioManager::UnLoad()
160 {
161   //  Free sounds from windows
162   {
163     windowSoundMap::iterator it = m_windowSoundMap.begin();
164     while (it != m_windowSoundMap.end())
165     {
166       if (it->second.initSound  ) FreeSound(it->second.initSound  );
167       if (it->second.deInitSound) FreeSound(it->second.deInitSound);
168       m_windowSoundMap.erase(it++);
169     }
170   }
171
172   // Free sounds from python
173   {
174     pythonSoundsMap::iterator it = m_pythonSounds.begin();
175     while (it != m_pythonSounds.end())
176     {
177       IAESound* sound = it->second;
178       FreeSound(sound);
179       m_pythonSounds.erase(it++);
180     }
181   }
182
183   // free action sounds
184   {
185     actionSoundMap::iterator it = m_actionSoundMap.begin();
186     while (it != m_actionSoundMap.end())
187     {
188       IAESound* sound = it->second;
189       FreeSound(sound);
190       m_actionSoundMap.erase(it++);
191     }
192   }
193 }
194
195 // \brief Load the config file (sounds.xml) for nav sounds
196 // Can be located in a folder "sounds" in the skin or from a
197 // subfolder of the folder "sounds" in the root directory of
198 // xbmc
199 bool CGUIAudioManager::Load()
200 {
201   CSingleLock lock(m_cs);
202
203   UnLoad();
204
205   if (CSettings::Get().GetString("lookandfeel.soundskin")=="OFF")
206     return true;
207   else
208     Enable(true);
209
210   if (CSettings::Get().GetString("lookandfeel.soundskin")=="SKINDEFAULT")
211   {
212     m_strMediaDir = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "sounds");
213   }
214   else
215     m_strMediaDir = URIUtils::AddFileToFolder("special://xbmc/sounds", CSettings::Get().GetString("lookandfeel.soundskin"));
216
217   CStdString strSoundsXml = URIUtils::AddFileToFolder(m_strMediaDir, "sounds.xml");
218
219   //  Load our xml file
220   CXBMCTinyXML xmlDoc;
221
222   CLog::Log(LOGINFO, "Loading %s", strSoundsXml.c_str());
223
224   //  Load the config file
225   if (!xmlDoc.LoadFile(strSoundsXml))
226   {
227     CLog::Log(LOGNOTICE, "%s, Line %d\n%s", strSoundsXml.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
228     return false;
229   }
230
231   TiXmlElement* pRoot = xmlDoc.RootElement();
232   CStdString strValue = pRoot->Value();
233   if ( strValue != "sounds")
234   {
235     CLog::Log(LOGNOTICE, "%s Doesn't contain <sounds>", strSoundsXml.c_str());
236     return false;
237   }
238
239   //  Load sounds for actions
240   TiXmlElement* pActions = pRoot->FirstChildElement("actions");
241   if (pActions)
242   {
243     TiXmlNode* pAction = pActions->FirstChild("action");
244
245     while (pAction)
246     {
247       TiXmlNode* pIdNode = pAction->FirstChild("name");
248       int id = 0;    // action identity
249       if (pIdNode && pIdNode->FirstChild())
250       {
251         CButtonTranslator::TranslateActionString(pIdNode->FirstChild()->Value(), id);
252       }
253
254       TiXmlNode* pFileNode = pAction->FirstChild("file");
255       CStdString strFile;
256       if (pFileNode && pFileNode->FirstChild())
257         strFile += pFileNode->FirstChild()->Value();
258
259       if (id > 0 && !strFile.empty())
260       {
261         CStdString filename = URIUtils::AddFileToFolder(m_strMediaDir, strFile);
262         IAESound *sound = LoadSound(filename);
263         if (sound)
264           m_actionSoundMap.insert(pair<int, IAESound *>(id, sound));
265       }
266
267       pAction = pAction->NextSibling();
268     }
269   }
270
271   //  Load window specific sounds
272   TiXmlElement* pWindows = pRoot->FirstChildElement("windows");
273   if (pWindows)
274   {
275     TiXmlNode* pWindow = pWindows->FirstChild("window");
276
277     while (pWindow)
278     {
279       int id = 0;
280
281       TiXmlNode* pIdNode = pWindow->FirstChild("name");
282       if (pIdNode)
283       {
284         if (pIdNode->FirstChild())
285           id = CButtonTranslator::TranslateWindow(pIdNode->FirstChild()->Value());
286       }
287
288       CWindowSounds sounds;
289       sounds.initSound   = LoadWindowSound(pWindow, "activate"  );
290       sounds.deInitSound = LoadWindowSound(pWindow, "deactivate");
291
292       if (id > 0)
293         m_windowSoundMap.insert(pair<int, CWindowSounds>(id, sounds));
294
295       pWindow = pWindow->NextSibling();
296     }
297   }
298
299   return true;
300 }
301
302 IAESound* CGUIAudioManager::LoadSound(const CStdString &filename)
303 {
304   CSingleLock lock(m_cs);
305   soundCache::iterator it = m_soundCache.find(filename);
306   if (it != m_soundCache.end())
307   {
308     ++it->second.usage;
309     return it->second.sound;
310   }
311
312   IAESound *sound = CAEFactory::MakeSound(filename);
313   if (!sound)
314     return NULL;
315
316   CSoundInfo info;
317   info.usage = 1;
318   info.sound = sound;
319   m_soundCache[filename] = info;
320
321   return info.sound;
322 }
323
324 void CGUIAudioManager::FreeSound(IAESound *sound)
325 {
326   CSingleLock lock(m_cs);
327   for(soundCache::iterator it = m_soundCache.begin(); it != m_soundCache.end(); ++it) {
328     if (it->second.sound == sound) {
329       if (--it->second.usage == 0) {     
330         CAEFactory::FreeSound(sound);
331         m_soundCache.erase(it);
332       }
333       return;
334     }
335   }
336 }
337
338 // \brief Load a window node of the config file (sounds.xml)
339 IAESound* CGUIAudioManager::LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier)
340 {
341   if (!pWindowNode)
342     return NULL;
343
344   TiXmlNode* pFileNode = pWindowNode->FirstChild(strIdentifier);
345   if (pFileNode && pFileNode->FirstChild())
346     return LoadSound(URIUtils::AddFileToFolder(m_strMediaDir, pFileNode->FirstChild()->Value()));
347
348   return NULL;
349 }
350
351 // \brief Enable/Disable nav sounds
352 void CGUIAudioManager::Enable(bool bEnable)
353 {
354   // always deinit audio when we don't want gui sounds
355   if (CSettings::Get().GetString("lookandfeel.soundskin")=="OFF")
356     bEnable = false;
357
358   CSingleLock lock(m_cs);
359   m_bEnabled = bEnable;
360 }
361
362 // \brief Sets the volume of all playing sounds
363 void CGUIAudioManager::SetVolume(float level)
364 {
365   CSingleLock lock(m_cs);
366
367   {
368     actionSoundMap::iterator it = m_actionSoundMap.begin();
369     while (it!=m_actionSoundMap.end())
370     {
371       if (it->second)
372         it->second->SetVolume(level);
373       ++it;
374     }
375   }
376
377   for(windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
378   {
379     if (it->second.initSound  ) it->second.initSound  ->SetVolume(level);
380     if (it->second.deInitSound) it->second.deInitSound->SetVolume(level);
381   }
382
383   {
384     pythonSoundsMap::iterator it = m_pythonSounds.begin();
385     while (it != m_pythonSounds.end())
386     {
387       if (it->second)
388         it->second->SetVolume(level);
389
390       ++it;
391     }
392   }
393 }