Fix keymap.
[vuplus_xbmc] / xbmc / addons / Visualisation.cpp
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 "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"
35 #ifdef TARGET_POSIX
36 #include <dlfcn.h>
37 #include "filesystem/SpecialProtocol.h"
38 #endif
39
40 using namespace std;
41 using namespace MUSIC_INFO;
42 using namespace ADDON;
43
44 CAudioBuffer::CAudioBuffer(int iSize)
45 {
46   m_iLen = iSize;
47   m_pBuffer = new float[iSize];
48 }
49
50 CAudioBuffer::~CAudioBuffer()
51 {
52   delete [] m_pBuffer;
53 }
54
55 const float* CAudioBuffer::Get() const
56 {
57   return m_pBuffer;
58 }
59
60 void CAudioBuffer::Set(const float* psBuffer, int iSize)
61 {
62   if (iSize<0)
63     return;
64   memcpy(m_pBuffer, psBuffer, iSize * sizeof(float));
65   for (int i = iSize; i < m_iLen; ++i) m_pBuffer[i] = 0;
66 }
67
68 bool CVisualisation::Create(int x, int y, int w, int h, void *device)
69 {
70   m_pInfo = new VIS_PROPS;
71   #ifdef HAS_DX
72   m_pInfo->device     = g_Windowing.Get3DDevice();
73 #else
74   m_pInfo->device     = NULL;
75 #endif
76   m_pInfo->x = x;
77   m_pInfo->y = y;
78   m_pInfo->width = w;
79   m_pInfo->height = h;
80   m_pInfo->pixelRatio = g_graphicsContext.GetResInfo().fPixelRatio;
81
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;
86
87   if (CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Create() == ADDON_STATUS_OK)
88   {
89     // Start the visualisation
90     CStdString strFile = URIUtils::GetFileName(g_application.CurrentFile());
91     CLog::Log(LOGDEBUG, "Visualisation::Start()\n");
92     try
93     {
94       m_pStruct->Start(m_iChannels, m_iSamplesPerSec, m_iBitsPerSample, strFile);
95     }
96     catch (std::exception e)
97     {
98       HandleException(e, "m_pStruct->Start() (CVisualisation::Create)");
99       return false;
100     }
101
102     GetPresets();
103
104     if (GetSubModules())
105       m_pInfo->submodule = strdup(CSpecialProtocol::TranslatePath(m_submodules.front()).c_str());
106     else
107       m_pInfo->submodule = NULL;
108
109     CreateBuffers();
110
111     g_application.m_pPlayer->RegisterAudioCallback(this);
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 CStdString &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   g_application.m_pPlayer->UnRegisterAudioCallback();
177   CAEFactory::UnregisterAudioCallback();
178   if (Initialized())
179   {
180     CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Stop();
181   }
182 }
183
184 void CVisualisation::GetInfo(VIS_INFO *info)
185 {
186   if (Initialized())
187   {
188     try
189     {
190       m_pStruct->GetInfo(info);
191     }
192     catch (std::exception e)
193     {
194       HandleException(e, "m_pStruct->GetInfo (CVisualisation::GetInfo)");
195     }
196   }
197 }
198
199 bool CVisualisation::OnAction(VIS_ACTION action, void *param)
200 {
201   if (!Initialized())
202     return false;
203
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
207   try
208   {
209     if (action != VIS_ACTION_NONE && m_pStruct->OnAction)
210     {
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 )
214       {
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));
219         
220         VisTrack track;
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();
233
234         return m_pStruct->OnAction(action, &track);
235       }
236       return m_pStruct->OnAction((int)action, param);
237     }
238   }
239   catch (std::exception e)
240   {
241     HandleException(e, "m_pStruct->OnAction (CVisualisation::OnAction)");
242   }
243   return false;
244 }
245
246 void CVisualisation::OnInitialize(int iChannels, int iSamplesPerSec, int iBitsPerSample)
247 {
248   if (!m_pStruct)
249     return ;
250   CLog::Log(LOGDEBUG, "OnInitialize() started");
251
252   m_iChannels = iChannels;
253   m_iSamplesPerSec = iSamplesPerSec;
254   m_iBitsPerSample = iBitsPerSample;
255   UpdateTrack();
256
257   CLog::Log(LOGDEBUG, "OnInitialize() done");
258 }
259
260 void CVisualisation::OnAudioData(const float* pAudioData, int iAudioDataLength)
261 {
262   if (!m_pStruct)
263     return ;
264
265   // FIXME: iAudioDataLength should never be less than 0
266   if (iAudioDataLength<0)
267     return;
268
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() );
273
274   if ( (int)m_vecBuffers.size() < m_iNumBuffers) return ;
275
276   auto_ptr<CAudioBuffer> ptrAudioBuffer ( m_vecBuffers.front() );
277   m_vecBuffers.pop_front();
278   // Fourier transform the data if the vis wants it...
279   if (m_bWantsFreq)
280   {
281     const float *psAudioData = ptrAudioBuffer->Get();
282     memcpy(m_fFreq, psAudioData, AUDIO_BUFFER_SIZE * sizeof(float));
283
284     // FFT the data
285     twochanwithwindow(m_fFreq, AUDIO_BUFFER_SIZE);
286
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++)
291     {
292       m_fFreq[i] *= fInvMinData;
293     }
294
295     // Transfer data to our visualisation
296     AudioData(psAudioData, AUDIO_BUFFER_SIZE, m_fFreq, AUDIO_BUFFER_SIZE);
297   }
298   else
299   { // Transfer data to our visualisation
300     AudioData(ptrAudioBuffer->Get(), AUDIO_BUFFER_SIZE, NULL, 0);
301   }
302   return ;
303 }
304
305 void CVisualisation::CreateBuffers()
306 {
307   ClearBuffers();
308
309   // Get the number of buffers from the current vis
310   VIS_INFO info;
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)
317     m_iNumBuffers = 1;
318 }
319
320 void CVisualisation::ClearBuffers()
321 {
322   m_bWantsFreq = false;
323   m_iNumBuffers = 0;
324
325   while (m_vecBuffers.size() > 0)
326   {
327     CAudioBuffer* pAudioBuffer = m_vecBuffers.front();
328     delete pAudioBuffer;
329     m_vecBuffers.pop_front();
330   }
331   for (int j = 0; j < AUDIO_BUFFER_SIZE*2; j++)
332   {
333     m_fFreq[j] = 0.0f;
334   }
335 }
336
337 bool CVisualisation::UpdateTrack()
338 {
339   bool handled = false;
340   if (Initialized())
341   {
342     // get the current album art filename
343     m_AlbumThumb = CSpecialProtocol::TranslatePath(g_infoManager.GetImage(MUSICPLAYER_COVER, WINDOW_INVALID));
344
345     // get the current track tag
346     const CMusicInfoTag* tag = g_infoManager.GetCurrentSongTag();
347
348     if (m_AlbumThumb == "DefaultAlbumCover.png")
349       m_AlbumThumb = "";
350     else
351       CLog::Log(LOGDEBUG,"Updating visualisation albumart: %s", m_AlbumThumb.c_str());
352
353     // inform the visualisation of the current album art
354     if (OnAction( VIS_ACTION_UPDATE_ALBUMART, (void*)( m_AlbumThumb.c_str() ) ) )
355       handled = true;
356
357     // inform the visualisation of the current track's tag information
358     if ( tag && OnAction( VIS_ACTION_UPDATE_TRACK, (void*)tag ) )
359       handled = true;
360   }
361   return handled;
362 }
363
364 bool CVisualisation::GetPresetList(std::vector<CStdString> &vecpresets)
365 {
366   vecpresets = m_presets;
367   return !m_presets.empty();
368 }
369
370 bool CVisualisation::GetPresets()
371 {
372   m_presets.clear();
373   char **presets = NULL;
374   unsigned int entries = 0;
375   try
376   {
377     entries = m_pStruct->GetPresets(&presets);
378   }
379   catch (std::exception e)
380   {
381     HandleException(e, "m_pStruct->OnAction (CVisualisation::GetPresets)");
382     return false;
383   }
384   if (presets && entries > 0)
385   {
386     for (unsigned i=0; i < entries; i++)
387     {
388       if (presets[i])
389       {
390         m_presets.push_back(presets[i]);
391       }
392     }
393   }
394   return (!m_presets.empty());
395 }
396
397 bool CVisualisation::GetSubModuleList(std::vector<CStdString> &vecmodules)
398 {
399   vecmodules = m_submodules;
400   return !m_submodules.empty();
401 }
402
403 bool CVisualisation::GetSubModules()
404 {
405   m_submodules.clear();
406   char **modules = NULL;
407   unsigned int entries = 0;
408   try
409   {
410     entries = m_pStruct->GetSubModules(&modules);
411   }
412   catch (...)
413   {
414     CLog::Log(LOGERROR, "Exception in Visualisation::GetSubModules()");
415     return false;
416   }
417   if (modules && entries > 0)
418   {
419     for (unsigned i=0; i < entries; i++)
420     {
421       if (modules[i])
422       {
423         m_submodules.push_back(modules[i]);
424       }
425     }
426   }
427   return (!m_submodules.empty());
428 }
429
430 CStdString CVisualisation::GetFriendlyName(const CStdString& strVisz,
431                                            const CStdString& strSubModule)
432 {
433   // should be of the format "moduleName (visName)"
434   return CStdString(strSubModule + " (" + strVisz + ")");
435 }
436
437 bool CVisualisation::IsLocked()
438 {
439   if (!m_presets.empty())
440   {
441     if (!m_pStruct)
442       return false;
443
444     return m_pStruct->IsLocked();
445   }
446   return false;
447 }
448
449 void CVisualisation::Destroy()
450 {
451   // Free what was allocated in method CVisualisation::Create
452   if (m_pInfo)
453   {
454     free((void *) m_pInfo->name);
455     free((void *) m_pInfo->presets);
456     free((void *) m_pInfo->profile);
457     free((void *) m_pInfo->submodule);
458
459     delete m_pInfo;
460     m_pInfo = NULL;
461   }
462
463   CAddonDll<DllVisualisation, Visualisation, VIS_PROPS>::Destroy();
464 }
465
466 unsigned CVisualisation::GetPreset()
467 {
468   unsigned index = 0;
469   try
470   {
471     index = m_pStruct->GetPreset();
472   }
473   catch(...)
474   {
475     return 0;
476   }
477   return index;
478 }
479
480 CStdString CVisualisation::GetPresetName()
481 {
482   if (!m_presets.empty())
483     return m_presets[GetPreset()];
484   else
485     return "";
486 }
487