2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
22 #include "GUIAudioManager.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"
34 CGUIAudioManager g_audioManager;
36 CGUIAudioManager::CGUIAudioManager()
41 CGUIAudioManager::~CGUIAudioManager()
45 void CGUIAudioManager::Initialize()
49 void CGUIAudioManager::DeInitialize()
51 CSingleLock lock(m_cs);
55 void CGUIAudioManager::Stop()
57 CSingleLock lock(m_cs);
58 for (windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
60 if (it->second.initSound ) it->second.initSound ->Stop();
61 if (it->second.deInitSound) it->second.deInitSound->Stop();
64 for (pythonSoundsMap::iterator it = m_pythonSounds.begin(); it != m_pythonSounds.end(); ++it)
66 IAESound* sound = it->second;
71 // \brief Play a sound associated with a CAction
72 void CGUIAudioManager::PlayActionSound(const CAction& action)
74 CSingleLock lock(m_cs);
76 // it's not possible to play gui sounds when passthrough is active
80 actionSoundMap::iterator it = m_actionSoundMap.find(action.GetID());
81 if (it == m_actionSoundMap.end())
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)
92 CSingleLock lock(m_cs);
94 // it's not possible to play gui sounds when passthrough is active
98 windowSoundMap::iterator it=m_windowSoundMap.find(id);
99 if (it==m_windowSoundMap.end())
102 CWindowSounds sounds=it->second;
103 IAESound *sound = NULL;
107 sound = sounds.initSound;
110 sound = sounds.deInitSound;
120 // \brief Play a sound given by filename
121 void CGUIAudioManager::PlayPythonSound(const CStdString& strFileName)
123 CSingleLock lock(m_cs);
125 // it's not possible to play gui sounds when passthrough is active
129 // If we already loaded the sound, just play it
130 pythonSoundsMap::iterator itsb=m_pythonSounds.find(strFileName);
131 if (itsb != m_pythonSounds.end())
133 IAESound* sound = itsb->second;
138 IAESound *sound = LoadSound(strFileName);
142 m_pythonSounds.insert(pair<const CStdString, IAESound*>(strFileName, sound));
146 void CGUIAudioManager::UnLoad()
148 // Free sounds from windows
150 windowSoundMap::iterator it = m_windowSoundMap.begin();
151 while (it != m_windowSoundMap.end())
153 if (it->second.initSound ) FreeSound(it->second.initSound );
154 if (it->second.deInitSound) FreeSound(it->second.deInitSound);
155 m_windowSoundMap.erase(it++);
159 // Free sounds from python
161 pythonSoundsMap::iterator it = m_pythonSounds.begin();
162 while (it != m_pythonSounds.end())
164 IAESound* sound = it->second;
166 m_pythonSounds.erase(it++);
170 // free action sounds
172 actionSoundMap::iterator it = m_actionSoundMap.begin();
173 while (it != m_actionSoundMap.end())
175 IAESound* sound = it->second;
177 m_actionSoundMap.erase(it++);
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
186 bool CGUIAudioManager::Load()
188 CSingleLock lock(m_cs);
192 if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF")
197 if (g_guiSettings.GetString("lookandfeel.soundskin")=="SKINDEFAULT")
199 m_strMediaDir = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "sounds");
202 m_strMediaDir = URIUtils::AddFileToFolder("special://xbmc/sounds", g_guiSettings.GetString("lookandfeel.soundskin"));
204 CStdString strSoundsXml = URIUtils::AddFileToFolder(m_strMediaDir, "sounds.xml");
209 CLog::Log(LOGINFO, "Loading %s", strSoundsXml.c_str());
211 // Load the config file
212 if (!xmlDoc.LoadFile(strSoundsXml))
214 CLog::Log(LOGNOTICE, "%s, Line %d\n%s", strSoundsXml.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
218 TiXmlElement* pRoot = xmlDoc.RootElement();
219 CStdString strValue = pRoot->Value();
220 if ( strValue != "sounds")
222 CLog::Log(LOGNOTICE, "%s Doesn't contain <sounds>", strSoundsXml.c_str());
226 // Load sounds for actions
227 TiXmlElement* pActions = pRoot->FirstChildElement("actions");
230 TiXmlNode* pAction = pActions->FirstChild("action");
234 TiXmlNode* pIdNode = pAction->FirstChild("name");
235 int id = 0; // action identity
236 if (pIdNode && pIdNode->FirstChild())
238 CButtonTranslator::TranslateActionString(pIdNode->FirstChild()->Value(), id);
241 TiXmlNode* pFileNode = pAction->FirstChild("file");
243 if (pFileNode && pFileNode->FirstChild())
244 strFile += pFileNode->FirstChild()->Value();
246 if (id > 0 && !strFile.IsEmpty())
248 CStdString filename = URIUtils::AddFileToFolder(m_strMediaDir, strFile);
249 IAESound *sound = LoadSound(filename);
251 m_actionSoundMap.insert(pair<int, IAESound *>(id, sound));
254 pAction = pAction->NextSibling();
258 // Load window specific sounds
259 TiXmlElement* pWindows = pRoot->FirstChildElement("windows");
262 TiXmlNode* pWindow = pWindows->FirstChild("window");
268 TiXmlNode* pIdNode = pWindow->FirstChild("name");
271 if (pIdNode->FirstChild())
272 id = CButtonTranslator::TranslateWindow(pIdNode->FirstChild()->Value());
275 CWindowSounds sounds;
276 sounds.initSound = LoadWindowSound(pWindow, "activate" );
277 sounds.deInitSound = LoadWindowSound(pWindow, "deactivate");
280 m_windowSoundMap.insert(pair<int, CWindowSounds>(id, sounds));
282 pWindow = pWindow->NextSibling();
289 IAESound* CGUIAudioManager::LoadSound(const CStdString &filename)
291 CSingleLock lock(m_cs);
292 soundCache::iterator it = m_soundCache.find(filename);
293 if (it != m_soundCache.end())
296 return it->second.sound;
299 IAESound *sound = CAEFactory::MakeSound(filename);
306 m_soundCache[filename] = info;
311 void CGUIAudioManager::FreeSound(IAESound *sound)
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);
325 // \brief Load a window node of the config file (sounds.xml)
326 IAESound* CGUIAudioManager::LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier)
331 TiXmlNode* pFileNode = pWindowNode->FirstChild(strIdentifier);
332 if (pFileNode && pFileNode->FirstChild())
333 return LoadSound(URIUtils::AddFileToFolder(m_strMediaDir, pFileNode->FirstChild()->Value()));
338 // \brief Enable/Disable nav sounds
339 void CGUIAudioManager::Enable(bool bEnable)
341 // always deinit audio when we don't want gui sounds
342 if (g_guiSettings.GetString("lookandfeel.soundskin")=="OFF")
345 CSingleLock lock(m_cs);
346 m_bEnabled = bEnable;
349 // \brief Sets the volume of all playing sounds
350 void CGUIAudioManager::SetVolume(float level)
352 CSingleLock lock(m_cs);
355 actionSoundMap::iterator it = m_actionSoundMap.begin();
356 while (it!=m_actionSoundMap.end())
359 it->second->SetVolume(level);
364 for(windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
366 if (it->second.initSound ) it->second.initSound ->SetVolume(level);
367 if (it->second.deInitSound) it->second.deInitSound->SetVolume(level);
371 pythonSoundsMap::iterator it = m_pythonSounds.begin();
372 while (it != m_pythonSounds.end())
375 it->second->SetVolume(level);