[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / guilib / GUIVisualisationControl.cpp
blobfb02bc9f18a3703e7b20e884c3fc31b46d9e89f8
1 /*
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.
7 */
9 #include "GUIVisualisationControl.h"
11 #include "GUIComponent.h"
12 #include "GUIInfoManager.h"
13 #include "GUIUserMessages.h"
14 #include "GUIWindowManager.h"
15 #include "ServiceBroker.h"
16 #include "addons/AddonManager.h"
17 #include "addons/Visualization.h"
18 #include "addons/addoninfo/AddonType.h"
19 #include "application/Application.h"
20 #include "application/ApplicationComponents.h"
21 #include "application/ApplicationPlayer.h"
22 #include "cores/AudioEngine/Interfaces/AE.h"
23 #include "filesystem/SpecialProtocol.h"
24 #include "guilib/guiinfo/GUIInfoLabels.h"
25 #include "input/actions/Action.h"
26 #include "input/actions/ActionIDs.h"
27 #include "music/tags/MusicInfoTag.h"
28 #include "settings/AdvancedSettings.h"
29 #include "settings/Settings.h"
30 #include "settings/SettingsComponent.h"
31 #include "utils/URIUtils.h"
32 #include "utils/log.h"
34 #include <memory>
36 namespace
38 constexpr unsigned int MAX_AUDIO_BUFFERS = 16;
39 } // namespace
41 CAudioBuffer::CAudioBuffer(int iSize)
43 m_iLen = iSize;
44 m_pBuffer = new float[iSize];
47 CAudioBuffer::~CAudioBuffer()
49 delete[] m_pBuffer;
52 const float* CAudioBuffer::Get() const
54 return m_pBuffer;
57 int CAudioBuffer::Size() const
59 return m_iLen;
62 void CAudioBuffer::Set(const float* psBuffer, int iSize)
64 if (iSize < 0)
65 return;
67 memcpy(m_pBuffer, psBuffer, iSize * sizeof(float));
68 for (int i = iSize; i < m_iLen; ++i)
69 m_pBuffer[i] = 0;
72 CGUIVisualisationControl::CGUIVisualisationControl(
73 int parentID, int controlID, float posX, float posY, float width, float height)
74 : CGUIControl(parentID, controlID, posX, posY, width, height)
76 ControlType = GUICONTROL_VISUALISATION;
79 CGUIVisualisationControl::CGUIVisualisationControl(const CGUIVisualisationControl& from)
80 : CGUIControl(from)
82 ControlType = GUICONTROL_VISUALISATION;
85 std::string CGUIVisualisationControl::Name()
87 if (m_instance == nullptr)
88 return "";
89 return m_instance->Name();
92 bool CGUIVisualisationControl::OnMessage(CGUIMessage& message)
94 if (m_alreadyStarted)
96 switch (message.GetMessage())
98 case GUI_MSG_GET_VISUALISATION:
99 message.SetPointer(this);
100 return true;
101 case GUI_MSG_VISUALISATION_RELOAD:
102 FreeResources(true);
103 return true;
104 case GUI_MSG_PLAYBACK_STARTED:
105 m_updateTrack = true;
106 return true;
107 default:
108 break;
111 return CGUIControl::OnMessage(message);
114 bool CGUIVisualisationControl::OnAction(const CAction& action)
116 if (m_alreadyStarted)
118 switch (action.GetID())
120 case ACTION_VIS_PRESET_NEXT:
121 m_instance->NextPreset();
122 break;
123 case ACTION_VIS_PRESET_PREV:
124 m_instance->PrevPreset();
125 break;
126 case ACTION_VIS_PRESET_RANDOM:
127 m_instance->RandomPreset();
128 break;
129 case ACTION_VIS_RATE_PRESET_PLUS:
130 m_instance->RatePreset(true);
131 break;
132 case ACTION_VIS_RATE_PRESET_MINUS:
133 m_instance->RatePreset(false);
134 break;
135 case ACTION_VIS_PRESET_LOCK:
136 m_instance->LockPreset();
137 break;
138 default:
139 break;
141 return true;
144 return CGUIControl::OnAction(action);
147 void CGUIVisualisationControl::Process(unsigned int currentTime, CDirtyRegionList& dirtyregions)
149 const auto& components = CServiceBroker::GetAppComponents();
150 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
151 if (appPlayer->IsPlayingAudio())
153 if (m_bInvalidated)
154 FreeResources(true);
156 if (!m_instance && !m_attemptedLoad)
158 InitVisualization();
160 m_attemptedLoad = true;
162 else if (m_callStart && m_instance)
164 auto& context = CServiceBroker::GetWinSystem()->GetGfxContext();
166 context.CaptureStateBlock();
167 if (m_alreadyStarted)
169 m_instance->Stop();
170 m_alreadyStarted = false;
173 std::string songTitle = URIUtils::GetFileName(g_application.CurrentFile());
174 const MUSIC_INFO::CMusicInfoTag* tag =
175 CServiceBroker::GetGUI()->GetInfoManager().GetCurrentSongTag();
176 if (tag && !tag->GetTitle().empty())
177 songTitle = tag->GetTitle();
178 m_alreadyStarted = m_instance->Start(m_channels, m_samplesPerSec, m_bitsPerSample, songTitle);
179 context.ApplyStateBlock();
180 m_callStart = false;
181 m_updateTrack = true;
183 else if (m_updateTrack)
185 /* Initial update of currently processed track */
186 UpdateTrack();
187 m_updateTrack = false;
190 if (m_instance && m_instance->IsDirty())
191 MarkDirtyRegion();
194 CGUIControl::Process(currentTime, dirtyregions);
197 void CGUIVisualisationControl::Render()
199 if (m_instance && m_alreadyStarted)
201 auto& context = CServiceBroker::GetWinSystem()->GetGfxContext();
204 * set the viewport - note: We currently don't have any control over how
205 * the addon renders, so the best we can do is attempt to define
206 * a viewport??
208 context.SetViewPort(m_posX, m_posY, m_width, m_height);
209 context.CaptureStateBlock();
210 m_instance->Render();
211 context.ApplyStateBlock();
212 context.RestoreViewPort();
215 CGUIControl::Render();
218 void CGUIVisualisationControl::UpdateVisibility(const CGUIListItem* item /* = nullptr*/)
220 // if made invisible, start timer, only free addonptr after
221 // some period, configurable by window class
222 CGUIControl::UpdateVisibility(item);
223 if (!IsVisible() && m_attemptedLoad)
224 FreeResources();
227 bool CGUIVisualisationControl::CanFocusFromPoint(const CPoint& point) const
228 { // mouse is allowed to focus this control, but it doesn't actually receive focus
229 return IsVisible() && HitTest(point);
232 void CGUIVisualisationControl::FreeResources(bool immediately)
234 DeInitVisualization();
236 CGUIControl::FreeResources(immediately);
238 CLog::Log(LOGDEBUG, "FreeVisualisation() done");
241 void CGUIVisualisationControl::OnInitialize(int channels, int samplesPerSec, int bitsPerSample)
243 m_channels = channels;
244 m_samplesPerSec = samplesPerSec;
245 m_bitsPerSample = bitsPerSample;
246 m_callStart = true;
249 void CGUIVisualisationControl::OnAudioData(const float* audioData, unsigned int audioDataLength)
251 if (!m_instance || !m_alreadyStarted || !audioData || audioDataLength == 0)
252 return;
254 // Save our audio data in the buffers
255 std::unique_ptr<CAudioBuffer> pBuffer(new CAudioBuffer(audioDataLength));
256 pBuffer->Set(audioData, audioDataLength);
257 m_vecBuffers.emplace_back(std::move(pBuffer));
259 if (m_vecBuffers.size() < m_numBuffers)
260 return;
262 std::unique_ptr<CAudioBuffer> ptrAudioBuffer = std::move(m_vecBuffers.front());
263 m_vecBuffers.pop_front();
265 // Transfer data to our visualisation
266 m_instance->AudioData(ptrAudioBuffer->Get(), ptrAudioBuffer->Size());
269 void CGUIVisualisationControl::UpdateTrack()
271 if (!m_instance || !m_alreadyStarted)
272 return;
274 // get the current album art filename
275 m_albumThumb = CSpecialProtocol::TranslatePath(
276 CServiceBroker::GetGUI()->GetInfoManager().GetImage(MUSICPLAYER_COVER, WINDOW_INVALID));
277 if (m_albumThumb == "DefaultAlbumCover.png")
278 m_albumThumb = "";
279 else
280 CLog::Log(LOGDEBUG, "Updating visualization albumart: {}", m_albumThumb);
282 m_instance->UpdateAlbumart(m_albumThumb.c_str());
284 const MUSIC_INFO::CMusicInfoTag* tag =
285 CServiceBroker::GetGUI()->GetInfoManager().GetCurrentSongTag();
286 if (!tag)
287 return;
289 const std::string artist(tag->GetArtistString());
290 const std::string albumArtist(tag->GetAlbumArtistString());
291 const std::string genre(StringUtils::Join(
292 tag->GetGenre(),
293 CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_musicItemSeparator));
295 KODI_ADDON_VISUALIZATION_TRACK track = {};
296 track.title = tag->GetTitle().c_str();
297 track.artist = artist.c_str();
298 track.album = tag->GetAlbum().c_str();
299 track.albumArtist = albumArtist.c_str();
300 track.genre = genre.c_str();
301 track.comment = tag->GetComment().c_str();
302 track.lyrics = tag->GetLyrics().c_str();
303 track.trackNumber = tag->GetTrackNumber();
304 track.discNumber = tag->GetDiscNumber();
305 track.duration = tag->GetDuration();
306 track.year = tag->GetYear();
307 track.rating = tag->GetUserrating();
309 m_instance->UpdateTrack(&track);
312 bool CGUIVisualisationControl::IsLocked()
314 if (m_instance && m_alreadyStarted)
315 return m_instance->IsLocked();
317 return false;
320 bool CGUIVisualisationControl::HasPresets()
322 if (m_instance && m_alreadyStarted)
323 return m_instance->HasPresets();
325 return false;
328 int CGUIVisualisationControl::GetActivePreset()
330 if (m_instance && m_alreadyStarted)
331 return m_instance->GetActivePreset();
333 return -1;
336 void CGUIVisualisationControl::SetPreset(int idx)
338 if (m_instance && m_alreadyStarted)
339 m_instance->LoadPreset(idx);
342 std::string CGUIVisualisationControl::GetActivePresetName()
344 if (m_instance && m_alreadyStarted)
345 return m_instance->GetActivePresetName();
347 return "";
350 bool CGUIVisualisationControl::GetPresetList(std::vector<std::string>& vecpresets)
352 if (m_instance && m_alreadyStarted)
353 return m_instance->GetPresetList(vecpresets);
355 return false;
358 bool CGUIVisualisationControl::InitVisualization()
360 IAE* ae = CServiceBroker::GetActiveAE();
361 CWinSystemBase* const winSystem = CServiceBroker::GetWinSystem();
362 if (!ae || !winSystem)
363 return false;
365 const std::string addon = CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(
366 CSettings::SETTING_MUSICPLAYER_VISUALISATION);
367 const ADDON::AddonInfoPtr addonBase =
368 CServiceBroker::GetAddonMgr().GetAddonInfo(addon, ADDON::AddonType::VISUALIZATION);
369 if (!addonBase)
370 return false;
372 ae->RegisterAudioCallback(this);
374 auto& context = winSystem->GetGfxContext();
376 context.CaptureStateBlock();
378 float x = context.ScaleFinalXCoord(GetXPosition(), GetYPosition());
379 float y = context.ScaleFinalYCoord(GetXPosition(), GetYPosition());
380 float w = context.ScaleFinalXCoord(GetXPosition() + GetWidth(), GetYPosition() + GetHeight()) - x;
381 float h = context.ScaleFinalYCoord(GetXPosition() + GetWidth(), GetYPosition() + GetHeight()) - y;
382 if (x < 0)
383 x = 0;
384 if (y < 0)
385 y = 0;
386 if (x + w > context.GetWidth())
387 w = context.GetWidth() - x;
388 if (y + h > context.GetHeight())
389 h = context.GetHeight() - y;
391 m_instance = std::make_unique<KODI::ADDONS::CVisualization>(addonBase, x, y, w, h);
392 CreateBuffers();
394 m_alreadyStarted = false;
395 context.ApplyStateBlock();
396 return true;
399 void CGUIVisualisationControl::DeInitVisualization()
401 if (!m_attemptedLoad)
402 return;
404 CWinSystemBase* const winSystem = CServiceBroker::GetWinSystem();
405 if (!winSystem)
406 return;
408 IAE* ae = CServiceBroker::GetActiveAE();
409 if (ae)
410 ae->UnregisterAudioCallback(this);
412 m_attemptedLoad = false;
414 CGUIMessage msg(GUI_MSG_VISUALISATION_UNLOADING, m_controlID, 0);
415 CServiceBroker::GetGUI()->GetWindowManager().SendMessage(msg);
417 CLog::Log(LOGDEBUG, "FreeVisualisation() started");
419 if (m_instance)
421 if (m_alreadyStarted)
423 auto& context = winSystem->GetGfxContext();
425 context.CaptureStateBlock();
426 m_instance->Stop();
427 context.ApplyStateBlock();
428 m_alreadyStarted = false;
431 m_instance.reset();
434 ClearBuffers();
437 void CGUIVisualisationControl::CreateBuffers()
439 ClearBuffers();
441 m_numBuffers = 1;
442 if (m_instance)
443 m_numBuffers += m_instance->GetSyncDelay();
444 if (m_numBuffers > MAX_AUDIO_BUFFERS)
445 m_numBuffers = MAX_AUDIO_BUFFERS;
446 if (m_numBuffers < 1)
447 m_numBuffers = 1;
450 void CGUIVisualisationControl::ClearBuffers()
452 m_numBuffers = 0;
453 m_vecBuffers.clear();