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/>.
21 #include "Visualisation.h"
22 #include "utils/fft.h"
23 #include "GUIInfoManager.h"
24 #include "Application.h"
25 #include "music/tags/MusicInfoTag.h"
26 #include "settings/Settings.h"
27 #include "settings/AdvancedSettings.h"
28 #include "settings/DisplaySettings.h"
29 #include "windowing/WindowingFactory.h"
30 #include "utils/URIUtils.h"
31 #include "utils/StringUtils.h"
32 #include "cores/IPlayer.h"
33 #include "cores/AudioEngine/AEFactory.h"
34 #include "cores/AudioEngine/Utils/AEConvert.h"
37 #include "filesystem/SpecialProtocol.h"
41 using namespace MUSIC_INFO;
42 using namespace ADDON;
44 CAudioBuffer::CAudioBuffer(int iSize)
47 m_pBuffer = new float[iSize];
50 CAudioBuffer::~CAudioBuffer()
55 const float* CAudioBuffer::Get() const
60 void CAudioBuffer::Set(const float* psBuffer, int iSize)
64 memcpy(m_pBuffer, psBuffer, iSize * sizeof(float));
65 for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0;
68 bool CVisualisation::Create(int x, int y, int w, int h, void *device)
70 m_pInfo = new VIS_PROPS;
72 m_pInfo->device = g_Windowing.Get3DDevice();
74 m_pInfo->device = NULL;
80 m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio;
82 m_pInfo->name = strdup(Name().c_str());
83 m_pInfo->presets = strdup(CSpecialProtocol::TranslatePath(Path()).c_str());
84 m_pInfo->profile = strdup(CSpecialProtocol::TranslatePath(Profile()).c_str());
85 m_pInfo->submodule = NULL;
87 if (CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Create() == ADDON_STATUS_OK)
89 // Start the visualisation
90 CStdString strFile = URIUtils::GetFileName(g_application.CurrentFile());
91 CLog::Log(LOGDEBUG, "Visualisation::Start()\n");
94 m_pStruct->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile);
96 catch (std::exception e)
98 HandleException(e, "m_pStruct->Start() (CVisualisation::Create)");
105 m_pInfo->submodule = strdup(CSpecialProtocol::TranslatePath(m_submodules.front()).c_str());
107 m_pInfo->submodule = NULL;
111 g_application.m_pPlayer->RegisterAudioCallback(this);
112 CAEFactory::RegisterAudioCallback(this);
119 void CVisualisation::Start(int iChannels, int iSamplesPerSec, int iBitsPerSample, const CStdString &strSongName)
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
127 m_pStruct->Start(iChannels, iSamplesPerSec, iBitsPerSample, strSongName.c_str());
129 catch (std::exception e)
131 HandleException(e, "m_pStruct->Start (CVisualisation::Start)");
136 void CVisualisation::AudioData(const float* pAudioData, int iAudioDataLength, float *pFreqData, int iFreqDataLength)
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
147 m_pStruct->AudioData(pAudioData, iAudioDataLength, pFreqData, iFreqDataLength);
149 catch (std::exception e)
151 HandleException(e, "m_pStruct->AudioData (CVisualisation::AudioData)");
156 void CVisualisation::Render()
158 // ask visz. to render itself
159 g_graphicsContext.BeginPaint();
166 catch (std::exception e)
168 HandleException(e, "m_pStruct->Render (CVisualisation::Render)");
171 g_graphicsContext.EndPaint();
174 void CVisualisation::Stop()
176 g_application.m_pPlayer->UnRegisterAudioCallback();
177 CAEFactory::UnregisterAudioCallback();
180 CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Stop();
184 void CVisualisation::GetInfo(VIS_INFO *info)
190 m_pStruct->GetInfo(info);
192 catch (std::exception e)
194 HandleException(e, "m_pStruct->GetInfo (CVisualisation::GetInfo)");
199 bool CVisualisation::OnAction(VIS_ACTION action, void *param)
204 // see if vis wants to handle the input
205 // returns false if vis doesnt want the input
206 // returns true if vis handled the input
209 if (action != VIS_ACTION_NONE && m_pStruct->OnAction)
211 // if this is a VIS_ACTION_UPDATE_TRACK action, copy relevant
212 // tags from CMusicInfoTag to VisTag
213 if ( action == VIS_ACTION_UPDATE_TRACK && param )
215 const CMusicInfoTag* tag = (const CMusicInfoTag*)param;
216 CStdString artist(StringUtils::Join(tag->GetArtist(), g_advancedSettings.m_musicItemSeparator));
217 CStdString albumArtist(StringUtils::Join(tag->GetAlbumArtist(), g_advancedSettings.m_musicItemSeparator));
218 CStdString genre(StringUtils::Join(tag->GetGenre(), g_advancedSettings.m_musicItemSeparator));
221 track.title = tag->GetTitle().c_str();
222 track.artist = artist.c_str();
223 track.album = tag->GetAlbum().c_str();
224 track.albumArtist = albumArtist.c_str();
225 track.genre = genre.c_str();
226 track.comment = tag->GetComment().c_str();
227 track.lyrics = tag->GetLyrics().c_str();
228 track.trackNumber = tag->GetTrackNumber();
229 track.discNumber = tag->GetDiscNumber();
230 track.duration = tag->GetDuration();
231 track.year = tag->GetYear();
232 track.rating = tag->GetRating();
234 return m_pStruct->OnAction(action, &track);
236 return m_pStruct->OnAction((int)action, param);
239 catch (std::exception e)
241 HandleException(e, "m_pStruct->OnAction (CVisualisation::OnAction)");
246 void CVisualisation::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample)
250 CLog::Log(LOGDEBUG, "OnInitialize() started");
252 m_iChannels = iChannels;
253 m_iSamplesPerSec = iSamplesPerSec;
254 m_iBitsPerSample = iBitsPerSample;
257 CLog::Log(LOGDEBUG, "OnInitialize() done");
260 void CVisualisation::OnAudioData(const float* pAudioData, int iAudioDataLength)
265 // FIXME: iAudioDataLength should never be less than 0
266 if (iAudioDataLength<0)
269 // Save our audio data in the buffers
270 auto_ptr<CAudioBuffer> pBuffer ( new CAudioBuffer(AUDIO_BUFFER_SIZE) );
271 pBuffer->Set(pAudioData, iAudioDataLength);
272 m_vecBuffers.push_back( pBuffer.release() );
274 if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ;
276 auto_ptr<CAudioBuffer> ptrAudioBuffer ( m_vecBuffers.front() );
277 m_vecBuffers.pop_front();
278 // Fourier transform the data if the vis wants it...
281 const float *psAudioData = ptrAudioBuffer->Get();
282 memcpy(m_fFreq, psAudioData, AUDIO_BUFFER_SIZE * sizeof(float));
285 twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE);
287 // Normalize the data
288 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
289 float fInvMinData = 1.0f/fMinData;
290 for (int i = 0; i < AUDIO_BUFFER_SIZE + 2; i++)
292 m_fFreq[i] *= fInvMinData;
295 // Transfer data to our visualisation
296 AudioData(psAudioData, AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE);
299 { // Transfer data to our visualisation
300 AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, NULL, 0);
305 void CVisualisation::CreateBuffers()
309 // Get the number of buffers from the current vis
311 m_pStruct->GetInfo(&info);
312 m_iNumBuffers = info.iSyncDelay + 1;
313 m_bWantsFreq = (info.bWantsFreq != 0);
314 if (m_iNumBuffers > MAX_AUDIO_BUFFERS)
315 m_iNumBuffers = MAX_AUDIO_BUFFERS;
316 if (m_iNumBuffers < 1)
320 void CVisualisation::ClearBuffers()
322 m_bWantsFreq = false;
325 while (m_vecBuffers.size() > 0)
327 CAudioBuffer* pAudioBuffer = m_vecBuffers.front();
329 m_vecBuffers.pop_front();
331 for (int j = 0; j < AUDIO_BUFFER_SIZE*2; j++)
337 bool CVisualisation::UpdateTrack()
339 bool handled = false;
342 // get the current album art filename
343 m_AlbumThumb = CSpecialProtocol::TranslatePath(g_infoManager.GetImage(MUSICPLAYER_COVER, WINDOW_INVALID));
345 // get the current track tag
346 const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag();
348 if (m_AlbumThumb == "DefaultAlbumCover.png")
351 CLog::Log(LOGDEBUG,"Updating visualisation albumart: %s", m_AlbumThumb.c_str());
353 // inform the visualisation of the current album art
354 if (OnAction( VIS_ACTION_UPDATE_ALBUMART, (void*)( m_AlbumThumb.c_str() ) ) )
357 // inform the visualisation of the current track's tag information
358 if ( tag && OnAction( VIS_ACTION_UPDATE_TRACK, (void*)tag ) )
364 bool CVisualisation::GetPresetList(std::vector<CStdString> &vecpresets)
366 vecpresets = m_presets;
367 return !m_presets.empty();
370 bool CVisualisation::GetPresets()
373 char **presets = NULL;
374 unsigned int entries = 0;
377 entries = m_pStruct->GetPresets(&presets);
379 catch (std::exception e)
381 HandleException(e, "m_pStruct->OnAction (CVisualisation::GetPresets)");
384 if (presets && entries > 0)
386 for (unsigned i=0; i < entries; i++)
390 m_presets.push_back(presets[i]);
394 return (!m_presets.empty());
397 bool CVisualisation::GetSubModuleList(std::vector<CStdString> &vecmodules)
399 vecmodules = m_submodules;
400 return !m_submodules.empty();
403 bool CVisualisation::GetSubModules()
405 m_submodules.clear();
406 char **modules = NULL;
407 unsigned int entries = 0;
410 entries = m_pStruct->GetSubModules(&modules);
414 CLog::Log(LOGERROR, "Exception in Visualisation::GetSubModules()");
417 if (modules && entries > 0)
419 for (unsigned i=0; i < entries; i++)
423 m_submodules.push_back(modules[i]);
427 return (!m_submodules.empty());
430 CStdString CVisualisation::GetFriendlyName(const CStdString& strVisz,
431 const CStdString& strSubModule)
433 // should be of the format "moduleName (visName)"
434 return CStdString(strSubModule + " (" + strVisz + ")");
437 bool CVisualisation::IsLocked()
439 if (!m_presets.empty())
444 return m_pStruct->IsLocked();
449 void CVisualisation::Destroy()
451 // Free what was allocated in method CVisualisation::Create
454 free((void *) m_pInfo->name);
455 free((void *) m_pInfo->presets);
456 free((void *) m_pInfo->profile);
457 free((void *) m_pInfo->submodule);
463 CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Destroy();
466 unsigned CVisualisation::GetPreset()
471 index = m_pStruct->GetPreset();
480 CStdString CVisualisation::GetPresetName()
482 if (!m_presets.empty())
483 return m_presets[GetPreset()];