diff options
| author | manuel <manuel@mausz.at> | 2015-03-03 16:53:59 +0100 |
|---|---|---|
| committer | manuel <manuel@mausz.at> | 2015-03-03 16:53:59 +0100 |
| commit | ffca21f2743a7b367fa212799c6e2fea6190dd5d (patch) | |
| tree | 0608ea3a29cf644ec9ab204e2b4bb9bfaae1c381 /xbmc/addons/Visualisation.cpp | |
| download | kodi-pvr-build-ffca21f2743a7b367fa212799c6e2fea6190dd5d.tar.gz kodi-pvr-build-ffca21f2743a7b367fa212799c6e2fea6190dd5d.tar.bz2 kodi-pvr-build-ffca21f2743a7b367fa212799c6e2fea6190dd5d.zip | |
initial commit for kodi master
Diffstat (limited to 'xbmc/addons/Visualisation.cpp')
| -rw-r--r-- | xbmc/addons/Visualisation.cpp | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/xbmc/addons/Visualisation.cpp b/xbmc/addons/Visualisation.cpp new file mode 100644 index 0000000..a64ee59 --- /dev/null +++ b/xbmc/addons/Visualisation.cpp | |||
| @@ -0,0 +1,486 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2013 Team XBMC | ||
| 3 | * http://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 | #include "system.h" | ||
| 21 | #include "Visualisation.h" | ||
| 22 | #include "utils/fft.h" | ||
| 23 | #include "GUIInfoManager.h" | ||
| 24 | #include "Application.h" | ||
| 25 | #include "guilib/GraphicContext.h" | ||
| 26 | #include "guilib/WindowIDs.h" | ||
| 27 | #include "music/tags/MusicInfoTag.h" | ||
| 28 | #include "settings/Settings.h" | ||
| 29 | #include "settings/AdvancedSettings.h" | ||
| 30 | #include "settings/DisplaySettings.h" | ||
| 31 | #include "windowing/WindowingFactory.h" | ||
| 32 | #include "utils/URIUtils.h" | ||
| 33 | #include "utils/StringUtils.h" | ||
| 34 | #include "cores/IPlayer.h" | ||
| 35 | #include "cores/AudioEngine/AEFactory.h" | ||
| 36 | #ifdef TARGET_POSIX | ||
| 37 | #include <dlfcn.h> | ||
| 38 | #include "filesystem/SpecialProtocol.h" | ||
| 39 | #endif | ||
| 40 | |||
| 41 | using namespace std; | ||
| 42 | using namespace MUSIC_INFO; | ||
| 43 | using namespace ADDON; | ||
| 44 | |||
| 45 | CAudioBuffer::CAudioBuffer(int iSize) | ||
| 46 | { | ||
| 47 | m_iLen = iSize; | ||
| 48 | m_pBuffer = new float[iSize]; | ||
| 49 | } | ||
| 50 | |||
| 51 | CAudioBuffer::~CAudioBuffer() | ||
| 52 | { | ||
| 53 | delete [] m_pBuffer; | ||
| 54 | } | ||
| 55 | |||
| 56 | const float* CAudioBuffer::Get() const | ||
| 57 | { | ||
| 58 | return m_pBuffer; | ||
| 59 | } | ||
| 60 | |||
| 61 | void CAudioBuffer::Set(const float* psBuffer, int iSize) | ||
| 62 | { | ||
| 63 | if (iSize<0) | ||
| 64 | return; | ||
| 65 | memcpy(m_pBuffer, psBuffer, iSize * sizeof(float)); | ||
| 66 | for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0; | ||
| 67 | } | ||
| 68 | |||
| 69 | bool CVisualisation::Create(int x, int y, int w, int h, void *device) | ||
| 70 | { | ||
| 71 | m_pInfo = new VIS_PROPS; | ||
| 72 | #ifdef HAS_DX | ||
| 73 | m_pInfo->device = g_Windowing.Get3DDevice(); | ||
| 74 | #else | ||
| 75 | m_pInfo->device = NULL; | ||
| 76 | #endif | ||
| 77 | m_pInfo->x = x; | ||
| 78 | m_pInfo->y = y; | ||
| 79 | m_pInfo->width = w; | ||
| 80 | m_pInfo->height = h; | ||
| 81 | m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio; | ||
| 82 | |||
| 83 | m_pInfo->name = strdup(Name().c_str()); | ||
| 84 | m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str()); | ||
| 85 | m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str()); | ||
| 86 | m_pInfo->submodule = NULL; | ||
| 87 | |||
| 88 | if (CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Create() == ADDON_STATUS_OK) | ||
| 89 | { | ||
| 90 | // Start the visualisation | ||
| 91 | std::string strFile = URIUtils::GetFileName(g_application.CurrentFile()); | ||
| 92 | CLog::Log(LOGDEBUG, "Visualisation::Start()\n"); | ||
| 93 | try | ||
| 94 | { | ||
| 95 | m_pStruct->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile.c_str()); | ||
| 96 | } | ||
| 97 | catch (std::exception e) | ||
| 98 | { | ||
| 99 | HandleException(e, "m_pStruct->Start() (CVisualisation::Create)"); | ||
| 100 | return false; | ||
| 101 | } | ||
| 102 | |||
| 103 | GetPresets(); | ||
| 104 | |||
| 105 | if (GetSubModules()) | ||
| 106 | m_pInfo->submodule = strdup(CSpecialProtocol::TranslatePath(m_submodules.front()).c_str()); | ||
| 107 | else | ||
| 108 | m_pInfo->submodule = NULL; | ||
| 109 | |||
| 110 | CreateBuffers(); | ||
| 111 | |||
| 112 | CAEFactory::RegisterAudioCallback(this); | ||
| 113 | |||
| 114 | return true; | ||
| 115 | } | ||
| 116 | return false; | ||
| 117 | } | ||
| 118 | |||
| 119 | void CVisualisation::Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName) | ||
| 120 | { | ||
| 121 | // notify visz. that new song has been started | ||
| 122 | // pass it the nr of audio channels, sample rate, bits/sample and offcourse the songname | ||
| 123 | if (Initialized()) | ||
| 124 | { | ||
| 125 | try | ||
| 126 | { | ||
| 127 | m_pStruct->Start(iChannels, iSamplesPerSec, iBitsPerSample, strSongName.c_str()); | ||
| 128 | } | ||
| 129 | catch (std::exception e) | ||
| 130 | { | ||
| 131 | HandleException(e, "m_pStruct->Start (CVisualisation::Start)"); | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | void CVisualisation::AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength) | ||
| 137 | { | ||
| 138 | // pass audio data to visz. | ||
| 139 | // audio data: is short audiodata [channel][iAudioDataLength] containing the raw audio data | ||
| 140 | // iAudioDataLength = length of audiodata array | ||
| 141 | // pFreqData = fft-ed audio data | ||
| 142 | // iFreqDataLength = length of pFreqData | ||
| 143 | if (Initialized()) | ||
| 144 | { | ||
| 145 | try | ||
| 146 | { | ||
| 147 | m_pStruct->AudioData(pAudioData, iAudioDataLength, pFreqData, iFreqDataLength); | ||
| 148 | } | ||
| 149 | catch (std::exception e) | ||
| 150 | { | ||
| 151 | HandleException(e, "m_pStruct->AudioData (CVisualisation::AudioData)"); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | void CVisualisation::Render() | ||
| 157 | { | ||
| 158 | // ask visz. to render itself | ||
| 159 | g_graphicsContext.BeginPaint(); | ||
| 160 | if (Initialized()) | ||
| 161 | { | ||
| 162 | try | ||
| 163 | { | ||
| 164 | m_pStruct->Render(); | ||
| 165 | } | ||
| 166 | catch (std::exception e) | ||
| 167 | { | ||
| 168 | HandleException(e, "m_pStruct->Render (CVisualisation::Render)"); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | g_graphicsContext.EndPaint(); | ||
| 172 | } | ||
| 173 | |||
| 174 | void CVisualisation::Stop() | ||
| 175 | { | ||
| 176 | CAEFactory::UnregisterAudioCallback(); | ||
| 177 | if (Initialized()) | ||
| 178 | { | ||
| 179 | CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Stop(); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | void CVisualisation::GetInfo(VIS_INFO *info) | ||
| 184 | { | ||
| 185 | if (Initialized()) | ||
| 186 | { | ||
| 187 | try | ||
| 188 | { | ||
| 189 | m_pStruct->GetInfo(info); | ||
| 190 | } | ||
| 191 | catch (std::exception e) | ||
| 192 | { | ||
| 193 | HandleException(e, "m_pStruct->GetInfo (CVisualisation::GetInfo)"); | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | bool CVisualisation::OnAction(VIS_ACTION action, void *param) | ||
| 199 | { | ||
| 200 | if (!Initialized()) | ||
| 201 | return false; | ||
| 202 | |||
| 203 | // see if vis wants to handle the input | ||
| 204 | // returns false if vis doesnt want the input | ||
| 205 | // returns true if vis handled the input | ||
| 206 | try | ||
| 207 | { | ||
| 208 | if (action != VIS_ACTION_NONE && m_pStruct->OnAction) | ||
| 209 | { | ||
| 210 | // if this is a VIS_ACTION_UPDATE_TRACK action, copy relevant | ||
| 211 | // tags from CMusicInfoTag to VisTag | ||
| 212 | if ( action == VIS_ACTION_UPDATE_TRACK && param ) | ||
| 213 | { | ||
| 214 | const CMusicInfoTag* tag = (const CMusicInfoTag*)param; | ||
| 215 | std::string artist(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)); | ||
| 216 | std::string albumArtist(StringUtils::Join(tag->GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator)); | ||
| 217 | std::string genre(StringUtils::Join(tag->GetGenre(), g_advancedSettings.m_musicItemSeparator)); | ||
| 218 | |||
| 219 | VisTrack track; | ||
| 220 | track.title = tag->GetTitle().c_str(); | ||
| 221 | track.artist = artist.c_str(); | ||
| 222 | track.album = tag->GetAlbum().c_str(); | ||
| 223 | track.albumArtist = albumArtist.c_str(); | ||
| 224 | track.genre = genre.c_str(); | ||
| 225 | track.comment = tag->GetComment().c_str(); | ||
| 226 | track.lyrics = tag->GetLyrics().c_str(); | ||
| 227 | track.trackNumber = tag->GetTrackNumber(); | ||
| 228 | track.discNumber = tag->GetDiscNumber(); | ||
| 229 | track.duration = tag->GetDuration(); | ||
| 230 | track.year = tag->GetYear(); | ||
| 231 | track.rating = tag->GetRating(); | ||
| 232 | |||
| 233 | return m_pStruct->OnAction(action, &track); | ||
| 234 | } | ||
| 235 | return m_pStruct->OnAction((int)action, param); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | catch (std::exception e) | ||
| 239 | { | ||
| 240 | HandleException(e, "m_pStruct->OnAction (CVisualisation::OnAction)"); | ||
| 241 | } | ||
| 242 | return false; | ||
| 243 | } | ||
| 244 | |||
| 245 | void CVisualisation::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample) | ||
| 246 | { | ||
| 247 | if (!m_pStruct) | ||
| 248 | return ; | ||
| 249 | CLog::Log(LOGDEBUG, "OnInitialize() started"); | ||
| 250 | |||
| 251 | m_iChannels = iChannels; | ||
| 252 | m_iSamplesPerSec = iSamplesPerSec; | ||
| 253 | m_iBitsPerSample = iBitsPerSample; | ||
| 254 | UpdateTrack(); | ||
| 255 | |||
| 256 | CLog::Log(LOGDEBUG, "OnInitialize() done"); | ||
| 257 | } | ||
| 258 | |||
| 259 | void CVisualisation::OnAudioData(const float* pAudioData, int iAudioDataLength) | ||
| 260 | { | ||
| 261 | if (!m_pStruct) | ||
| 262 | return ; | ||
| 263 | |||
| 264 | // FIXME: iAudioDataLength should never be less than 0 | ||
| 265 | if (iAudioDataLength<0) | ||
| 266 | return; | ||
| 267 | |||
| 268 | // Save our audio data in the buffers | ||
| 269 | unique_ptr<CAudioBuffer> pBuffer ( new CAudioBuffer(AUDIO_BUFFER_SIZE) ); | ||
| 270 | pBuffer->Set(pAudioData, iAudioDataLength); | ||
| 271 | m_vecBuffers.push_back( pBuffer.release() ); | ||
| 272 | |||
| 273 | if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ; | ||
| 274 | |||
| 275 | unique_ptr<CAudioBuffer> ptrAudioBuffer ( m_vecBuffers.front() ); | ||
| 276 | m_vecBuffers.pop_front(); | ||
| 277 | // Fourier transform the data if the vis wants it... | ||
| 278 | if (m_bWantsFreq) | ||
| 279 | { | ||
| 280 | const float *psAudioData = ptrAudioBuffer->Get(); | ||
| 281 | memcpy(m_fFreq, psAudioData, AUDIO_BUFFER_SIZE * sizeof(float)); | ||
| 282 | |||
| 283 | // FFT the data | ||
| 284 | twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE); | ||
| 285 | |||
| 286 | // Normalize the data | ||
| 287 | float fMinData = (float)AUDIO_BUFFER_SIZE * AUDIO_BUFFER_SIZE * 3 / 8 * 0.5 * 0.5; // 3/8 for the Hann window, 0.5 as minimum amplitude | ||
| 288 | float fInvMinData = 1.0f/fMinData; | ||
| 289 | for (int i = 0; i < AUDIO_BUFFER_SIZE + 2; i++) | ||
| 290 | { | ||
| 291 | m_fFreq[i] *= fInvMinData; | ||
| 292 | } | ||
| 293 | |||
| 294 | // Transfer data to our visualisation | ||
| 295 | AudioData(psAudioData, AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE); | ||
| 296 | } | ||
| 297 | else | ||
| 298 | { // Transfer data to our visualisation | ||
| 299 | AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, NULL, 0); | ||
| 300 | } | ||
| 301 | return ; | ||
| 302 | } | ||
| 303 | |||
| 304 | void CVisualisation::CreateBuffers() | ||
| 305 | { | ||
| 306 | ClearBuffers(); | ||
| 307 | |||
| 308 | // Get the number of buffers from the current vis | ||
| 309 | VIS_INFO info; | ||
| 310 | m_pStruct->GetInfo(&info); | ||
| 311 | m_iNumBuffers = info.iSyncDelay + 1; | ||
| 312 | m_bWantsFreq = (info.bWantsFreq != 0); | ||
| 313 | if (m_iNumBuffers > MAX_AUDIO_BUFFERS) | ||
| 314 | m_iNumBuffers = MAX_AUDIO_BUFFERS; | ||
| 315 | if (m_iNumBuffers < 1) | ||
| 316 | m_iNumBuffers = 1; | ||
| 317 | } | ||
| 318 | |||
| 319 | void CVisualisation::ClearBuffers() | ||
| 320 | { | ||
| 321 | m_bWantsFreq = false; | ||
| 322 | m_iNumBuffers = 0; | ||
| 323 | |||
| 324 | while (!m_vecBuffers.empty()) | ||
| 325 | { | ||
| 326 | CAudioBuffer* pAudioBuffer = m_vecBuffers.front(); | ||
| 327 | delete pAudioBuffer; | ||
| 328 | m_vecBuffers.pop_front(); | ||
| 329 | } | ||
| 330 | for (int j = 0; j < AUDIO_BUFFER_SIZE*2; j++) | ||
| 331 | { | ||
| 332 | m_fFreq[j] = 0.0f; | ||
| 333 | } | ||
| 334 | } | ||
| 335 | |||
| 336 | bool CVisualisation::UpdateTrack() | ||
| 337 | { | ||
| 338 | bool handled = false; | ||
| 339 | if (Initialized()) | ||
| 340 | { | ||
| 341 | // get the current album art filename | ||
| 342 | m_AlbumThumb = CSpecialProtocol::TranslatePath(g_infoManager.GetImage(MUSICPLAYER_COVER, WINDOW_INVALID)); | ||
| 343 | |||
| 344 | // get the current track tag | ||
| 345 | const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag(); | ||
| 346 | |||
| 347 | if (m_AlbumThumb == "DefaultAlbumCover.png") | ||
| 348 | m_AlbumThumb = ""; | ||
| 349 | else | ||
| 350 | CLog::Log(LOGDEBUG,"Updating visualisation albumart: %s", m_AlbumThumb.c_str()); | ||
| 351 | |||
| 352 | // inform the visualisation of the current album art | ||
| 353 | if (OnAction( VIS_ACTION_UPDATE_ALBUMART, (void*)( m_AlbumThumb.c_str() ) ) ) | ||
| 354 | handled = true; | ||
| 355 | |||
| 356 | // inform the visualisation of the current track's tag information | ||
| 357 | if ( tag && OnAction( VIS_ACTION_UPDATE_TRACK, (void*)tag ) ) | ||
| 358 | handled = true; | ||
| 359 | } | ||
| 360 | return handled; | ||
| 361 | } | ||
| 362 | |||
| 363 | bool CVisualisation::GetPresetList(std::vector<std::string> &vecpresets) | ||
| 364 | { | ||
| 365 | vecpresets = m_presets; | ||
| 366 | return !m_presets.empty(); | ||
| 367 | } | ||
| 368 | |||
| 369 | bool CVisualisation::GetPresets() | ||
| 370 | { | ||
| 371 | m_presets.clear(); | ||
| 372 | char **presets = NULL; | ||
| 373 | unsigned int entries = 0; | ||
| 374 | try | ||
| 375 | { | ||
| 376 | entries = m_pStruct->GetPresets(&presets); | ||
| 377 | } | ||
| 378 | catch (std::exception e) | ||
| 379 | { | ||
| 380 | HandleException(e, "m_pStruct->OnAction (CVisualisation::GetPresets)"); | ||
| 381 | return false; | ||
| 382 | } | ||
| 383 | if (presets && entries > 0) | ||
| 384 | { | ||
| 385 | for (unsigned i=0; i < entries; i++) | ||
| 386 | { | ||
| 387 | if (presets[i]) | ||
| 388 | { | ||
| 389 | m_presets.push_back(presets[i]); | ||
| 390 | } | ||
| 391 | } | ||
| 392 | } | ||
| 393 | return (!m_presets.empty()); | ||
| 394 | } | ||
| 395 | |||
| 396 | bool CVisualisation::GetSubModuleList(std::vector<std::string> &vecmodules) | ||
| 397 | { | ||
| 398 | vecmodules = m_submodules; | ||
| 399 | return !m_submodules.empty(); | ||
| 400 | } | ||
| 401 | |||
| 402 | bool CVisualisation::GetSubModules() | ||
| 403 | { | ||
| 404 | m_submodules.clear(); | ||
| 405 | char **modules = NULL; | ||
| 406 | unsigned int entries = 0; | ||
| 407 | try | ||
| 408 | { | ||
| 409 | entries = m_pStruct->GetSubModules(&modules); | ||
| 410 | } | ||
| 411 | catch (...) | ||
| 412 | { | ||
| 413 | CLog::Log(LOGERROR, "Exception in Visualisation::GetSubModules()"); | ||
| 414 | return false; | ||
| 415 | } | ||
| 416 | if (modules && entries > 0) | ||
| 417 | { | ||
| 418 | for (unsigned i=0; i < entries; i++) | ||
| 419 | { | ||
| 420 | if (modules[i]) | ||
| 421 | { | ||
| 422 | m_submodules.push_back(modules[i]); | ||
| 423 | } | ||
| 424 | } | ||
| 425 | } | ||
| 426 | return (!m_submodules.empty()); | ||
| 427 | } | ||
| 428 | |||
| 429 | std::string CVisualisation::GetFriendlyName(const std::string& strVisz, | ||
| 430 | const std::string& strSubModule) | ||
| 431 | { | ||
| 432 | // should be of the format "moduleName (visName)" | ||
| 433 | return strSubModule + " (" + strVisz + ")"; | ||
| 434 | } | ||
| 435 | |||
| 436 | bool CVisualisation::IsLocked() | ||
| 437 | { | ||
| 438 | if (!m_presets.empty()) | ||
| 439 | { | ||
| 440 | if (!m_pStruct) | ||
| 441 | return false; | ||
| 442 | |||
| 443 | return m_pStruct->IsLocked(); | ||
| 444 | } | ||
| 445 | return false; | ||
| 446 | } | ||
| 447 | |||
| 448 | void CVisualisation::Destroy() | ||
| 449 | { | ||
| 450 | // Free what was allocated in method CVisualisation::Create | ||
| 451 | if (m_pInfo) | ||
| 452 | { | ||
| 453 | free((void *) m_pInfo->name); | ||
| 454 | free((void *) m_pInfo->presets); | ||
| 455 | free((void *) m_pInfo->profile); | ||
| 456 | free((void *) m_pInfo->submodule); | ||
| 457 | |||
| 458 | delete m_pInfo; | ||
| 459 | m_pInfo = NULL; | ||
| 460 | } | ||
| 461 | |||
| 462 | CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Destroy(); | ||
| 463 | } | ||
| 464 | |||
| 465 | unsigned CVisualisation::GetPreset() | ||
| 466 | { | ||
| 467 | unsigned index = 0; | ||
| 468 | try | ||
| 469 | { | ||
| 470 | index = m_pStruct->GetPreset(); | ||
| 471 | } | ||
| 472 | catch(...) | ||
| 473 | { | ||
| 474 | return 0; | ||
| 475 | } | ||
| 476 | return index; | ||
| 477 | } | ||
| 478 | |||
| 479 | std::string CVisualisation::GetPresetName() | ||
| 480 | { | ||
| 481 | if (!m_presets.empty()) | ||
| 482 | return m_presets[GetPreset()]; | ||
| 483 | else | ||
| 484 | return ""; | ||
| 485 | } | ||
| 486 | |||
