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 "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"
34 CGUIAudioManager g_audioManager;
36 CGUIAudioManager::CGUIAudioManager()
41 CGUIAudioManager::~CGUIAudioManager()
45 void CGUIAudioManager::OnSettingChanged(const CSetting *setting)
50 const std::string &settingId = setting->GetId();
51 if (settingId == "lookandfeel.soundskin")
58 void CGUIAudioManager::Initialize()
62 void CGUIAudioManager::DeInitialize()
64 CSingleLock lock(m_cs);
68 void CGUIAudioManager::Stop()
70 CSingleLock lock(m_cs);
71 for (windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
73 if (it->second.initSound ) it->second.initSound ->Stop();
74 if (it->second.deInitSound) it->second.deInitSound->Stop();
77 for (pythonSoundsMap::iterator it = m_pythonSounds.begin(); it != m_pythonSounds.end(); ++it)
79 IAESound* sound = it->second;
84 // \brief Play a sound associated with a CAction
85 void CGUIAudioManager::PlayActionSound(const CAction& action)
87 CSingleLock lock(m_cs);
89 // it's not possible to play gui sounds when passthrough is active
93 actionSoundMap::iterator it = m_actionSoundMap.find(action.GetID());
94 if (it == m_actionSoundMap.end())
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)
105 CSingleLock lock(m_cs);
107 // it's not possible to play gui sounds when passthrough is active
111 windowSoundMap::iterator it=m_windowSoundMap.find(id);
112 if (it==m_windowSoundMap.end())
115 CWindowSounds sounds=it->second;
116 IAESound *sound = NULL;
120 sound = sounds.initSound;
123 sound = sounds.deInitSound;
133 // \brief Play a sound given by filename
134 void CGUIAudioManager::PlayPythonSound(const CStdString& strFileName)
136 CSingleLock lock(m_cs);
138 // it's not possible to play gui sounds when passthrough is active
142 // If we already loaded the sound, just play it
143 pythonSoundsMap::iterator itsb=m_pythonSounds.find(strFileName);
144 if (itsb != m_pythonSounds.end())
146 IAESound* sound = itsb->second;
151 IAESound *sound = LoadSound(strFileName);
155 m_pythonSounds.insert(pair<const CStdString, IAESound*>(strFileName, sound));
159 void CGUIAudioManager::UnLoad()
161 // Free sounds from windows
163 windowSoundMap::iterator it = m_windowSoundMap.begin();
164 while (it != m_windowSoundMap.end())
166 if (it->second.initSound ) FreeSound(it->second.initSound );
167 if (it->second.deInitSound) FreeSound(it->second.deInitSound);
168 m_windowSoundMap.erase(it++);
172 // Free sounds from python
174 pythonSoundsMap::iterator it = m_pythonSounds.begin();
175 while (it != m_pythonSounds.end())
177 IAESound* sound = it->second;
179 m_pythonSounds.erase(it++);
183 // free action sounds
185 actionSoundMap::iterator it = m_actionSoundMap.begin();
186 while (it != m_actionSoundMap.end())
188 IAESound* sound = it->second;
190 m_actionSoundMap.erase(it++);
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
199 bool CGUIAudioManager::Load()
201 CSingleLock lock(m_cs);
205 if (CSettings::Get().GetString("lookandfeel.soundskin")=="OFF")
210 if (CSettings::Get().GetString("lookandfeel.soundskin")=="SKINDEFAULT")
212 m_strMediaDir = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "sounds");
215 m_strMediaDir = URIUtils::AddFileToFolder("special://xbmc/sounds", CSettings::Get().GetString("lookandfeel.soundskin"));
217 CStdString strSoundsXml = URIUtils::AddFileToFolder(m_strMediaDir, "sounds.xml");
222 CLog::Log(LOGINFO, "Loading %s", strSoundsXml.c_str());
224 // Load the config file
225 if (!xmlDoc.LoadFile(strSoundsXml))
227 CLog::Log(LOGNOTICE, "%s, Line %d\n%s", strSoundsXml.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
231 TiXmlElement* pRoot = xmlDoc.RootElement();
232 CStdString strValue = pRoot->Value();
233 if ( strValue != "sounds")
235 CLog::Log(LOGNOTICE, "%s Doesn't contain <sounds>", strSoundsXml.c_str());
239 // Load sounds for actions
240 TiXmlElement* pActions = pRoot->FirstChildElement("actions");
243 TiXmlNode* pAction = pActions->FirstChild("action");
247 TiXmlNode* pIdNode = pAction->FirstChild("name");
248 int id = 0; // action identity
249 if (pIdNode && pIdNode->FirstChild())
251 CButtonTranslator::TranslateActionString(pIdNode->FirstChild()->Value(), id);
254 TiXmlNode* pFileNode = pAction->FirstChild("file");
256 if (pFileNode && pFileNode->FirstChild())
257 strFile += pFileNode->FirstChild()->Value();
259 if (id > 0 && !strFile.empty())
261 CStdString filename = URIUtils::AddFileToFolder(m_strMediaDir, strFile);
262 IAESound *sound = LoadSound(filename);
264 m_actionSoundMap.insert(pair<int, IAESound *>(id, sound));
267 pAction = pAction->NextSibling();
271 // Load window specific sounds
272 TiXmlElement* pWindows = pRoot->FirstChildElement("windows");
275 TiXmlNode* pWindow = pWindows->FirstChild("window");
281 TiXmlNode* pIdNode = pWindow->FirstChild("name");
284 if (pIdNode->FirstChild())
285 id = CButtonTranslator::TranslateWindow(pIdNode->FirstChild()->Value());
288 CWindowSounds sounds;
289 sounds.initSound = LoadWindowSound(pWindow, "activate" );
290 sounds.deInitSound = LoadWindowSound(pWindow, "deactivate");
293 m_windowSoundMap.insert(pair<int, CWindowSounds>(id, sounds));
295 pWindow = pWindow->NextSibling();
302 IAESound* CGUIAudioManager::LoadSound(const CStdString &filename)
304 CSingleLock lock(m_cs);
305 soundCache::iterator it = m_soundCache.find(filename);
306 if (it != m_soundCache.end())
309 return it->second.sound;
312 IAESound *sound = CAEFactory::MakeSound(filename);
319 m_soundCache[filename] = info;
324 void CGUIAudioManager::FreeSound(IAESound *sound)
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);
338 // \brief Load a window node of the config file (sounds.xml)
339 IAESound* CGUIAudioManager::LoadWindowSound(TiXmlNode* pWindowNode, const CStdString& strIdentifier)
344 TiXmlNode* pFileNode = pWindowNode->FirstChild(strIdentifier);
345 if (pFileNode && pFileNode->FirstChild())
346 return LoadSound(URIUtils::AddFileToFolder(m_strMediaDir, pFileNode->FirstChild()->Value()));
351 // \brief Enable/Disable nav sounds
352 void CGUIAudioManager::Enable(bool bEnable)
354 // always deinit audio when we don't want gui sounds
355 if (CSettings::Get().GetString("lookandfeel.soundskin")=="OFF")
358 CSingleLock lock(m_cs);
359 m_bEnabled = bEnable;
362 // \brief Sets the volume of all playing sounds
363 void CGUIAudioManager::SetVolume(float level)
365 CSingleLock lock(m_cs);
368 actionSoundMap::iterator it = m_actionSoundMap.begin();
369 while (it!=m_actionSoundMap.end())
372 it->second->SetVolume(level);
377 for(windowSoundMap::iterator it = m_windowSoundMap.begin(); it != m_windowSoundMap.end(); ++it)
379 if (it->second.initSound ) it->second.initSound ->SetVolume(level);
380 if (it->second.deInitSound) it->second.deInitSound->SetVolume(level);
384 pythonSoundsMap::iterator it = m_pythonSounds.begin();
385 while (it != m_pythonSounds.end())
388 it->second->SetVolume(level);