[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / guilib / GUIAudioManager.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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 "settings/GUISettings.h"
25 #include "input/ButtonTranslator.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::Initialize()
46 {
47 }
48
49 void CGUIAudioManager::DeInitialize()
50 {
51   CSingleLock lock(m_cs);
52   UnLoad();
53 }
54
55 void CGUIAudioManager::Stop()
56 {
57   CSingleLock lock(m_cs);
58   for (windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
59   {
60     if (it->second.initSound  ) it->second.initSound  ->Stop();
61     if (it->second.deInitSound) it->second.deInitSound->Stop();
62   }
63
64   for (pythonSoundsMap::iterator it = m_pythonSounds.begin(); it != m_pythonSounds.end(); ++it)
65   {
66     IAESound* sound = it->second;
67     sound->Stop();
68   }
69 }
70
71 // \brief Play a sound associated with a CAction
72 void CGUIAudioManager::PlayActionSound(const CAction& action)
73 {
74   CSingleLock lock(m_cs);
75
76   // it's not possible to play gui sounds when passthrough is active
77   if (!m_bEnabled)
78     return;
79
80   actionSoundMap::iterator it = m_actionSoundMap.find(action.GetID());
81   if (it == m_actionSoundMap.end())
82     return;
83
84   if (it->second)
85     it->second->Play();
86 }
87
88 // \brief Play a sound associated with a window and its event
89 // Events: SOUND_INIT, SOUND_DEINIT
90 void CGUIAudioManager::PlayWindowSound(int id, WINDOW_SOUND event)
91 {
92   CSingleLock lock(m_cs);
93
94   // it's not possible to play gui sounds when passthrough is active
95   if (!m_bEnabled)
96     return;
97
98   windowSoundMap::iterator it=m_windowSoundMap.find(id);
99   if (it==m_windowSoundMap.end())
100     return;
101
102   CWindowSounds sounds=it->second;
103   IAESound *sound = NULL;
104   switch (event)
105   {
106   case SOUND_INIT:
107     sound = sounds.initSound;
108     break;
109   case SOUND_DEINIT:
110     sound = sounds.deInitSound;
111     break;
112   }
113
114   if (!sound)
115     return;
116
117   sound->Play();
118 }
119
120 // \brief Play a sound given by filename
121 void CGUIAudioManager::PlayPythonSound(const CStdString& strFileName)
122 {
123   CSingleLock lock(m_cs);
124
125   // it's not possible to play gui sounds when passthrough is active
126   if (!m_bEnabled)
127     return;
128
129   // If we already loaded the sound, just play it
130   pythonSoundsMap::iterator itsb=m_pythonSounds.find(strFileName);
131   if (itsb != m_pythonSounds.end())
132   {
133     IAESound* sound = itsb->second;
134     sound->Play();
135     return;
136   }
137
138   IAESound *sound = LoadSound(strFileName);
139   if (!sound)
140     return;
141
142   m_pythonSounds.insert(pair<const CStdString, IAESound*>(strFileName, sound));
143   sound->Play();
144 }
145
146 void CGUIAudioManager::UnLoad()
147 {
148   //  Free sounds from windows
149   {
150     windowSoundMap::iterator it = m_windowSoundMap.begin();
151     while (it != m_windowSoundMap.end())
152     {
153       if (it->second.initSound  ) FreeSound(it->second.initSound  );
154       if (it->second.deInitSound) FreeSound(it->second.deInitSound);
155       m_windowSoundMap.erase(it++);
156     }
157   }
158
159   // Free sounds from python
160   {
161     pythonSoundsMap::iterator it = m_pythonSounds.begin();
162     while (it != m_pythonSounds.end())
163     {
164       IAESound* sound = it->second;
165       FreeSound(sound);
166       m_pythonSounds.erase(it++);
167     }
168   }
169
170   // free action sounds
171   {
172     actionSoundMap::iterator it = m_actionSoundMap.begin();
173     while (it != m_actionSoundMap.end())
174     {
175       IAESound* sound = it->second;
176       FreeSound(sound);
177       m_actionSoundMap.erase(it++);
178     }
179   }
180 }
181
182 // \brief Load the config file (sounds.xml) for nav sounds
183 // Can be located in a folder "sounds" in the skin or from a
184 // subfolder of the folder "sounds" in the root directory of
185 // xbmc
186 bool CGUIAudioManager::Load()
187 {
188   CSingleLock lock(m_cs);
189
190   UnLoad();
191
192   if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF")
193     return true;
194   else
195     Enable(true);
196
197   if (g_guiSettings.GetString("lookandfeel.soundskin")=="SKINDEFAULT")
198   {
199     m_strMediaDir = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "sounds");
200   }
201   else
202     m_strMediaDir = URIUtils::AddFileToFolder("special://xbmc/sounds", g_guiSettings.GetString("lookandfeel.soundskin"));
203
204   CStdString strSoundsXml = URIUtils::AddFileToFolder(m_strMediaDir, "sounds.xml");
205
206   //  Load our xml file
207   CXBMCTinyXML xmlDoc;
208
209   CLog::Log(LOGINFO, "Loading %s", strSoundsXml.c_str());
210
211   //  Load the config file
212   if (!xmlDoc.LoadFile(strSoundsXml))
213   {
214     CLog::Log(LOGNOTICE, "%s, Line %d\n%s", strSoundsXml.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
215     return false;
216   }
217
218   TiXmlElement* pRoot = xmlDoc.RootElement();
219   CStdString strValue = pRoot->Value();
220   if ( strValue != "sounds")
221   {
222     CLog::Log(LOGNOTICE, "%s Doesn't contain <sounds>", strSoundsXml.c_str());
223     return false;
224   }
225
226   //  Load sounds for actions
227   TiXmlElement* pActions = pRoot->FirstChildElement("actions");
228   if (pActions)
229   {
230     TiXmlNode* pAction = pActions->FirstChild("action");
231
232     while (pAction)
233     {
234       TiXmlNode* pIdNode = pAction->FirstChild("name");
235       int id = 0;    // action identity
236       if (pIdNode && pIdNode->FirstChild())
237       {
238         CButtonTranslator::TranslateActionString(pIdNode->FirstChild()->Value(), id);
239       }
240
241       TiXmlNode* pFileNode = pAction->FirstChild("file");
242       CStdString strFile;
243       if (pFileNode && pFileNode->FirstChild())
244         strFile += pFileNode->FirstChild()->Value();
245
246       if (id > 0 && !strFile.IsEmpty())
247       {
248         CStdString filename = URIUtils::AddFileToFolder(m_strMediaDir, strFile);
249         IAESound *sound = LoadSound(filename);
250         if (sound)
251           m_actionSoundMap.insert(pair<int, IAESound *>(id, sound));
252       }
253
254       pAction = pAction->NextSibling();
255     }
256   }
257
258   //  Load window specific sounds
259   TiXmlElement* pWindows = pRoot->FirstChildElement("windows");
260   if (pWindows)
261   {
262     TiXmlNode* pWindow = pWindows->FirstChild("window");
263
264     while (pWindow)
265     {
266       int id = 0;
267
268       TiXmlNode* pIdNode = pWindow->FirstChild("name");
269       if (pIdNode)
270       {
271         if (pIdNode->FirstChild())
272           id = CButtonTranslator::TranslateWindow(pIdNode->FirstChild()->Value());
273       }
274
275       CWindowSounds sounds;
276       sounds.initSound   = LoadWindowSound(pWindow, "activate"  );
277       sounds.deInitSound = LoadWindowSound(pWindow, "deactivate");
278
279       if (id > 0)
280         m_windowSoundMap.insert(pair<int, CWindowSounds>(id, sounds));
281
282       pWindow = pWindow->NextSibling();
283     }
284   }
285
286   return true;
287 }
288
289 IAESound* CGUIAudioManager::LoadSound(const CStdString &filename)
290 {
291   CSingleLock lock(m_cs);
292   soundCache::iterator it = m_soundCache.find(filename);
293   if (it != m_soundCache.end())
294   {
295     ++it->second.usage;
296     return it->second.sound;
297   }
298
299   IAESound *sound = CAEFactory::MakeSound(filename);
300   if (!sound)
301     return NULL;
302
303   CSoundInfo info;
304   info.usage = 1;
305   info.sound = sound;
306   m_soundCache[filename] = info;
307
308   return info.sound;
309 }
310
311 void CGUIAudioManager::FreeSound(IAESound *sound)
312 {
313   CSingleLock lock(m_cs);
314   for(soundCache::iterator it = m_soundCache.begin(); it != m_soundCache.end(); ++it) {
315     if (it->second.sound == sound) {
316       if (--it->second.usage == 0) {     
317         CAEFactory::FreeSound(sound);
318         m_soundCache.erase(it);
319       }
320       return;
321     }
322   }
323 }
324
325 // \brief Load a window node of the config file (sounds.xml)
326 IAESound* CGUIAudioManager::LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier)
327 {
328   if (!pWindowNode)
329     return NULL;
330
331   TiXmlNode* pFileNode = pWindowNode->FirstChild(strIdentifier);
332   if (pFileNode && pFileNode->FirstChild())
333     return LoadSound(URIUtils::AddFileToFolder(m_strMediaDir, pFileNode->FirstChild()->Value()));
334
335   return NULL;
336 }
337
338 // \brief Enable/Disable nav sounds
339 void CGUIAudioManager::Enable(bool bEnable)
340 {
341   // always deinit audio when we don't want gui sounds
342   if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF")
343     bEnable = false;
344
345   CSingleLock lock(m_cs);
346   m_bEnabled = bEnable;
347 }
348
349 // \brief Sets the volume of all playing sounds
350 void CGUIAudioManager::SetVolume(float level)
351 {
352   CSingleLock lock(m_cs);
353
354   {
355     actionSoundMap::iterator it = m_actionSoundMap.begin();
356     while (it!=m_actionSoundMap.end())
357     {
358       if (it->second)
359         it->second->SetVolume(level);
360       ++it;
361     }
362   }
363
364   for(windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
365   {
366     if (it->second.initSound  ) it->second.initSound  ->SetVolume(level);
367     if (it->second.deInitSound) it->second.deInitSound->SetVolume(level);
368   }
369
370   {
371     pythonSoundsMap::iterator it = m_pythonSounds.begin();
372     while (it != m_pythonSounds.end())
373     {
374       if (it->second)
375         it->second->SetVolume(level);
376
377       ++it;
378     }
379   }
380 }