2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "GUIAudioManager.h"
11 #include "ServiceBroker.h"
12 #include "addons/AddonManager.h"
13 #include "addons/Skin.h"
14 #include "addons/addoninfo/AddonType.h"
15 #include "cores/AudioEngine/Interfaces/AE.h"
16 #include "filesystem/Directory.h"
17 #include "input/WindowTranslator.h"
18 #include "input/actions/Action.h"
19 #include "input/actions/ActionIDs.h"
20 #include "input/actions/ActionTranslator.h"
21 #include "settings/Settings.h"
22 #include "settings/SettingsComponent.h"
23 #include "settings/lib/Setting.h"
24 #include "utils/URIUtils.h"
25 #include "utils/XBMCTinyXML.h"
26 #include "utils/log.h"
32 CGUIAudioManager::CGUIAudioManager()
33 : m_settings(CServiceBroker::GetSettingsComponent()->GetSettings())
37 m_settings
->RegisterCallback(this, {CSettings::SETTING_LOOKANDFEEL_SOUNDSKIN
,
38 CSettings::SETTING_AUDIOOUTPUT_GUISOUNDVOLUME
});
41 CGUIAudioManager::~CGUIAudioManager()
43 m_settings
->UnregisterCallback(this);
46 void CGUIAudioManager::OnSettingChanged(const std::shared_ptr
<const CSetting
>& setting
)
51 const std::string
&settingId
= setting
->GetId();
52 if (settingId
== CSettings::SETTING_LOOKANDFEEL_SOUNDSKIN
)
59 bool CGUIAudioManager::OnSettingUpdate(const std::shared_ptr
<CSetting
>& setting
,
60 const char* oldSettingId
,
61 const TiXmlNode
* oldSettingNode
)
66 if (setting
->GetId() == CSettings::SETTING_LOOKANDFEEL_SOUNDSKIN
)
68 //Migrate the old settings
69 if (std::static_pointer_cast
<CSettingString
>(setting
)->GetValue() == "SKINDEFAULT")
70 std::static_pointer_cast
<CSettingString
>(setting
)->Reset();
71 else if (std::static_pointer_cast
<CSettingString
>(setting
)->GetValue() == "OFF")
72 std::static_pointer_cast
<CSettingString
>(setting
)->SetValue("");
74 if (setting
->GetId() == CSettings::SETTING_AUDIOOUTPUT_GUISOUNDVOLUME
)
76 int vol
= m_settings
->GetInt(CSettings::SETTING_AUDIOOUTPUT_GUISOUNDVOLUME
);
77 SetVolume(0.01f
* vol
);
83 void CGUIAudioManager::Initialize()
87 void CGUIAudioManager::DeInitialize()
89 std::unique_lock
<CCriticalSection
> lock(m_cs
);
93 void CGUIAudioManager::Stop()
95 std::unique_lock
<CCriticalSection
> lock(m_cs
);
96 for (const auto& windowSound
: m_windowSoundMap
)
98 if (windowSound
.second
.initSound
)
99 windowSound
.second
.initSound
->Stop();
100 if (windowSound
.second
.deInitSound
)
101 windowSound
.second
.deInitSound
->Stop();
104 for (const auto& pythonSound
: m_pythonSounds
)
106 pythonSound
.second
->Stop();
110 // \brief Play a sound associated with a CAction
111 void CGUIAudioManager::PlayActionSound(const CAction
& action
)
113 std::unique_lock
<CCriticalSection
> lock(m_cs
);
115 // it's not possible to play gui sounds when passthrough is active
119 const auto it
= m_actionSoundMap
.find(action
.GetID());
120 if (it
== m_actionSoundMap
.end())
125 int vol
= m_settings
->GetInt(CSettings::SETTING_AUDIOOUTPUT_GUISOUNDVOLUME
);
126 it
->second
->SetVolume(0.01f
* vol
);
131 // \brief Play a sound associated with a window and its event
132 // Events: SOUND_INIT, SOUND_DEINIT
133 void CGUIAudioManager::PlayWindowSound(int id
, WINDOW_SOUND event
)
135 std::unique_lock
<CCriticalSection
> lock(m_cs
);
137 // it's not possible to play gui sounds when passthrough is active
141 const auto it
= m_windowSoundMap
.find(id
);
142 if (it
==m_windowSoundMap
.end())
145 std::shared_ptr
<IAESound
> sound
;
149 sound
= it
->second
.initSound
;
152 sound
= it
->second
.deInitSound
;
159 int vol
= m_settings
->GetInt(CSettings::SETTING_AUDIOOUTPUT_GUISOUNDVOLUME
);
160 sound
->SetVolume(0.01f
* vol
);
164 // \brief Play a sound given by filename
165 void CGUIAudioManager::PlayPythonSound(const std::string
& strFileName
, bool useCached
/*= true*/)
167 std::unique_lock
<CCriticalSection
> lock(m_cs
);
169 // it's not possible to play gui sounds when passthrough is active
173 // If we already loaded the sound, just play it
174 const auto itsb
= m_pythonSounds
.find(strFileName
);
175 if (itsb
!= m_pythonSounds
.end())
177 const auto& sound
= itsb
->second
;
185 m_pythonSounds
.erase(itsb
);
189 auto sound
= LoadSound(strFileName
);
193 int vol
= m_settings
->GetInt(CSettings::SETTING_AUDIOOUTPUT_GUISOUNDVOLUME
);
194 sound
->SetVolume(0.01f
* vol
);
196 m_pythonSounds
.emplace(strFileName
, std::move(sound
));
199 void CGUIAudioManager::UnLoad()
201 m_windowSoundMap
.clear();
202 m_pythonSounds
.clear();
203 m_actionSoundMap
.clear();
204 m_soundCache
.clear();
208 std::string
GetSoundSkinPath()
210 auto setting
= std::static_pointer_cast
<CSettingString
>(CServiceBroker::GetSettingsComponent()->GetSettings()->GetSetting(CSettings::SETTING_LOOKANDFEEL_SOUNDSKIN
));
211 auto value
= setting
->GetValue();
215 ADDON::AddonPtr addon
;
216 if (!CServiceBroker::GetAddonMgr().GetAddon(value
, addon
, ADDON::AddonType::RESOURCE_UISOUNDS
,
217 ADDON::OnlyEnabled::CHOICE_YES
))
219 CLog::Log(LOGINFO
, "Unknown sounds addon '{}'. Setting default sounds.", value
);
222 return URIUtils::AddFileToFolder("resource://", setting
->GetValue());
226 // \brief Load the config file (sounds.xml) for nav sounds
227 bool CGUIAudioManager::Load()
229 std::unique_lock
<CCriticalSection
> lock(m_cs
);
232 m_strMediaDir
= GetSoundSkinPath();
233 if (m_strMediaDir
.empty())
237 std::string strSoundsXml
= URIUtils::AddFileToFolder(m_strMediaDir
, "sounds.xml");
242 CLog::Log(LOGINFO
, "Loading {}", strSoundsXml
);
244 // Load the config file
245 if (!xmlDoc
.LoadFile(strSoundsXml
))
247 CLog::Log(LOGINFO
, "{}, Line {}\n{}", strSoundsXml
, xmlDoc
.ErrorRow(), xmlDoc
.ErrorDesc());
251 TiXmlElement
* pRoot
= xmlDoc
.RootElement();
252 std::string strValue
= pRoot
->Value();
253 if ( strValue
!= "sounds")
255 CLog::Log(LOGINFO
, "{} Doesn't contain <sounds>", strSoundsXml
);
259 // Load sounds for actions
260 TiXmlElement
* pActions
= pRoot
->FirstChildElement("actions");
263 TiXmlNode
* pAction
= pActions
->FirstChild("action");
267 TiXmlNode
* pIdNode
= pAction
->FirstChild("name");
268 unsigned int id
= ACTION_NONE
; // action identity
269 if (pIdNode
&& pIdNode
->FirstChild())
271 ACTION::CActionTranslator::TranslateString(pIdNode
->FirstChild()->Value(), id
);
274 TiXmlNode
* pFileNode
= pAction
->FirstChild("file");
276 if (pFileNode
&& pFileNode
->FirstChild())
277 strFile
+= pFileNode
->FirstChild()->Value();
279 if (id
!= ACTION_NONE
&& !strFile
.empty())
281 std::string filename
= URIUtils::AddFileToFolder(m_strMediaDir
, strFile
);
282 auto sound
= LoadSound(filename
);
284 m_actionSoundMap
.emplace(id
, std::move(sound
));
287 pAction
= pAction
->NextSibling();
291 // Load window specific sounds
292 TiXmlElement
* pWindows
= pRoot
->FirstChildElement("windows");
295 TiXmlNode
* pWindow
= pWindows
->FirstChild("window");
301 TiXmlNode
* pIdNode
= pWindow
->FirstChild("name");
304 if (pIdNode
->FirstChild())
305 id
= CWindowTranslator::TranslateWindow(pIdNode
->FirstChild()->Value());
308 CWindowSounds sounds
;
309 sounds
.initSound
= LoadWindowSound(pWindow
, "activate" );
310 sounds
.deInitSound
= LoadWindowSound(pWindow
, "deactivate");
313 m_windowSoundMap
.insert(std::pair
<int, CWindowSounds
>(id
, sounds
));
315 pWindow
= pWindow
->NextSibling();
322 std::shared_ptr
<IAESound
> CGUIAudioManager::LoadSound(const std::string
& filename
)
324 std::unique_lock
<CCriticalSection
> lock(m_cs
);
325 const auto it
= m_soundCache
.find(filename
);
326 if (it
!= m_soundCache
.end())
328 auto sound
= it
->second
.lock();
332 m_soundCache
.erase(it
); // cleanup orphaned cache entry
335 IAE
*ae
= CServiceBroker::GetActiveAE();
339 std::shared_ptr
<IAESound
> sound(ae
->MakeSound(filename
));
343 m_soundCache
[filename
] = sound
;
348 // \brief Load a window node of the config file (sounds.xml)
349 std::shared_ptr
<IAESound
> CGUIAudioManager::LoadWindowSound(TiXmlNode
* pWindowNode
,
350 const std::string
& strIdentifier
)
355 TiXmlNode
* pFileNode
= pWindowNode
->FirstChild(strIdentifier
);
356 if (pFileNode
&& pFileNode
->FirstChild())
357 return LoadSound(URIUtils::AddFileToFolder(m_strMediaDir
, pFileNode
->FirstChild()->Value()));
362 // \brief Enable/Disable nav sounds
363 void CGUIAudioManager::Enable(bool bEnable
)
365 // always deinit audio when we don't want gui sounds
366 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOOKANDFEEL_SOUNDSKIN
).empty())
369 std::unique_lock
<CCriticalSection
> lock(m_cs
);
370 m_bEnabled
= bEnable
;
373 // \brief Sets the volume of all playing sounds
374 void CGUIAudioManager::SetVolume(float level
)
376 std::unique_lock
<CCriticalSection
> lock(m_cs
);
379 for (const auto& actionSound
: m_actionSoundMap
)
381 if (actionSound
.second
)
382 actionSound
.second
->SetVolume(level
);
386 for (const auto& windowSound
: m_windowSoundMap
)
388 if (windowSound
.second
.initSound
)
389 windowSound
.second
.initSound
->SetVolume(level
);
390 if (windowSound
.second
.deInitSound
)
391 windowSound
.second
.deInitSound
->SetVolume(level
);
395 for (const auto& pythonSound
: m_pythonSounds
)
397 if (pythonSound
.second
)
398 pythonSound
.second
->SetVolume(level
);