From ffca21f2743a7b367fa212799c6e2fea6190dd5d Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 3 Mar 2015 16:53:59 +0100 Subject: initial commit for kodi master --- xbmc/addons/Visualisation.cpp | 486 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 486 insertions(+) create mode 100644 xbmc/addons/Visualisation.cpp (limited to 'xbmc/addons/Visualisation.cpp') 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 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ +#include "system.h" +#include "Visualisation.h" +#include "utils/fft.h" +#include "GUIInfoManager.h" +#include "Application.h" +#include "guilib/GraphicContext.h" +#include "guilib/WindowIDs.h" +#include "music/tags/MusicInfoTag.h" +#include "settings/Settings.h" +#include "settings/AdvancedSettings.h" +#include "settings/DisplaySettings.h" +#include "windowing/WindowingFactory.h" +#include "utils/URIUtils.h" +#include "utils/StringUtils.h" +#include "cores/IPlayer.h" +#include "cores/AudioEngine/AEFactory.h" +#ifdef TARGET_POSIX +#include +#include "filesystem/SpecialProtocol.h" +#endif + +using namespace std; +using namespace MUSIC_INFO; +using namespace ADDON; + +CAudioBuffer::CAudioBuffer(int iSize) +{ + m_iLen = iSize; + m_pBuffer = new float[iSize]; +} + +CAudioBuffer::~CAudioBuffer() +{ + delete [] m_pBuffer; +} + +const float* CAudioBuffer::Get() const +{ + return m_pBuffer; +} + +void CAudioBuffer::Set(const float* psBuffer, int iSize) +{ + if (iSize<0) + return; + memcpy(m_pBuffer, psBuffer, iSize * sizeof(float)); + for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0; +} + +bool CVisualisation::Create(int x, int y, int w, int h, void *device) +{ + m_pInfo = new VIS_PROPS; + #ifdef HAS_DX + m_pInfo->device = g_Windowing.Get3DDevice(); +#else + m_pInfo->device = NULL; +#endif + m_pInfo->x = x; + m_pInfo->y = y; + m_pInfo->width = w; + m_pInfo->height = h; + m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio; + + m_pInfo->name = strdup(Name().c_str()); + m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str()); + m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str()); + m_pInfo->submodule = NULL; + + if (CAddonDll::Create() == ADDON_STATUS_OK) + { + // Start the visualisation + std::string strFile = URIUtils::GetFileName(g_application.CurrentFile()); + CLog::Log(LOGDEBUG, "Visualisation::Start()\n"); + try + { + m_pStruct->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile.c_str()); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Start() (CVisualisation::Create)"); + return false; + } + + GetPresets(); + + if (GetSubModules()) + m_pInfo->submodule = strdup(CSpecialProtocol::TranslatePath(m_submodules.front()).c_str()); + else + m_pInfo->submodule = NULL; + + CreateBuffers(); + + CAEFactory::RegisterAudioCallback(this); + + return true; + } + return false; +} + +void CVisualisation::Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const std::string &strSongName) +{ + // notify visz. that new song has been started + // pass it the nr of audio channels, sample rate, bits/sample and offcourse the songname + if (Initialized()) + { + try + { + m_pStruct->Start(iChannels, iSamplesPerSec, iBitsPerSample, strSongName.c_str()); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Start (CVisualisation::Start)"); + } + } +} + +void CVisualisation::AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength) +{ + // pass audio data to visz. + // audio data: is short audiodata [channel][iAudioDataLength] containing the raw audio data + // iAudioDataLength = length of audiodata array + // pFreqData = fft-ed audio data + // iFreqDataLength = length of pFreqData + if (Initialized()) + { + try + { + m_pStruct->AudioData(pAudioData, iAudioDataLength, pFreqData, iFreqDataLength); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->AudioData (CVisualisation::AudioData)"); + } + } +} + +void CVisualisation::Render() +{ + // ask visz. to render itself + g_graphicsContext.BeginPaint(); + if (Initialized()) + { + try + { + m_pStruct->Render(); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->Render (CVisualisation::Render)"); + } + } + g_graphicsContext.EndPaint(); +} + +void CVisualisation::Stop() +{ + CAEFactory::UnregisterAudioCallback(); + if (Initialized()) + { + CAddonDll::Stop(); + } +} + +void CVisualisation::GetInfo(VIS_INFO *info) +{ + if (Initialized()) + { + try + { + m_pStruct->GetInfo(info); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->GetInfo (CVisualisation::GetInfo)"); + } + } +} + +bool CVisualisation::OnAction(VIS_ACTION action, void *param) +{ + if (!Initialized()) + return false; + + // see if vis wants to handle the input + // returns false if vis doesnt want the input + // returns true if vis handled the input + try + { + if (action != VIS_ACTION_NONE && m_pStruct->OnAction) + { + // if this is a VIS_ACTION_UPDATE_TRACK action, copy relevant + // tags from CMusicInfoTag to VisTag + if ( action == VIS_ACTION_UPDATE_TRACK && param ) + { + const CMusicInfoTag* tag = (const CMusicInfoTag*)param; + std::string artist(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator)); + std::string albumArtist(StringUtils::Join(tag->GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator)); + std::string genre(StringUtils::Join(tag->GetGenre(), g_advancedSettings.m_musicItemSeparator)); + + VisTrack track; + track.title = tag->GetTitle().c_str(); + track.artist = artist.c_str(); + track.album = tag->GetAlbum().c_str(); + track.albumArtist = albumArtist.c_str(); + track.genre = genre.c_str(); + track.comment = tag->GetComment().c_str(); + track.lyrics = tag->GetLyrics().c_str(); + track.trackNumber = tag->GetTrackNumber(); + track.discNumber = tag->GetDiscNumber(); + track.duration = tag->GetDuration(); + track.year = tag->GetYear(); + track.rating = tag->GetRating(); + + return m_pStruct->OnAction(action, &track); + } + return m_pStruct->OnAction((int)action, param); + } + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->OnAction (CVisualisation::OnAction)"); + } + return false; +} + +void CVisualisation::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample) +{ + if (!m_pStruct) + return ; + CLog::Log(LOGDEBUG, "OnInitialize() started"); + + m_iChannels = iChannels; + m_iSamplesPerSec = iSamplesPerSec; + m_iBitsPerSample = iBitsPerSample; + UpdateTrack(); + + CLog::Log(LOGDEBUG, "OnInitialize() done"); +} + +void CVisualisation::OnAudioData(const float* pAudioData, int iAudioDataLength) +{ + if (!m_pStruct) + return ; + + // FIXME: iAudioDataLength should never be less than 0 + if (iAudioDataLength<0) + return; + + // Save our audio data in the buffers + unique_ptr pBuffer ( new CAudioBuffer(AUDIO_BUFFER_SIZE) ); + pBuffer->Set(pAudioData, iAudioDataLength); + m_vecBuffers.push_back( pBuffer.release() ); + + if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ; + + unique_ptr ptrAudioBuffer ( m_vecBuffers.front() ); + m_vecBuffers.pop_front(); + // Fourier transform the data if the vis wants it... + if (m_bWantsFreq) + { + const float *psAudioData = ptrAudioBuffer->Get(); + memcpy(m_fFreq, psAudioData, AUDIO_BUFFER_SIZE * sizeof(float)); + + // FFT the data + twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE); + + // Normalize the data + 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 + float fInvMinData = 1.0f/fMinData; + for (int i = 0; i < AUDIO_BUFFER_SIZE + 2; i++) + { + m_fFreq[i] *= fInvMinData; + } + + // Transfer data to our visualisation + AudioData(psAudioData, AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE); + } + else + { // Transfer data to our visualisation + AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, NULL, 0); + } + return ; +} + +void CVisualisation::CreateBuffers() +{ + ClearBuffers(); + + // Get the number of buffers from the current vis + VIS_INFO info; + m_pStruct->GetInfo(&info); + m_iNumBuffers = info.iSyncDelay + 1; + m_bWantsFreq = (info.bWantsFreq != 0); + if (m_iNumBuffers > MAX_AUDIO_BUFFERS) + m_iNumBuffers = MAX_AUDIO_BUFFERS; + if (m_iNumBuffers < 1) + m_iNumBuffers = 1; +} + +void CVisualisation::ClearBuffers() +{ + m_bWantsFreq = false; + m_iNumBuffers = 0; + + while (!m_vecBuffers.empty()) + { + CAudioBuffer* pAudioBuffer = m_vecBuffers.front(); + delete pAudioBuffer; + m_vecBuffers.pop_front(); + } + for (int j = 0; j < AUDIO_BUFFER_SIZE*2; j++) + { + m_fFreq[j] = 0.0f; + } +} + +bool CVisualisation::UpdateTrack() +{ + bool handled = false; + if (Initialized()) + { + // get the current album art filename + m_AlbumThumb = CSpecialProtocol::TranslatePath(g_infoManager.GetImage(MUSICPLAYER_COVER, WINDOW_INVALID)); + + // get the current track tag + const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag(); + + if (m_AlbumThumb == "DefaultAlbumCover.png") + m_AlbumThumb = ""; + else + CLog::Log(LOGDEBUG,"Updating visualisation albumart: %s", m_AlbumThumb.c_str()); + + // inform the visualisation of the current album art + if (OnAction( VIS_ACTION_UPDATE_ALBUMART, (void*)( m_AlbumThumb.c_str() ) ) ) + handled = true; + + // inform the visualisation of the current track's tag information + if ( tag && OnAction( VIS_ACTION_UPDATE_TRACK, (void*)tag ) ) + handled = true; + } + return handled; +} + +bool CVisualisation::GetPresetList(std::vector &vecpresets) +{ + vecpresets = m_presets; + return !m_presets.empty(); +} + +bool CVisualisation::GetPresets() +{ + m_presets.clear(); + char **presets = NULL; + unsigned int entries = 0; + try + { + entries = m_pStruct->GetPresets(&presets); + } + catch (std::exception e) + { + HandleException(e, "m_pStruct->OnAction (CVisualisation::GetPresets)"); + return false; + } + if (presets && entries > 0) + { + for (unsigned i=0; i < entries; i++) + { + if (presets[i]) + { + m_presets.push_back(presets[i]); + } + } + } + return (!m_presets.empty()); +} + +bool CVisualisation::GetSubModuleList(std::vector &vecmodules) +{ + vecmodules = m_submodules; + return !m_submodules.empty(); +} + +bool CVisualisation::GetSubModules() +{ + m_submodules.clear(); + char **modules = NULL; + unsigned int entries = 0; + try + { + entries = m_pStruct->GetSubModules(&modules); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in Visualisation::GetSubModules()"); + return false; + } + if (modules && entries > 0) + { + for (unsigned i=0; i < entries; i++) + { + if (modules[i]) + { + m_submodules.push_back(modules[i]); + } + } + } + return (!m_submodules.empty()); +} + +std::string CVisualisation::GetFriendlyName(const std::string& strVisz, + const std::string& strSubModule) +{ + // should be of the format "moduleName (visName)" + return strSubModule + " (" + strVisz + ")"; +} + +bool CVisualisation::IsLocked() +{ + if (!m_presets.empty()) + { + if (!m_pStruct) + return false; + + return m_pStruct->IsLocked(); + } + return false; +} + +void CVisualisation::Destroy() +{ + // Free what was allocated in method CVisualisation::Create + if (m_pInfo) + { + free((void *) m_pInfo->name); + free((void *) m_pInfo->presets); + free((void *) m_pInfo->profile); + free((void *) m_pInfo->submodule); + + delete m_pInfo; + m_pInfo = NULL; + } + + CAddonDll::Destroy(); +} + +unsigned CVisualisation::GetPreset() +{ + unsigned index = 0; + try + { + index = m_pStruct->GetPreset(); + } + catch(...) + { + return 0; + } + return index; +} + +std::string CVisualisation::GetPresetName() +{ + if (!m_presets.empty()) + return m_presets[GetPreset()]; + else + return ""; +} + -- cgit v1.2.3